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

Skip to content

Commit b2173c3

Browse files
committed
Allow assignments to instance.__dict__ and instance.__class__. The
former lets you give an instance a set of new instance vars. The latter lets you give it a new class. Both are typechecked and disallowed in restricted mode. For classes, the check for read-only special attributes is tightened so that only assignments to __dict__, __bases__, __name__, __getattr__, __setattr__, and __delattr__ (these could be made to work as well, but I don't know if that's useful -- let's see first whether mucking with instances will help).
1 parent a27d112 commit b2173c3

1 file changed

Lines changed: 57 additions & 17 deletions

File tree

Objects/classobject.c

Lines changed: 57 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -175,20 +175,31 @@ class_setattr(op, name, v)
175175
PyObject *name;
176176
PyObject *v;
177177
{
178-
char *sname = PyString_AsString(name);
179-
if (sname[0] == '_' && sname[1] == '_') {
180-
int n = PyString_Size(name);
181-
if (sname[n-1] == '_' && sname[n-2] == '_') {
182-
PyErr_SetString(PyExc_TypeError,
183-
"read-only special attribute");
184-
return -1;
185-
}
186-
}
178+
char *sname;
187179
if (PyEval_GetRestricted()) {
188180
PyErr_SetString(PyExc_RuntimeError,
189181
"classes are read-only in restricted mode");
190182
return -1;
191183
}
184+
sname = PyString_AsString(name);
185+
if (sname[0] == '_' && sname[1] == '_') {
186+
int n = PyString_Size(name);
187+
if (sname[n-1] == '_' && sname[n-2] == '_') {
188+
if (strcmp(sname, "__dict__") == 0 ||
189+
strcmp(sname, "__bases__") == 0 ||
190+
strcmp(sname, "__name__") == 0 ||
191+
strcmp(sname, "__getattr__") == 0 ||
192+
strcmp(sname, "__setattr__") == 0 ||
193+
strcmp(sname, "__delattr__") == 0)
194+
{
195+
/* XXX In unrestricted mode, we should
196+
XXX allow this -- with a type check */
197+
PyErr_SetString(PyExc_TypeError,
198+
"read-only special attribute");
199+
return -1;
200+
}
201+
}
202+
}
192203
if (v == NULL) {
193204
int rv = PyDict_DelItem(op->cl_dict, name);
194205
if (rv < 0)
@@ -489,16 +500,45 @@ instance_setattr(inst, name, v)
489500
PyObject *name;
490501
PyObject *v;
491502
{
492-
PyObject *func, *args, *res;
503+
PyObject *func, *args, *res, *tmp;
493504
char *sname = PyString_AsString(name);
494-
if (sname[0] == '_' && sname[1] == '_'
495-
&& (strcmp(sname, "__dict__") == 0 ||
496-
strcmp(sname, "__class__") == 0)) {
497-
int n = PyString_Size(name);
505+
if (sname[0] == '_' && sname[1] == '_') {
506+
int n = PyString_Size(name);
498507
if (sname[n-1] == '_' && sname[n-2] == '_') {
499-
PyErr_SetString(PyExc_TypeError,
500-
"read-only special attribute");
501-
return -1;
508+
if (strcmp(sname, "__dict__") == 0) {
509+
if (PyEval_GetRestricted()) {
510+
PyErr_SetString(PyExc_RuntimeError,
511+
"__dict__ not accessible in restricted mode");
512+
return -1;
513+
}
514+
if (v == NULL || !PyDict_Check(v)) {
515+
PyErr_SetString(PyExc_TypeError,
516+
"__dict__ must be set to a dictionary");
517+
return -1;
518+
}
519+
tmp = inst->in_dict;
520+
Py_INCREF(v);
521+
inst->in_dict = v;
522+
Py_DECREF(tmp);
523+
return 0;
524+
}
525+
if (strcmp(sname, "__class__") == 0) {
526+
if (PyEval_GetRestricted()) {
527+
PyErr_SetString(PyExc_RuntimeError,
528+
"__class__ not accessible in restricted mode");
529+
return -1;
530+
}
531+
if (v == NULL || !PyClass_Check(v)) {
532+
PyErr_SetString(PyExc_TypeError,
533+
"__class__ must be set to a class");
534+
return -1;
535+
}
536+
tmp = (PyObject *)(inst->in_class);
537+
Py_INCREF(v);
538+
inst->in_class = (PyClassObject *)v;
539+
Py_DECREF(tmp);
540+
return 0;
541+
}
502542
}
503543
}
504544
if (v == NULL)

0 commit comments

Comments
 (0)