Issue #23722: Initialize __class__ from type.__new__()

The __class__ cell used by zero-argument super() is now initialized
from type.__new__ rather than __build_class__, so class methods
relying on that will now work correctly when called from metaclass
methods during class creation.

Patch by Martin Teichmann.
This commit is contained in:
Nick Coghlan 2016-09-11 14:45:49 +10:00
parent fc3f7d5677
commit 944368e1cc
8 changed files with 1358 additions and 1258 deletions

View file

@ -2285,7 +2285,7 @@ static PyObject *
type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
{
PyObject *name, *bases = NULL, *orig_dict, *dict = NULL;
PyObject *qualname, *slots = NULL, *tmp, *newslots;
PyObject *qualname, *slots = NULL, *tmp, *newslots, *cell;
PyTypeObject *type = NULL, *base, *tmptype, *winner;
PyHeapTypeObject *et;
PyMemberDef *mp;
@ -2293,6 +2293,7 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
int j, may_add_dict, may_add_weak, add_dict, add_weak;
_Py_IDENTIFIER(__qualname__);
_Py_IDENTIFIER(__slots__);
_Py_IDENTIFIER(__classcell__);
assert(args != NULL && PyTuple_Check(args));
assert(kwds == NULL || PyDict_Check(kwds));
@ -2559,7 +2560,7 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
}
et->ht_qualname = qualname ? qualname : et->ht_name;
Py_INCREF(et->ht_qualname);
if (qualname != NULL && PyDict_DelItem(dict, PyId___qualname__.object) < 0)
if (qualname != NULL && _PyDict_DelItemId(dict, &PyId___qualname__) < 0)
goto error;
/* Set tp_doc to a copy of dict['__doc__'], if the latter is there
@ -2685,6 +2686,14 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
else
type->tp_free = PyObject_Del;
/* store type in class' cell */
cell = _PyDict_GetItemId(dict, &PyId___classcell__);
if (cell != NULL && PyCell_Check(cell)) {
PyCell_Set(cell, (PyObject *) type);
_PyDict_DelItemId(dict, &PyId___classcell__);
PyErr_Clear();
}
/* Initialize the rest */
if (PyType_Ready(type) < 0)
goto error;