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:
parent
fc3f7d5677
commit
944368e1cc
|
@ -236,7 +236,8 @@ _code_type = type(_write_atomic.__code__)
|
||||||
# Python 3.6b1 3373 (add BUILD_STRING opcode #27078)
|
# Python 3.6b1 3373 (add BUILD_STRING opcode #27078)
|
||||||
# Python 3.6b1 3375 (add SETUP_ANNOTATIONS and STORE_ANNOTATION opcodes
|
# Python 3.6b1 3375 (add SETUP_ANNOTATIONS and STORE_ANNOTATION opcodes
|
||||||
# #27985)
|
# #27985)
|
||||||
# Python 3.6a1 3376 (simplify CALL_FUNCTIONs & BUILD_MAP_UNPACK_WITH_CALL)
|
# Python 3.6b1 3376 (simplify CALL_FUNCTIONs & BUILD_MAP_UNPACK_WITH_CALL)
|
||||||
|
# Python 3.6b1 3377 (set __class__ cell from type.__new__ #23722)
|
||||||
#
|
#
|
||||||
# MAGIC must change whenever the bytecode emitted by the compiler may no
|
# MAGIC must change whenever the bytecode emitted by the compiler may no
|
||||||
# longer be understood by older implementations of the eval loop (usually
|
# longer be understood by older implementations of the eval loop (usually
|
||||||
|
@ -245,7 +246,7 @@ _code_type = type(_write_atomic.__code__)
|
||||||
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
|
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
|
||||||
# in PC/launcher.c must also be updated.
|
# in PC/launcher.c must also be updated.
|
||||||
|
|
||||||
MAGIC_NUMBER = (3376).to_bytes(2, 'little') + b'\r\n'
|
MAGIC_NUMBER = (3377).to_bytes(2, 'little') + b'\r\n'
|
||||||
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c
|
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c
|
||||||
|
|
||||||
_PYCACHE = '__pycache__'
|
_PYCACHE = '__pycache__'
|
||||||
|
|
|
@ -143,6 +143,87 @@ class TestSuper(unittest.TestCase):
|
||||||
return __class__
|
return __class__
|
||||||
self.assertIs(X.f(), X)
|
self.assertIs(X.f(), X)
|
||||||
|
|
||||||
|
def test___class___new(self):
|
||||||
|
test_class = None
|
||||||
|
|
||||||
|
class Meta(type):
|
||||||
|
def __new__(cls, name, bases, namespace):
|
||||||
|
nonlocal test_class
|
||||||
|
self = super().__new__(cls, name, bases, namespace)
|
||||||
|
test_class = self.f()
|
||||||
|
return self
|
||||||
|
|
||||||
|
class A(metaclass=Meta):
|
||||||
|
@staticmethod
|
||||||
|
def f():
|
||||||
|
return __class__
|
||||||
|
|
||||||
|
self.assertIs(test_class, A)
|
||||||
|
|
||||||
|
def test___class___delayed(self):
|
||||||
|
test_namespace = None
|
||||||
|
|
||||||
|
class Meta(type):
|
||||||
|
def __new__(cls, name, bases, namespace):
|
||||||
|
nonlocal test_namespace
|
||||||
|
test_namespace = namespace
|
||||||
|
return None
|
||||||
|
|
||||||
|
class A(metaclass=Meta):
|
||||||
|
@staticmethod
|
||||||
|
def f():
|
||||||
|
return __class__
|
||||||
|
|
||||||
|
self.assertIs(A, None)
|
||||||
|
|
||||||
|
B = type("B", (), test_namespace)
|
||||||
|
self.assertIs(B.f(), B)
|
||||||
|
|
||||||
|
def test___class___mro(self):
|
||||||
|
test_class = None
|
||||||
|
|
||||||
|
class Meta(type):
|
||||||
|
def mro(self):
|
||||||
|
# self.f() doesn't work yet...
|
||||||
|
self.__dict__["f"]()
|
||||||
|
return super().mro()
|
||||||
|
|
||||||
|
class A(metaclass=Meta):
|
||||||
|
def f():
|
||||||
|
nonlocal test_class
|
||||||
|
test_class = __class__
|
||||||
|
|
||||||
|
self.assertIs(test_class, A)
|
||||||
|
|
||||||
|
def test___classcell___deleted(self):
|
||||||
|
class Meta(type):
|
||||||
|
def __new__(cls, name, bases, namespace):
|
||||||
|
del namespace['__classcell__']
|
||||||
|
return super().__new__(cls, name, bases, namespace)
|
||||||
|
|
||||||
|
class A(metaclass=Meta):
|
||||||
|
@staticmethod
|
||||||
|
def f():
|
||||||
|
__class__
|
||||||
|
|
||||||
|
with self.assertRaises(NameError):
|
||||||
|
A.f()
|
||||||
|
|
||||||
|
def test___classcell___reset(self):
|
||||||
|
class Meta(type):
|
||||||
|
def __new__(cls, name, bases, namespace):
|
||||||
|
namespace['__classcell__'] = 0
|
||||||
|
return super().__new__(cls, name, bases, namespace)
|
||||||
|
|
||||||
|
class A(metaclass=Meta):
|
||||||
|
@staticmethod
|
||||||
|
def f():
|
||||||
|
__class__
|
||||||
|
|
||||||
|
with self.assertRaises(NameError):
|
||||||
|
A.f()
|
||||||
|
self.assertEqual(A.__classcell__, 0)
|
||||||
|
|
||||||
def test_obscure_super_errors(self):
|
def test_obscure_super_errors(self):
|
||||||
def f():
|
def f():
|
||||||
super()
|
super()
|
||||||
|
|
|
@ -10,6 +10,11 @@ What's New in Python 3.6.0 beta 1
|
||||||
Core and Builtins
|
Core and Builtins
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
- Issue #23722: 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.
|
||||||
|
|
||||||
- Issue #25221: Fix corrupted result from PyLong_FromLong(0) when Python
|
- Issue #25221: Fix corrupted result from PyLong_FromLong(0) when Python
|
||||||
is compiled with NSMALLPOSINTS = 0.
|
is compiled with NSMALLPOSINTS = 0.
|
||||||
|
|
||||||
|
|
|
@ -2285,7 +2285,7 @@ static PyObject *
|
||||||
type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
|
type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
|
||||||
{
|
{
|
||||||
PyObject *name, *bases = NULL, *orig_dict, *dict = NULL;
|
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;
|
PyTypeObject *type = NULL, *base, *tmptype, *winner;
|
||||||
PyHeapTypeObject *et;
|
PyHeapTypeObject *et;
|
||||||
PyMemberDef *mp;
|
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;
|
int j, may_add_dict, may_add_weak, add_dict, add_weak;
|
||||||
_Py_IDENTIFIER(__qualname__);
|
_Py_IDENTIFIER(__qualname__);
|
||||||
_Py_IDENTIFIER(__slots__);
|
_Py_IDENTIFIER(__slots__);
|
||||||
|
_Py_IDENTIFIER(__classcell__);
|
||||||
|
|
||||||
assert(args != NULL && PyTuple_Check(args));
|
assert(args != NULL && PyTuple_Check(args));
|
||||||
assert(kwds == NULL || PyDict_Check(kwds));
|
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;
|
et->ht_qualname = qualname ? qualname : et->ht_name;
|
||||||
Py_INCREF(et->ht_qualname);
|
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;
|
goto error;
|
||||||
|
|
||||||
/* 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
|
||||||
|
@ -2685,6 +2686,14 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
|
||||||
else
|
else
|
||||||
type->tp_free = PyObject_Del;
|
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 */
|
/* Initialize the rest */
|
||||||
if (PyType_Ready(type) < 0)
|
if (PyType_Ready(type) < 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
|
@ -1089,7 +1089,7 @@ static PYC_MAGIC magic_values[] = {
|
||||||
{ 3190, 3230, L"3.3" },
|
{ 3190, 3230, L"3.3" },
|
||||||
{ 3250, 3310, L"3.4" },
|
{ 3250, 3310, L"3.4" },
|
||||||
{ 3320, 3351, L"3.5" },
|
{ 3320, 3351, L"3.5" },
|
||||||
{ 3360, 3375, L"3.6" },
|
{ 3360, 3379, L"3.6" },
|
||||||
{ 0 }
|
{ 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -54,7 +54,7 @@ _Py_IDENTIFIER(stderr);
|
||||||
static PyObject *
|
static PyObject *
|
||||||
builtin___build_class__(PyObject *self, PyObject *args, PyObject *kwds)
|
builtin___build_class__(PyObject *self, PyObject *args, PyObject *kwds)
|
||||||
{
|
{
|
||||||
PyObject *func, *name, *bases, *mkw, *meta, *winner, *prep, *ns, *cell;
|
PyObject *func, *name, *bases, *mkw, *meta, *winner, *prep, *ns, *none;
|
||||||
PyObject *cls = NULL;
|
PyObject *cls = NULL;
|
||||||
Py_ssize_t nargs;
|
Py_ssize_t nargs;
|
||||||
int isclass = 0; /* initialize to prevent gcc warning */
|
int isclass = 0; /* initialize to prevent gcc warning */
|
||||||
|
@ -167,15 +167,13 @@ builtin___build_class__(PyObject *self, PyObject *args, PyObject *kwds)
|
||||||
Py_DECREF(bases);
|
Py_DECREF(bases);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
cell = PyEval_EvalCodeEx(PyFunction_GET_CODE(func), PyFunction_GET_GLOBALS(func), ns,
|
none = PyEval_EvalCodeEx(PyFunction_GET_CODE(func), PyFunction_GET_GLOBALS(func), ns,
|
||||||
NULL, 0, NULL, 0, NULL, 0, NULL,
|
NULL, 0, NULL, 0, NULL, 0, NULL,
|
||||||
PyFunction_GET_CLOSURE(func));
|
PyFunction_GET_CLOSURE(func));
|
||||||
if (cell != NULL) {
|
if (none != NULL) {
|
||||||
PyObject *margs[3] = {name, bases, ns};
|
PyObject *margs[3] = {name, bases, ns};
|
||||||
cls = _PyObject_FastCallDict(meta, margs, 3, mkw);
|
cls = _PyObject_FastCallDict(meta, margs, 3, mkw);
|
||||||
if (cls != NULL && PyCell_Check(cell))
|
Py_DECREF(none);
|
||||||
PyCell_Set(cell, cls);
|
|
||||||
Py_DECREF(cell);
|
|
||||||
}
|
}
|
||||||
Py_DECREF(ns);
|
Py_DECREF(ns);
|
||||||
Py_DECREF(meta);
|
Py_DECREF(meta);
|
||||||
|
|
|
@ -1968,7 +1968,7 @@ compiler_class(struct compiler *c, stmt_ty s)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (c->u->u_ste->ste_needs_class_closure) {
|
if (c->u->u_ste->ste_needs_class_closure) {
|
||||||
/* return the (empty) __class__ cell */
|
/* store __classcell__ into class namespace */
|
||||||
str = PyUnicode_InternFromString("__class__");
|
str = PyUnicode_InternFromString("__class__");
|
||||||
if (str == NULL) {
|
if (str == NULL) {
|
||||||
compiler_exit_scope(c);
|
compiler_exit_scope(c);
|
||||||
|
@ -1981,15 +1981,20 @@ compiler_class(struct compiler *c, stmt_ty s)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
assert(i == 0);
|
assert(i == 0);
|
||||||
/* Return the cell where to store __class__ */
|
|
||||||
ADDOP_I(c, LOAD_CLOSURE, i);
|
ADDOP_I(c, LOAD_CLOSURE, i);
|
||||||
|
str = PyUnicode_InternFromString("__classcell__");
|
||||||
|
if (!str || !compiler_nameop(c, str, Store)) {
|
||||||
|
Py_XDECREF(str);
|
||||||
|
compiler_exit_scope(c);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
Py_DECREF(str);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
/* This happens when nobody references the cell. */
|
||||||
assert(PyDict_Size(c->u->u_cellvars) == 0);
|
assert(PyDict_Size(c->u->u_cellvars) == 0);
|
||||||
/* This happens when nobody references the cell. Return None. */
|
|
||||||
ADDOP_O(c, LOAD_CONST, Py_None, consts);
|
|
||||||
}
|
}
|
||||||
ADDOP_IN_SCOPE(c, RETURN_VALUE);
|
|
||||||
/* create the code object */
|
/* create the code object */
|
||||||
co = assemble(c, 1);
|
co = assemble(c, 1);
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue