diff options
author | Victor Stinner <victor.stinner@gmail.com> | 2012-02-25 00:22:36 (GMT) |
---|---|---|
committer | Victor Stinner <victor.stinner@gmail.com> | 2012-02-25 00:22:36 (GMT) |
commit | 6f73874edd30afaa9580f63d3ba0f1ea9c4b09b7 (patch) | |
tree | 2f0e2d2ca36b96315439cc3fbe6c87aeb9abc548 | |
parent | b0800dc53bdcb77b6fb7c07645460b7954792290 (diff) | |
download | cpython-6f73874edd30afaa9580f63d3ba0f1ea9c4b09b7.zip cpython-6f73874edd30afaa9580f63d3ba0f1ea9c4b09b7.tar.gz cpython-6f73874edd30afaa9580f63d3ba0f1ea9c4b09b7.tar.bz2 |
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__().
-rw-r--r-- | Lib/test/test_descr.py | 16 | ||||
-rw-r--r-- | Objects/typeobject.c | 155 |
2 files changed, 83 insertions, 88 deletions
diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index 7b9a523..d64af69 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -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 --git a/Objects/typeobject.c b/Objects/typeobject.c index 9aa752e..50b9723 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -1961,10 +1961,10 @@ _PyType_CalculateMetaclass(PyTypeObject *metatype, PyObject *bases) 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, PyObject *args, PyObject *kwds) 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, PyObject *args, PyObject *kwds) 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; + goto error; } - /* 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; - } - } + 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, PyObject *args, PyObject *kwds) 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, PyObject *args, PyObject *kwds) "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, PyObject *args, PyObject *kwds) "__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, PyObject *args, PyObject *kwds) */ 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, PyObject *args, PyObject *kwds) 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, PyObject *args, PyObject *kwds) "%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, PyObject *args, PyObject *kwds) } } - /* 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, PyObject *args, PyObject *kwds) 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, PyObject *args, PyObject *kwds) 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, PyObject *args, PyObject *kwds) 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, PyObject *args, PyObject *kwds) 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, PyObject *args, PyObject *kwds) /* 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, PyObject *args, PyObject *kwds) 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[] = { |