mirror of https://github.com/python/cpython
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:
parent
b0800dc53b
commit
6f73874edd
|
@ -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...
|
||||
|
|
|
@ -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[] = {
|
||||
|
|
Loading…
Reference in New Issue