Close #14095: type.__new__() doesn't remove __qualname__ key from the class

dict anymore if the key is present. Reject also non-string qualified names.
And fix reference leaks in type.__new__().
This commit is contained in:
Victor Stinner 2012-02-25 01:22:36 +01:00
parent b0800dc53b
commit 6f73874edd
2 changed files with 83 additions and 88 deletions

View File

@ -4474,6 +4474,16 @@ order (MRO) for bases """
self.assertEqual(float.real.__qualname__, 'float.real') self.assertEqual(float.real.__qualname__, 'float.real')
self.assertEqual(int.__add__.__qualname__, 'int.__add__') self.assertEqual(int.__add__.__qualname__, 'int.__add__')
def test_qualname_dict(self):
ns = {'__qualname__': 'some.name'}
tp = type('Foo', (), ns)
self.assertEqual(tp.__qualname__, 'some.name')
self.assertEqual(tp.__dict__['__qualname__'], 'some.name')
self.assertEqual(ns, {'__qualname__': 'some.name'})
ns = {'__qualname__': 1}
self.assertRaises(TypeError, type, 'Foo', (), ns)
class DictProxyTests(unittest.TestCase): class DictProxyTests(unittest.TestCase):
def setUp(self): def setUp(self):
@ -4491,7 +4501,7 @@ class DictProxyTests(unittest.TestCase):
keys = list(it) keys = list(it)
keys.sort() keys.sort()
self.assertEqual(keys, ['__dict__', '__doc__', '__module__', self.assertEqual(keys, ['__dict__', '__doc__', '__module__',
'__weakref__', 'meth']) '__qualname__', '__weakref__', 'meth'])
@unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(), @unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(),
'trace function introduces __local__') 'trace function introduces __local__')
@ -4500,7 +4510,7 @@ class DictProxyTests(unittest.TestCase):
it = self.C.__dict__.values() it = self.C.__dict__.values()
self.assertNotIsInstance(it, list) self.assertNotIsInstance(it, list)
values = list(it) values = list(it)
self.assertEqual(len(values), 5) self.assertEqual(len(values), 6)
@unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(), @unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(),
'trace function introduces __local__') 'trace function introduces __local__')
@ -4511,7 +4521,7 @@ class DictProxyTests(unittest.TestCase):
keys = [item[0] for item in it] keys = [item[0] for item in it]
keys.sort() keys.sort()
self.assertEqual(keys, ['__dict__', '__doc__', '__module__', self.assertEqual(keys, ['__dict__', '__doc__', '__module__',
'__weakref__', 'meth']) '__qualname__', '__weakref__', 'meth'])
def test_dict_type_with_metaclass(self): def test_dict_type_with_metaclass(self):
# Testing type of __dict__ when metaclass set... # Testing type of __dict__ when metaclass set...

View File

@ -1961,10 +1961,10 @@ _PyType_CalculateMetaclass(PyTypeObject *metatype, PyObject *bases)
static PyObject * static PyObject *
type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
{ {
PyObject *name, *bases, *dict; PyObject *name, *bases = NULL, *orig_dict, *dict = NULL;
static char *kwlist[] = {"name", "bases", "dict", 0}; static char *kwlist[] = {"name", "bases", "dict", 0};
PyObject *qualname, *slots, *tmp, *newslots; PyObject *qualname, *slots = NULL, *tmp, *newslots;
PyTypeObject *type, *base, *tmptype, *winner; PyTypeObject *type = NULL, *base, *tmptype, *winner;
PyHeapTypeObject *et; PyHeapTypeObject *et;
PyMemberDef *mp; PyMemberDef *mp;
Py_ssize_t i, nbases, nslots, slotoffset, add_dict, add_weak; Py_ssize_t i, nbases, nslots, slotoffset, add_dict, add_weak;
@ -1998,7 +1998,7 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
if (!PyArg_ParseTupleAndKeywords(args, kwds, "UO!O!:type", kwlist, if (!PyArg_ParseTupleAndKeywords(args, kwds, "UO!O!:type", kwlist,
&name, &name,
&PyTuple_Type, &bases, &PyTuple_Type, &bases,
&PyDict_Type, &dict)) &PyDict_Type, &orig_dict))
return NULL; return NULL;
/* Determine the proper metatype to deal with this: */ /* Determine the proper metatype to deal with this: */
@ -2018,39 +2018,27 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
if (nbases == 0) { if (nbases == 0) {
bases = PyTuple_Pack(1, &PyBaseObject_Type); bases = PyTuple_Pack(1, &PyBaseObject_Type);
if (bases == NULL) if (bases == NULL)
return NULL; goto error;
nbases = 1; nbases = 1;
} }
else else
Py_INCREF(bases); Py_INCREF(bases);
/* XXX From here until type is allocated, "return NULL" leaks bases! */
/* Calculate best base, and check that all bases are type objects */ /* Calculate best base, and check that all bases are type objects */
base = best_base(bases); base = best_base(bases);
if (base == NULL) { if (base == NULL) {
Py_DECREF(bases); goto error;
return NULL;
} }
if (!PyType_HasFeature(base, Py_TPFLAGS_BASETYPE)) { if (!PyType_HasFeature(base, Py_TPFLAGS_BASETYPE)) {
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError,
"type '%.100s' is not an acceptable base type", "type '%.100s' is not an acceptable base type",
base->tp_name); base->tp_name);
Py_DECREF(bases); goto error;
return NULL;
} }
/* Check for a __qualname__ variable in dict */ dict = PyDict_Copy(orig_dict);
qualname = PyDict_GetItemString(dict, "__qualname__"); if (dict == NULL)
if (qualname == NULL) { goto error;
qualname = name;
}
else {
if (PyDict_DelItemString(dict, "__qualname__") < 0) {
Py_DECREF(bases);
return NULL;
}
}
/* Check for a __slots__ sequence variable in dict, and count it */ /* Check for a __slots__ sequence variable in dict, and count it */
slots = PyDict_GetItemString(dict, "__slots__"); slots = PyDict_GetItemString(dict, "__slots__");
@ -2075,10 +2063,8 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
slots = PyTuple_Pack(1, slots); slots = PyTuple_Pack(1, slots);
else else
slots = PySequence_Tuple(slots); slots = PySequence_Tuple(slots);
if (slots == NULL) { if (slots == NULL)
Py_DECREF(bases); goto error;
return NULL;
}
assert(PyTuple_Check(slots)); assert(PyTuple_Check(slots));
/* Are slots allowed? */ /* Are slots allowed? */
@ -2088,24 +2074,21 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
"nonempty __slots__ " "nonempty __slots__ "
"not supported for subtype of '%s'", "not supported for subtype of '%s'",
base->tp_name); base->tp_name);
bad_slots: goto error;
Py_DECREF(bases);
Py_DECREF(slots);
return NULL;
} }
/* Check for valid slot names and two special cases */ /* Check for valid slot names and two special cases */
for (i = 0; i < nslots; i++) { for (i = 0; i < nslots; i++) {
PyObject *tmp = PyTuple_GET_ITEM(slots, i); PyObject *tmp = PyTuple_GET_ITEM(slots, i);
if (!valid_identifier(tmp)) if (!valid_identifier(tmp))
goto bad_slots; goto error;
assert(PyUnicode_Check(tmp)); assert(PyUnicode_Check(tmp));
if (PyUnicode_CompareWithASCIIString(tmp, "__dict__") == 0) { if (PyUnicode_CompareWithASCIIString(tmp, "__dict__") == 0) {
if (!may_add_dict || add_dict) { if (!may_add_dict || add_dict) {
PyErr_SetString(PyExc_TypeError, PyErr_SetString(PyExc_TypeError,
"__dict__ slot disallowed: " "__dict__ slot disallowed: "
"we already got one"); "we already got one");
goto bad_slots; goto error;
} }
add_dict++; add_dict++;
} }
@ -2115,7 +2098,7 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
"__weakref__ slot disallowed: " "__weakref__ slot disallowed: "
"either we already got one, " "either we already got one, "
"or __itemsize__ != 0"); "or __itemsize__ != 0");
goto bad_slots; goto error;
} }
add_weak++; add_weak++;
} }
@ -2127,7 +2110,7 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
*/ */
newslots = PyList_New(nslots - add_dict - add_weak); newslots = PyList_New(nslots - add_dict - add_weak);
if (newslots == NULL) if (newslots == NULL)
goto bad_slots; goto error;
for (i = j = 0; i < nslots; i++) { for (i = j = 0; i < nslots; i++) {
tmp = PyTuple_GET_ITEM(slots, i); tmp = PyTuple_GET_ITEM(slots, i);
if ((add_dict && if ((add_dict &&
@ -2138,7 +2121,7 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
tmp =_Py_Mangle(name, tmp); tmp =_Py_Mangle(name, tmp);
if (!tmp) { if (!tmp) {
Py_DECREF(newslots); Py_DECREF(newslots);
goto bad_slots; goto error;
} }
PyList_SET_ITEM(newslots, j, tmp); PyList_SET_ITEM(newslots, j, tmp);
if (PyDict_GetItem(dict, tmp)) { if (PyDict_GetItem(dict, tmp)) {
@ -2146,24 +2129,21 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
"%R in __slots__ conflicts with class variable", "%R in __slots__ conflicts with class variable",
tmp); tmp);
Py_DECREF(newslots); Py_DECREF(newslots);
goto bad_slots; goto error;
} }
j++; j++;
} }
assert(j == nslots - add_dict - add_weak); assert(j == nslots - add_dict - add_weak);
nslots = j; nslots = j;
Py_DECREF(slots); Py_CLEAR(slots);
if (PyList_Sort(newslots) == -1) { if (PyList_Sort(newslots) == -1) {
Py_DECREF(bases);
Py_DECREF(newslots); Py_DECREF(newslots);
return NULL; goto error;
} }
slots = PyList_AsTuple(newslots); slots = PyList_AsTuple(newslots);
Py_DECREF(newslots); Py_DECREF(newslots);
if (slots == NULL) { if (slots == NULL)
Py_DECREF(bases); goto error;
return NULL;
}
/* Secondary bases may provide weakrefs or dict */ /* Secondary bases may provide weakrefs or dict */
if (nbases > 1 && if (nbases > 1 &&
@ -2191,24 +2171,17 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
} }
} }
/* XXX From here until type is safely allocated,
"return NULL" may leak slots! */
/* Allocate the type object */ /* Allocate the type object */
type = (PyTypeObject *)metatype->tp_alloc(metatype, nslots); type = (PyTypeObject *)metatype->tp_alloc(metatype, nslots);
if (type == NULL) { if (type == NULL)
Py_XDECREF(slots); goto error;
Py_DECREF(bases);
return NULL;
}
/* Keep name and slots alive in the extended type object */ /* Keep name and slots alive in the extended type object */
et = (PyHeapTypeObject *)type; et = (PyHeapTypeObject *)type;
Py_INCREF(name); Py_INCREF(name);
Py_INCREF(qualname);
et->ht_name = name; et->ht_name = name;
et->ht_qualname = qualname;
et->ht_slots = slots; et->ht_slots = slots;
slots = NULL;
/* Initialize tp_flags */ /* Initialize tp_flags */
type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE | type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE |
@ -2222,22 +2195,18 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
type->tp_as_mapping = &et->as_mapping; type->tp_as_mapping = &et->as_mapping;
type->tp_as_buffer = &et->as_buffer; type->tp_as_buffer = &et->as_buffer;
type->tp_name = _PyUnicode_AsString(name); type->tp_name = _PyUnicode_AsString(name);
if (!type->tp_name) { if (!type->tp_name)
Py_DECREF(type); goto error;
return NULL;
}
/* Set tp_base and tp_bases */ /* Set tp_base and tp_bases */
type->tp_bases = bases; type->tp_bases = bases;
bases = NULL;
Py_INCREF(base); Py_INCREF(base);
type->tp_base = base; type->tp_base = base;
/* Initialize tp_dict from passed-in dict */ /* Initialize tp_dict from passed-in dict */
type->tp_dict = dict = PyDict_Copy(dict); Py_INCREF(dict);
if (dict == NULL) { type->tp_dict = dict;
Py_DECREF(type);
return NULL;
}
/* Set __module__ in the dict */ /* Set __module__ in the dict */
if (PyDict_GetItemString(dict, "__module__") == NULL) { if (PyDict_GetItemString(dict, "__module__") == NULL) {
@ -2247,11 +2216,29 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
if (tmp != NULL) { if (tmp != NULL) {
if (PyDict_SetItemString(dict, "__module__", if (PyDict_SetItemString(dict, "__module__",
tmp) < 0) tmp) < 0)
return NULL; goto error;
} }
} }
} }
/* Set ht_qualname to dict['__qualname__'] if available, else to
__name__. The __qualname__ accessor will look for ht_qualname.
*/
qualname = PyDict_GetItemString(dict, "__qualname__");
if (qualname != NULL) {
if (!PyUnicode_Check(qualname)) {
PyErr_Format(PyExc_TypeError,
"type __qualname__ must be a str, not %s",
Py_TYPE(qualname)->tp_name);
goto error;
}
}
else {
qualname = et->ht_name;
}
Py_INCREF(qualname);
et->ht_qualname = qualname;
/* Set tp_doc to a copy of dict['__doc__'], if the latter is there /* Set tp_doc to a copy of dict['__doc__'], if the latter is there
and is a string. The __doc__ accessor will first look for tp_doc; and is a string. The __doc__ accessor will first look for tp_doc;
if that fails, it will still look into __dict__. if that fails, it will still look into __dict__.
@ -2264,17 +2251,13 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
char *tp_doc; char *tp_doc;
doc_str = _PyUnicode_AsString(doc); doc_str = _PyUnicode_AsString(doc);
if (doc_str == NULL) { if (doc_str == NULL)
Py_DECREF(type); goto error;
return NULL;
}
/* Silently truncate the docstring if it contains null bytes. */ /* Silently truncate the docstring if it contains null bytes. */
len = strlen(doc_str); len = strlen(doc_str);
tp_doc = (char *)PyObject_MALLOC(len + 1); tp_doc = (char *)PyObject_MALLOC(len + 1);
if (tp_doc == NULL) { if (tp_doc == NULL)
Py_DECREF(type); goto error;
return NULL;
}
memcpy(tp_doc, doc_str, len + 1); memcpy(tp_doc, doc_str, len + 1);
type->tp_doc = tp_doc; type->tp_doc = tp_doc;
} }
@ -2285,10 +2268,8 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
tmp = PyDict_GetItemString(dict, "__new__"); tmp = PyDict_GetItemString(dict, "__new__");
if (tmp != NULL && PyFunction_Check(tmp)) { if (tmp != NULL && PyFunction_Check(tmp)) {
tmp = PyStaticMethod_New(tmp); tmp = PyStaticMethod_New(tmp);
if (tmp == NULL) { if (tmp == NULL)
Py_DECREF(type); goto error;
return NULL;
}
PyDict_SetItemString(dict, "__new__", tmp); PyDict_SetItemString(dict, "__new__", tmp);
Py_DECREF(tmp); Py_DECREF(tmp);
} }
@ -2296,14 +2277,12 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
/* Add descriptors for custom slots from __slots__, or for __dict__ */ /* Add descriptors for custom slots from __slots__, or for __dict__ */
mp = PyHeapType_GET_MEMBERS(et); mp = PyHeapType_GET_MEMBERS(et);
slotoffset = base->tp_basicsize; slotoffset = base->tp_basicsize;
if (slots != NULL) { if (et->ht_slots != NULL) {
for (i = 0; i < nslots; i++, mp++) { for (i = 0; i < nslots; i++, mp++) {
mp->name = _PyUnicode_AsString( mp->name = _PyUnicode_AsString(
PyTuple_GET_ITEM(slots, i)); PyTuple_GET_ITEM(et->ht_slots, i));
if (mp->name == NULL) { if (mp->name == NULL)
Py_DECREF(type); goto error;
return NULL;
}
mp->type = T_OBJECT_EX; mp->type = T_OBJECT_EX;
mp->offset = slotoffset; mp->offset = slotoffset;
@ -2364,15 +2343,21 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
type->tp_free = PyObject_Del; type->tp_free = PyObject_Del;
/* Initialize the rest */ /* Initialize the rest */
if (PyType_Ready(type) < 0) { if (PyType_Ready(type) < 0)
Py_DECREF(type); goto error;
return NULL;
}
/* Put the proper slots in place */ /* Put the proper slots in place */
fixup_slot_dispatchers(type); fixup_slot_dispatchers(type);
Py_DECREF(dict);
return (PyObject *)type; return (PyObject *)type;
error:
Py_XDECREF(dict);
Py_XDECREF(bases);
Py_XDECREF(slots);
Py_XDECREF(type);
return NULL;
} }
static short slotoffsets[] = { static short slotoffsets[] = {