diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -4474,6 +4474,13 @@ order (MRO) for bases """ self.assertEqual(float.real.__qualname__, 'float.real') self.assertEqual(int.__add__.__qualname__, 'int.__add__') + # Make sure a type's __qualname__ isn't removed from the original dict + d = {'__qualname__': 'some.name'} + tp = type('foo', (), d) + self.assertEqual(tp.__qualname__, 'some.name') + self.assertEqual(tp.__dict__['__qualname__'], 'some.name') + self.assertEqual(d, {'__qualname__': 'some.name'}) + class DictProxyTests(unittest.TestCase): def setUp(self): @@ -4491,7 +4498,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 +4507,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 +4518,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 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -2040,18 +2040,6 @@ type_new(PyTypeObject *metatype, PyObjec 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; - } - } - /* Check for a __slots__ sequence variable in dict, and count it */ slots = PyDict_GetItemString(dict, "__slots__"); nslots = 0; @@ -2205,9 +2193,7 @@ type_new(PyTypeObject *metatype, PyObjec /* 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; /* Initialize tp_flags */ @@ -2252,6 +2238,16 @@ type_new(PyTypeObject *metatype, PyObjec } } + /* 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) { + 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__.