summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuido van Rossum <guido@python.org>1998-05-29 21:37:21 (GMT)
committerGuido van Rossum <guido@python.org>1998-05-29 21:37:21 (GMT)
commita63eff6e6aac8325cb3542a2d678cfc69fa8597e (patch)
treeb41dcde4dae24912d027cb506f70a4a955bb1ec4
parentfe216b79373c9cb70b1c880abd5b21e87668f157 (diff)
downloadcpython-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.c99
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;
}
}