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(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...
|
||||||
|
|
|
@ -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[] = {
|
||||||
|
|
Loading…
Reference in New Issue