Mailing List Archive

python/dist/src/Objects typeobject.c,2.171,2.172
Update of /cvsroot/python/python/dist/src/Objects
In directory usw-pr-cvs1:/tmp/cvs-serv11963

Modified Files:
typeobject.c
Log Message:
Refactor how __dict__ and __weakref__ interact with __slots__.

1. You can now have __dict__ and/or __weakref__ in your __slots__
(before only __weakref__ was supported). This is treated
differently than before: it merely sets a flag that the object
should support the corresponding magic.

2. Dynamic types now always have descriptors __dict__ and __weakref__
thrust upon them. If the type in fact does not support one or the
other, that descriptor's __get__ method will raise AttributeError.

3. (This is the reason for all this; it fixes SF bug 575229, reported
by Cesar Douady.) Given this code:
class A(object): __slots__ = []
class B(object): pass
class C(A, B): __slots__ = []
the class object for C was broken; its size was less than that of
B, and some descriptors on B could cause a segfault. C now
correctly inherits __weakrefs__ and __dict__ from B, even though A
is the "primary" base (C.__base__ is A).

4. Some code cleanup, and a few comments added.


Index: typeobject.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Objects/typeobject.c,v
retrieving revision 2.171
retrieving revision 2.172
diff -C2 -d -r2.171 -r2.172
*** typeobject.c 10 Aug 2002 05:41:29 -0000 2.171
--- typeobject.c 12 Aug 2002 19:05:44 -0000 2.172
***************
*** 928,934 ****
}

static PyGetSetDef subtype_getsets[] = {
! {"__dict__", subtype_dict, subtype_setdict, NULL},
! {0},
};

--- 928,963 ----
}

+ static PyObject *
+ subtype_getweakref(PyObject *obj, void *context)
+ {
+ PyObject **weaklistptr;
+ PyObject *result;
+
+ if (obj->ob_type->tp_weaklistoffset == 0) {
+ PyErr_SetString(PyExc_AttributeError,
+ "This object has no __weaklist__");
+ return NULL;
+ }
+ assert(obj->ob_type->tp_weaklistoffset > 0);
+ assert(obj->ob_type->tp_weaklistoffset + sizeof(PyObject *) <=
+ obj->ob_type->tp_basicsize);
+ weaklistptr = (PyObject **)
+ ((void *)obj + obj->ob_type->tp_weaklistoffset);
+ if (*weaklistptr == NULL)
+ result = Py_None;
+ else
+ result = *weaklistptr;
+ Py_INCREF(result);
+ return result;
+ }
+
static PyGetSetDef subtype_getsets[] = {
! /* Not all objects have these attributes!
! The descriptor's __get__ method may raise AttributeError. */
! {"__dict__", subtype_dict, subtype_setdict,
! "dictionary for instance variables (if defined)"},
! {"__weakref__", subtype_getweakref, NULL,
! "list of weak references to the object (if defined)"},
! {0}
};

***************
*** 986,989 ****
--- 1015,1019 ----
PyMemberDef *mp;
int i, nbases, nslots, slotoffset, add_dict, add_weak;
+ int j, may_add_dict, may_add_weak;

