diff -r d937a32fe173 Lib/test/test_descr.py --- a/Lib/test/test_descr.py Thu Feb 23 20:55:35 2012 +0000 +++ b/Lib/test/test_descr.py Thu Feb 23 22:15:18 2012 +0100 @@ -4474,6 +4474,16 @@ order (MRO) for bases """ self.assertEqual(float.real.__qualname__, 'float.real') 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): def setUp(self): @@ -4491,7 +4501,7 @@ class DictProxyTests(unittest.TestCase): keys = list(it) keys.sort() self.assertEqual(keys, ['__dict__', '__doc__', '__module__', - '__weakref__', 'meth']) + '__qualname__', '__weakref__', 'meth']) @unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(), 'trace function introduces __local__') @@ -4500,7 +4510,7 @@ class DictProxyTests(unittest.TestCase): it = self.C.__dict__.values() self.assertNotIsInstance(it, list) values = list(it) - self.assertEqual(len(values), 5) + self.assertEqual(len(values), 6) @unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(), 'trace function introduces __local__') @@ -4511,7 +4521,7 @@ class DictProxyTests(unittest.TestCase): keys = [item[0] for item in it] keys.sort() self.assertEqual(keys, ['__dict__', '__doc__', '__module__', - '__weakref__', 'meth']) + '__qualname__', '__weakref__', 'meth']) def test_dict_type_with_metaclass(self): # Testing type of __dict__ when metaclass set... diff -r d937a32fe173 Objects/typeobject.c --- a/Objects/typeobject.c Thu Feb 23 20:55:35 2012 +0000 +++ b/Objects/typeobject.c Thu Feb 23 22:15:18 2012 +0100 @@ -1961,10 +1961,10 @@ _PyType_CalculateMetaclass(PyTypeObject static PyObject * 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}; - PyObject *qualname, *slots, *tmp, *newslots; - PyTypeObject *type, *base, *tmptype, *winner; + PyObject *qualname, *slots = NULL, *tmp, *newslots; + PyTypeObject *type = NULL, *base, *tmptype, *winner; PyHeapTypeObject *et; PyMemberDef *mp; Py_ssize_t i, nbases, nslots, slotoffset, add_dict, add_weak; @@ -1998,7 +1998,7 @@ type_new(PyTypeObject *metatype, PyObjec if (!PyArg_ParseTupleAndKeywords(args, kwds, "UO!O!:type", kwlist, &name, &PyTuple_Type, &bases, - &PyDict_Type, &dict)) + &PyDict_Type, &orig_dict)) return NULL; /* Determine the proper metatype to deal with this: */ @@ -2018,39 +2018,27 @@ type_new(PyTypeObject *metatype, PyObjec if (nbases == 0) { bases = PyTuple_Pack(1, &PyBaseObject_Type); if (bases == NULL) - return NULL; + goto error; nbases = 1; } else 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 */ base = best_base(bases); if (base == NULL) { - Py_DECREF(bases); - return NULL; + goto error; } if (!PyType_HasFeature(base, Py_TPFLAGS_BASETYPE)) { PyErr_Format(PyExc_TypeError, "type '%.100s' is not an acceptable base type", base->tp_name); - Py_DECREF(bases); - return NULL; - } - - /* Check for a __qualname__ variable in dict */ - qualname = PyDict_GetItemString(dict, "__qualname__"); - if (qualname == NULL) { - qualname = name; - } - else { - if (PyDict_DelItemString(dict, "__qualname__") < 0) { - Py_DECREF(bases); - return NULL; - } - } + goto error; + } + + dict = PyDict_Copy(orig_dict); + if (dict == NULL) + goto error; /* Check for a __slots__ sequence variable in dict, and count it */ slots = PyDict_GetItemString(dict, "__slots__"); @@ -2075,10 +2063,8 @@ type_new(PyTypeObject *metatype, PyObjec slots = PyTuple_Pack(1, slots); else slots = PySequence_Tuple(slots); - if (slots == NULL) { - Py_DECREF(bases); - return NULL; - } + if (slots == NULL) + goto error; assert(PyTuple_Check(slots)); /* Are slots allowed? */ @@ -2088,24 +2074,21 @@ type_new(PyTypeObject *metatype, PyObjec "nonempty __slots__ " "not supported for subtype of '%s'", base->tp_name); - bad_slots: - Py_DECREF(bases); - Py_DECREF(slots); - return NULL; + goto error; } /* Check for valid slot names and two special cases */ for (i = 0; i < nslots; i++) { PyObject *tmp = PyTuple_GET_ITEM(slots, i); if (!valid_identifier(tmp)) - goto bad_slots; + goto error; assert(PyUnicode_Check(tmp)); if (PyUnicode_CompareWithASCIIString(tmp, "__dict__") == 0) { if (!may_add_dict || add_dict) { PyErr_SetString(PyExc_TypeError, "__dict__ slot disallowed: " "we already got one"); - goto bad_slots; + goto error; } add_dict++; } @@ -2115,7 +2098,7 @@ type_new(PyTypeObject *metatype, PyObjec "__weakref__ slot disallowed: " "either we already got one, " "or __itemsize__ != 0"); - goto bad_slots; + goto error; } add_weak++; } @@ -2127,7 +2110,7 @@ type_new(PyTypeObject *metatype, PyObjec */ newslots = PyList_New(nslots - add_dict - add_weak); if (newslots == NULL) - goto bad_slots; + goto error; for (i = j = 0; i < nslots; i++) { tmp = PyTuple_GET_ITEM(slots, i); if ((add_dict && @@ -2138,7 +2121,7 @@ type_new(PyTypeObject *metatype, PyObjec tmp =_Py_Mangle(name, tmp); if (!tmp) { Py_DECREF(newslots); - goto bad_slots; + goto error; } PyList_SET_ITEM(newslots, j, tmp); if (PyDict_GetItem(dict, tmp)) { @@ -2146,24 +2129,21 @@ type_new(PyTypeObject *metatype, PyObjec "%R in __slots__ conflicts with class variable", tmp); Py_DECREF(newslots); - goto bad_slots; + goto error; } j++; } assert(j == nslots - add_dict - add_weak); nslots = j; - Py_DECREF(slots); + Py_CLEAR(slots); if (PyList_Sort(newslots) == -1) { - Py_DECREF(bases); Py_DECREF(newslots); - return NULL; + goto error; } slots = PyList_AsTuple(newslots); Py_DECREF(newslots); - if (slots == NULL) { - Py_DECREF(bases); - return NULL; - } + if (slots == NULL) + goto error; /* Secondary bases may provide weakrefs or dict */ if (nbases > 1 && @@ -2191,24 +2171,17 @@ type_new(PyTypeObject *metatype, PyObjec } } - /* XXX From here until type is safely allocated, - "return NULL" may leak slots! */ - /* Allocate the type object */ type = (PyTypeObject *)metatype->tp_alloc(metatype, nslots); - if (type == NULL) { - Py_XDECREF(slots); - Py_DECREF(bases); - return NULL; - } + if (type == NULL) + goto error; /* Keep name and slots alive in the extended type object */ et = (PyHeapTypeObject *)type; Py_INCREF(name); - Py_INCREF(qualname); et->ht_name = name; - et->ht_qualname = qualname; et->ht_slots = slots; + slots = NULL; /* Initialize tp_flags */ type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE | @@ -2222,22 +2195,18 @@ type_new(PyTypeObject *metatype, PyObjec type->tp_as_mapping = &et->as_mapping; type->tp_as_buffer = &et->as_buffer; type->tp_name = _PyUnicode_AsString(name); - if (!type->tp_name) { - Py_DECREF(type); - return NULL; - } + if (!type->tp_name) + goto error; /* Set tp_base and tp_bases */ type->tp_bases = bases; + bases = NULL; Py_INCREF(base); type->tp_base = base; /* Initialize tp_dict from passed-in dict */ - type->tp_dict = dict = PyDict_Copy(dict); - if (dict == NULL) { - Py_DECREF(type); - return NULL; - } + Py_INCREF(dict); + type->tp_dict = dict; /* Set __module__ in the dict */ if (PyDict_GetItemString(dict, "__module__") == NULL) { @@ -2247,11 +2216,29 @@ type_new(PyTypeObject *metatype, PyObjec if (tmp != NULL) { if (PyDict_SetItemString(dict, "__module__", 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 and is a string. The __doc__ accessor will first look for tp_doc; if that fails, it will still look into __dict__. @@ -2264,17 +2251,13 @@ type_new(PyTypeObject *metatype, PyObjec char *tp_doc; doc_str = _PyUnicode_AsString(doc); - if (doc_str == NULL) { - Py_DECREF(type); - return NULL; - } + if (doc_str == NULL) + goto error; /* Silently truncate the docstring if it contains null bytes. */ len = strlen(doc_str); tp_doc = (char *)PyObject_MALLOC(len + 1); - if (tp_doc == NULL) { - Py_DECREF(type); - return NULL; - } + if (tp_doc == NULL) + goto error; memcpy(tp_doc, doc_str, len + 1); type->tp_doc = tp_doc; } @@ -2285,10 +2268,8 @@ type_new(PyTypeObject *metatype, PyObjec tmp = PyDict_GetItemString(dict, "__new__"); if (tmp != NULL && PyFunction_Check(tmp)) { tmp = PyStaticMethod_New(tmp); - if (tmp == NULL) { - Py_DECREF(type); - return NULL; - } + if (tmp == NULL) + goto error; PyDict_SetItemString(dict, "__new__", tmp); Py_DECREF(tmp); } @@ -2296,14 +2277,12 @@ type_new(PyTypeObject *metatype, PyObjec /* Add descriptors for custom slots from __slots__, or for __dict__ */ mp = PyHeapType_GET_MEMBERS(et); slotoffset = base->tp_basicsize; - if (slots != NULL) { + if (et->ht_slots != NULL) { for (i = 0; i < nslots; i++, mp++) { mp->name = _PyUnicode_AsString( - PyTuple_GET_ITEM(slots, i)); - if (mp->name == NULL) { - Py_DECREF(type); - return NULL; - } + PyTuple_GET_ITEM(et->ht_slots, i)); + if (mp->name == NULL) + goto error; mp->type = T_OBJECT_EX; mp->offset = slotoffset; @@ -2364,15 +2343,21 @@ type_new(PyTypeObject *metatype, PyObjec type->tp_free = PyObject_Del; /* Initialize the rest */ - if (PyType_Ready(type) < 0) { - Py_DECREF(type); - return NULL; - } + if (PyType_Ready(type) < 0) + goto error; /* Put the proper slots in place */ fixup_slot_dispatchers(type); + Py_DECREF(dict); return (PyObject *)type; + +error: + Py_XDECREF(dict); + Py_XDECREF(bases); + Py_XDECREF(slots); + Py_XDECREF(type); + return NULL; } static short slotoffsets[] = {