diff options
author | Guido van Rossum <guido@python.org> | 1998-05-29 21:37:21 (GMT) |
---|---|---|
committer | Guido van Rossum <guido@python.org> | 1998-05-29 21:37:21 (GMT) |
commit | a63eff6e6aac8325cb3542a2d678cfc69fa8597e (patch) | |
tree | b41dcde4dae24912d027cb506f70a4a955bb1ec4 | |
parent | fe216b79373c9cb70b1c880abd5b21e87668f157 (diff) | |
download | cpython-a63eff6e6aac8325cb3542a2d678cfc69fa8597e.zip cpython-a63eff6e6aac8325cb3542a2d678cfc69fa8597e.tar.gz cpython-a63eff6e6aac8325cb3542a2d678cfc69fa8597e.tar.bz2 |
Allow assignments to special class attributes -- with typechecks, and
not in restricted mode.
__dict__ can be set to any dictionary; the cl_getattr, cl_setattr and
cl_delattr slots are refreshed.
__name__ can be set to any string.
__bases__ can be set to to a tuple of classes, provided they are not
subclasses of the class whose attribute is being assigned.
__getattr__, __setattr__ and __delattr__ can be set to anything, or
deleted; the appropriate slot (cl_getattr, cl_setattr, cl_delattr) is
refreshed.
(Note: __name__ really doesn't need to be a special attribute, but
that would be more work.)
-rw-r--r-- | Objects/classobject.c | 99 |
1 files changed, 87 insertions, 12 deletions
diff --git a/Objects/classobject.c b/Objects/classobject.c index 083759cd..61e50be 100644 --- a/Objects/classobject.c +++ b/Objects/classobject.c @@ -39,6 +39,8 @@ static PyObject *class_lookup Py_PROTO((PyClassObject *, PyObject *, PyClassObject **)); static PyObject *instance_getattr1 Py_PROTO((PyInstanceObject *, PyObject *)); +static PyObject *getattrstr, *setattrstr, *delattrstr; + PyObject * PyClass_New(bases, dict, name) PyObject *bases; /* NULL or tuple of classobjects! */ @@ -46,7 +48,6 @@ PyClass_New(bases, dict, name) PyObject *name; { PyClassObject *op, *dummy; - static PyObject *getattrstr, *setattrstr, *delattrstr; static PyObject *docstr, *modstr, *namestr; if (docstr == NULL) { docstr= PyString_InternFromString("__doc__"); @@ -215,6 +216,72 @@ class_getattr(op, name) return v; } +static void +set_slot(slot, v) + PyObject **slot; + PyObject *v; +{ + PyObject *temp = *slot; + Py_XINCREF(v); + *slot = v; + Py_XDECREF(temp); +} + +static char * +set_dict(c, v) + PyClassObject *c; + PyObject *v; +{ + PyClassObject *dummy; + + if (v == NULL || !PyDict_Check(v)) + return "__dict__ must be a dictionary object"; + set_slot(&c->cl_dict, v); + + set_slot(&c->cl_getattr, class_lookup(c, getattrstr, &dummy)); + set_slot(&c->cl_setattr, class_lookup(c, setattrstr, &dummy)); + set_slot(&c->cl_delattr, class_lookup(c, delattrstr, &dummy)); + + return ""; +} + +static char * +set_bases(c, v) + PyClassObject *c; + PyObject *v; +{ + PyObject *temp; + int i, n; + + if (v == NULL || !PyTuple_Check(v)) + return "__bases__ must be a tuple object"; + n = PyTuple_Size(v); + for (i = 0; i < n; i++) { + PyObject *x = PyTuple_GET_ITEM(v, i); + if (!PyClass_Check(x)) + return "__bases__ items must be classes"; + if (PyClass_IsSubclass(x, (PyObject *)c)) + return "a __bases__ item causes an inheritance cycle"; + } + set_slot(&c->cl_bases, v); + return ""; +} + +static char * +set_name(c, v) + PyClassObject *c; + PyObject *v; +{ + PyObject *temp; + + if (v == NULL || !PyString_Check(v)) + return "__name__ must be a string object"; + if (strlen(PyString_AS_STRING(v)) != PyString_GET_SIZE(v)) + return "__name__ must not contain null bytes"; + set_slot(&c->cl_name, v); + return ""; +} + static int class_setattr(op, name, v) PyClassObject *op; @@ -231,17 +298,25 @@ class_setattr(op, name, v) if (sname[0] == '_' && sname[1] == '_') { int n = PyString_Size(name); if (sname[n-1] == '_' && sname[n-2] == '_') { - if (strcmp(sname, "__dict__") == 0 || - strcmp(sname, "__bases__") == 0 || - strcmp(sname, "__name__") == 0 || - strcmp(sname, "__getattr__") == 0 || - strcmp(sname, "__setattr__") == 0 || - strcmp(sname, "__delattr__") == 0) - { - /* XXX In unrestricted mode, we should - XXX allow this -- with a type check */ - PyErr_SetString(PyExc_TypeError, - "read-only special attribute"); + char *err = NULL; + if (strcmp(sname, "__dict__") == 0) + err = set_dict(op, v); + else if (strcmp(sname, "__bases__") == 0) + err = set_bases(op, v); + else if (strcmp(sname, "__name__") == 0) + err = set_name(op, v); + else if (strcmp(sname, "__getattr__") == 0) + set_slot(&op->cl_getattr, v); + else if (strcmp(sname, "__setattr__") == 0) + set_slot(&op->cl_setattr, v); + else if (strcmp(sname, "__delattr__") == 0) + set_slot(&op->cl_delattr, v); + /* For the last three, we fall through to update the + dictionary as well. */ + if (err != NULL) { + if (*err == '\0') + return 0; + PyErr_SetString(PyExc_TypeError, err); return -1; } } |