Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit a63eff6

Browse files
committed
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.)
1 parent fe216b7 commit a63eff6

1 file changed

Lines changed: 87 additions & 12 deletions

File tree

Objects/classobject.c

Lines changed: 87 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,15 @@ static PyObject *class_lookup
3939
Py_PROTO((PyClassObject *, PyObject *, PyClassObject **));
4040
static PyObject *instance_getattr1 Py_PROTO((PyInstanceObject *, PyObject *));
4141

42+
static PyObject *getattrstr, *setattrstr, *delattrstr;
43+
4244
PyObject *
4345
PyClass_New(bases, dict, name)
4446
PyObject *bases; /* NULL or tuple of classobjects! */
4547
PyObject *dict;
4648
PyObject *name;
4749
{
4850
PyClassObject *op, *dummy;
49-
static PyObject *getattrstr, *setattrstr, *delattrstr;
5051
static PyObject *docstr, *modstr, *namestr;
5152
if (docstr == NULL) {
5253
docstr= PyString_InternFromString("__doc__");
@@ -215,6 +216,72 @@ class_getattr(op, name)
215216
return v;
216217
}
217218

219+
static void
220+
set_slot(slot, v)
221+
PyObject **slot;
222+
PyObject *v;
223+
{
224+
PyObject *temp = *slot;
225+
Py_XINCREF(v);
226+
*slot = v;
227+
Py_XDECREF(temp);
228+
}
229+
230+
static char *
231+
set_dict(c, v)
232+
PyClassObject *c;
233+
PyObject *v;
234+
{
235+
PyClassObject *dummy;
236+
237+
if (v == NULL || !PyDict_Check(v))
238+
return "__dict__ must be a dictionary object";
239+
set_slot(&c->cl_dict, v);
240+
241+
set_slot(&c->cl_getattr, class_lookup(c, getattrstr, &dummy));
242+
set_slot(&c->cl_setattr, class_lookup(c, setattrstr, &dummy));
243+
set_slot(&c->cl_delattr, class_lookup(c, delattrstr, &dummy));
244+
245+
return "";
246+
}
247+
248+
static char *
249+
set_bases(c, v)
250+
PyClassObject *c;
251+
PyObject *v;
252+
{
253+
PyObject *temp;
254+
int i, n;
255+
256+
if (v == NULL || !PyTuple_Check(v))
257+
return "__bases__ must be a tuple object";
258+
n = PyTuple_Size(v);
259+
for (i = 0; i < n; i++) {
260+
PyObject *x = PyTuple_GET_ITEM(v, i);
261+
if (!PyClass_Check(x))
262+
return "__bases__ items must be classes";
263+
if (PyClass_IsSubclass(x, (PyObject *)c))
264+
return "a __bases__ item causes an inheritance cycle";
265+
}
266+
set_slot(&c->cl_bases, v);
267+
return "";
268+
}
269+
270+
static char *
271+
set_name(c, v)
272+
PyClassObject *c;
273+
PyObject *v;
274+
{
275+
PyObject *temp;
276+
277+
if (v == NULL || !PyString_Check(v))
278+
return "__name__ must be a string object";
279+
if (strlen(PyString_AS_STRING(v)) != PyString_GET_SIZE(v))
280+
return "__name__ must not contain null bytes";
281+
set_slot(&c->cl_name, v);
282+
return "";
283+
}
284+
218285
static int
219286
class_setattr(op, name, v)
220287
PyClassObject *op;
@@ -231,17 +298,25 @@ class_setattr(op, name, v)
231298
if (sname[0] == '_' && sname[1] == '_') {
232299
int n = PyString_Size(name);
233300
if (sname[n-1] == '_' && sname[n-2] == '_') {
234-
if (strcmp(sname, "__dict__") == 0 ||
235-
strcmp(sname, "__bases__") == 0 ||
236-
strcmp(sname, "__name__") == 0 ||
237-
strcmp(sname, "__getattr__") == 0 ||
238-
strcmp(sname, "__setattr__") == 0 ||
239-
strcmp(sname, "__delattr__") == 0)
240-
{
241-
/* XXX In unrestricted mode, we should
242-
XXX allow this -- with a type check */
243-
PyErr_SetString(PyExc_TypeError,
244-
"read-only special attribute");
301+
char *err = NULL;
302+
if (strcmp(sname, "__dict__") == 0)
303+
err = set_dict(op, v);
304+
else if (strcmp(sname, "__bases__") == 0)
305+
err = set_bases(op, v);
306+
else if (strcmp(sname, "__name__") == 0)
307+
err = set_name(op, v);
308+
else if (strcmp(sname, "__getattr__") == 0)
309+
set_slot(&op->cl_getattr, v);
310+
else if (strcmp(sname, "__setattr__") == 0)
311+
set_slot(&op->cl_setattr, v);
312+
else if (strcmp(sname, "__delattr__") == 0)
313+
set_slot(&op->cl_delattr, v);
314+
/* For the last three, we fall through to update the
315+
dictionary as well. */
316+
if (err != NULL) {
317+
if (*err == '\0')
318+
return 0;
319+
PyErr_SetString(PyExc_TypeError, err);
245320
return -1;
246321
}
247322
}

0 commit comments

Comments
 (0)