assert(args != NULL && PyTuple_Check(args));
***************
*** 1073,1077 ****
add_dict = 0;
add_weak = 0;
! if (slots != NULL) {
/* Make it into a tuple */
if (PyString_Check(slots))
--- 1103,1119 ----
add_dict = 0;
add_weak = 0;
! may_add_dict = base->tp_dictoffset == 0;
! may_add_weak = base->tp_weaklistoffset == 0 && base->tp_itemsize == 0;
! if (slots == NULL) {
! if (may_add_dict) {
! add_dict++;
! }
! if (may_add_weak) {
! add_weak++;
! }
! }
! else {
! /* Have slots */
!
/* Make it into a tuple */
if (PyString_Check(slots))
***************
*** 1081,1084 ****
--- 1123,1129 ----
if (slots == NULL)
return NULL;
+ assert(PyTuple_Check(slots));
+
+ /* Are slots allowed? */
nslots = PyTuple_GET_SIZE(slots);
if (nslots > 0 && base->tp_itemsize != 0) {
***************
*** 1087,1107 ****
"not supported for subtype of '%s'",
base->tp_name);
return NULL;
}
for (i = 0; i < nslots; i++) {
! if (!valid_identifier(PyTuple_GET_ITEM(slots, i))) {
! Py_DECREF(slots);
! return NULL;
}
}

! newslots = PyTuple_New(nslots);
if (newslots == NULL)
! return NULL;
! for (i = 0; i < nslots; i++) {
tmp = PyTuple_GET_ITEM(slots, i);
if (_Py_Mangle(PyString_AS_STRING(name),
! PyString_AS_STRING(tmp),
! buffer, sizeof(buffer)))
{
tmp = PyString_FromString(buffer);
--- 1132,1183 ----
"not supported for subtype of '%s'",
base->tp_name);
+ bad_slots:
+ Py_DECREF(slots);
return NULL;
}
+
+ /* Check for valid slot names and two special cases */
for (i = 0; i < nslots; i++) {
! PyObject *tmp = PyTuple_GET_ITEM(slots, i);
! char *s;
! if (!valid_identifier(tmp))
! goto bad_slots;
! assert(PyString_Check(tmp));
! s = PyString_AS_STRING(tmp);
! if (strcmp(s, "__dict__") == 0) {
! if (!may_add_dict || add_dict) {
! PyErr_SetString(PyExc_TypeError,
! "__dict__ slot disallowed: "
! "we already got one");
! goto bad_slots;
! }
! add_dict++;
! }
! if (strcmp(s, "__weakref__") == 0) {
! if (!may_add_weak || add_weak) {
! PyErr_SetString(PyExc_TypeError,
! "__weakref__ slot disallowed: "
! "either we already got one, "
! "or __itemsize__ != 0");
! goto bad_slots;
! }
! add_weak++;
}
}

! /* Copy slots into yet another tuple, demangling names */
! newslots = PyTuple_New(nslots - add_dict - add_weak);
if (newslots == NULL)
! goto bad_slots;
! for (i = j = 0; i < nslots; i++) {
! char *s;
tmp = PyTuple_GET_ITEM(slots, i);
+ s = PyString_AS_STRING(tmp);
+ if ((add_dict && strcmp(s, "__dict__") == 0) ||
+ (add_weak && strcmp(s, "__weakref__") == 0))
+ continue;
if (_Py_Mangle(PyString_AS_STRING(name),
! PyString_AS_STRING(tmp),
! buffer, sizeof(buffer)))
{
tmp = PyString_FromString(buffer);
***************
*** 1109,1148 ****
Py_INCREF(tmp);
}
! PyTuple_SET_ITEM(newslots, i, tmp);
}
Py_DECREF(slots);
slots = newslots;

- }
- if (slots != NULL) {
/* See if *this* class defines __getstate__ */
! PyObject *getstate = PyDict_GetItemString(dict,
! "__getstate__");
! if (getstate == NULL) {
/* If not, provide a bozo that raises TypeError */
if (bozo_obj == NULL) {
bozo_obj = PyCFunction_New(&bozo_ml, NULL);
! if (bozo_obj == NULL) {
! /* XXX decref various things */
! return NULL;
! }
}
if (PyDict_SetItemString(dict,
"__getstate__",
! bozo_obj) < 0) {
! /* XXX decref various things */
! return NULL;
}
}
- }
- if (slots == NULL && base->tp_dictoffset == 0 &&
- (base->tp_setattro == PyObject_GenericSetAttr ||
- base->tp_setattro == NULL)) {
- add_dict++;
- }
- if (slots == NULL && base->tp_weaklistoffset == 0 &&
- base->tp_itemsize == 0) {
- nslots++;
- add_weak++;
}

--- 1185,1245 ----
Py_INCREF(tmp);
}
! PyTuple_SET_ITEM(newslots, j, tmp);
! j++;
}
+ assert(j == nslots - add_dict - add_weak);
+ nslots = j;
Py_DECREF(slots);
slots = newslots;

/* See if *this* class defines __getstate__ */
! if (PyDict_GetItemString(dict, "__getstate__") == NULL) {
/* If not, provide a bozo that raises TypeError */
if (bozo_obj == NULL) {
bozo_obj = PyCFunction_New(&bozo_ml, NULL);
! if (bozo_obj == NULL)
! goto bad_slots;
}
if (PyDict_SetItemString(dict,
"__getstate__",
! bozo_obj) < 0)
! {
! Py_DECREF(bozo_obj);
! goto bad_slots;
! }
! }
!
! /* Secondary bases may provide weakrefs or dict */
! if (nbases > 1 &&
! ((may_add_dict && !add_dict) ||
! (may_add_weak && !add_weak))) {
! for (i = 0; i < nbases; i++) {
! tmp = PyTuple_GET_ITEM(bases, i);
! if (tmp == (PyObject *)base)
! continue; /* Skip primary base */
! if (PyClass_Check(tmp)) {
! /* Classic base class provides both */
! if (may_add_dict && !add_dict)
! add_dict++;
! if (may_add_weak && !add_weak)
! add_weak++;
! break;
! }
! assert(PyType_Check(tmp));
! tmptype = (PyTypeObject *)tmp;
! if (may_add_dict && !add_dict &&
! tmptype->tp_dictoffset != 0)
! add_dict++;
! if (may_add_weak && !add_weak &&
! tmptype->tp_weaklistoffset != 0)
! add_weak++;
! if (may_add_dict && !add_dict)
! continue;
! if (may_add_weak && !add_weak)
! continue;
! /* Nothing more to check */
! break;
}
}
}

***************
*** 1152,1157 ****
/* Allocate the type object */
type = (PyTypeObject *)metatype->tp_alloc(metatype, nslots);
! if (type == NULL)
return NULL;

/* Keep name and slots alive in the extended type object */
--- 1249,1256 ----
/* Allocate the type object */
type = (PyTypeObject *)metatype->tp_alloc(metatype, nslots);
! if (type == NULL) {
! Py_XDECREF(slots);
return NULL;
+ }

/* Keep name and slots alive in the extended type object */
***************
*** 1246,1249 ****
--- 1345,1349 ----
if (base->tp_weaklistoffset == 0 &&
strcmp(mp->name, "__weakref__") == 0) {
+ add_weak++;
mp->type = T_OBJECT;
mp->flags = READONLY;
***************
*** 1253,1280 ****
}
}
! else {
! if (add_dict) {
! if (base->tp_itemsize)
! type->tp_dictoffset =
! -(long)sizeof(PyObject *);
! else
! type->tp_dictoffset = slotoffset;
! slotoffset += sizeof(PyObject *);
! type->tp_getset = subtype_getsets;
! }
! if (add_weak) {
! assert(!base->tp_itemsize);
! type->tp_weaklistoffset = slotoffset;
! mp->name = "__weakref__";
! mp->type = T_OBJECT;
! mp->offset = slotoffset;
! mp->flags = READONLY;
! mp++;
! slotoffset += sizeof(PyObject *);
! }
}
type->tp_basicsize = slotoffset;
type->tp_itemsize = base->tp_itemsize;
type->tp_members = et->members;

/* Special case some slots */
--- 1353,1372 ----
}
}
! if (add_dict) {
! if (base->tp_itemsize)
! type->tp_dictoffset = -(long)sizeof(PyObject *);
! else
! type->tp_dictoffset = slotoffset;
! slotoffset += sizeof(PyObject *);
! }
! if (add_weak) {
! assert(!base->tp_itemsize);
! type->tp_weaklistoffset = slotoffset;
! slotoffset += sizeof(PyObject *);
}
type->tp_basicsize = slotoffset;
type->tp_itemsize = base->tp_itemsize;
type->tp_members = et->members;
+ type->tp_getset = subtype_getsets;

/* Special case some slots */