cpython: c72063032a7a (original) (raw)
Mercurial > cpython
changeset 73062:c72063032a7a
Merge issue 1294232 patch from 3.2 [#1294232]
Nick Coghlan ncoghlan@gmail.com | |
---|---|
date | Sun, 23 Oct 2011 22:36:42 +1000 |
parents | 96d5f53a414a(current diff)c2a89b509be4(diff) |
children | 1959b2332c6a |
files | Include/object.h Lib/test/test_descr.py Misc/NEWS Objects/typeobject.c Python/bltinmodule.c |
diffstat | 5 files changed, 244 insertions(+), 23 deletions(-)[+] [-] Include/object.h 1 Lib/test/test_descr.py 168 Misc/NEWS 4 Objects/typeobject.c 61 Python/bltinmodule.c 33 |
line wrap: on
line diff
--- a/Include/object.h +++ b/Include/object.h @@ -449,6 +449,7 @@ PyAPI_FUNC(PyObject *) PyType_GenericNew #ifndef Py_LIMITED_API PyAPI_FUNC(PyObject *) _PyType_Lookup(PyTypeObject *, PyObject *); PyAPI_FUNC(PyObject *) _PyObject_LookupSpecial(PyObject *, char *, PyObject **); +PyAPI_FUNC(PyTypeObject *) _PyType_CalculateMetaclass(PyTypeObject *, PyObject *); #endif PyAPI_FUNC(unsigned int) PyType_ClearCache(void); PyAPI_FUNC(void) PyType_Modified(PyTypeObject *);
--- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -625,6 +625,174 @@ class ClassPropertiesAndMethods(unittest # The most derived metaclass of D is A rather than type. class D(B, C): pass
self.assertIs(A, type(D))[](#l2.7)
# issue1294232: correct metaclass calculation[](#l2.9)
new_calls = [] # to check the order of __new__ calls[](#l2.10)
class AMeta(type):[](#l2.11)
@staticmethod[](#l2.12)
def __new__(mcls, name, bases, ns):[](#l2.13)
new_calls.append('AMeta')[](#l2.14)
return super().__new__(mcls, name, bases, ns)[](#l2.15)
@classmethod[](#l2.16)
def __prepare__(mcls, name, bases):[](#l2.17)
return {}[](#l2.18)
class BMeta(AMeta):[](#l2.20)
@staticmethod[](#l2.21)
def __new__(mcls, name, bases, ns):[](#l2.22)
new_calls.append('BMeta')[](#l2.23)
return super().__new__(mcls, name, bases, ns)[](#l2.24)
@classmethod[](#l2.25)
def __prepare__(mcls, name, bases):[](#l2.26)
ns = super().__prepare__(name, bases)[](#l2.27)
ns['BMeta_was_here'] = True[](#l2.28)
return ns[](#l2.29)
class A(metaclass=AMeta):[](#l2.31)
pass[](#l2.32)
self.assertEqual(['AMeta'], new_calls)[](#l2.33)
new_calls.clear()[](#l2.34)
class B(metaclass=BMeta):[](#l2.36)
pass[](#l2.37)
# BMeta.__new__ calls AMeta.__new__ with super:[](#l2.38)
self.assertEqual(['BMeta', 'AMeta'], new_calls)[](#l2.39)
new_calls.clear()[](#l2.40)
class C(A, B):[](#l2.42)
pass[](#l2.43)
# The most derived metaclass is BMeta:[](#l2.44)
self.assertEqual(['BMeta', 'AMeta'], new_calls)[](#l2.45)
new_calls.clear()[](#l2.46)
# BMeta.__prepare__ should've been called:[](#l2.47)
self.assertIn('BMeta_was_here', C.__dict__)[](#l2.48)
# The order of the bases shouldn't matter:[](#l2.50)
class C2(B, A):[](#l2.51)
pass[](#l2.52)
self.assertEqual(['BMeta', 'AMeta'], new_calls)[](#l2.53)
new_calls.clear()[](#l2.54)
self.assertIn('BMeta_was_here', C2.__dict__)[](#l2.55)
# Check correct metaclass calculation when a metaclass is declared:[](#l2.57)
class D(C, metaclass=type):[](#l2.58)
pass[](#l2.59)
self.assertEqual(['BMeta', 'AMeta'], new_calls)[](#l2.60)
new_calls.clear()[](#l2.61)
self.assertIn('BMeta_was_here', D.__dict__)[](#l2.62)
class E(C, metaclass=AMeta):[](#l2.64)
pass[](#l2.65)
self.assertEqual(['BMeta', 'AMeta'], new_calls)[](#l2.66)
new_calls.clear()[](#l2.67)
self.assertIn('BMeta_was_here', E.__dict__)[](#l2.68)
# Special case: the given metaclass isn't a class,[](#l2.70)
# so there is no metaclass calculation.[](#l2.71)
marker = object()[](#l2.72)
def func(*args, **kwargs):[](#l2.73)
return marker[](#l2.74)
class X(metaclass=func):[](#l2.75)
pass[](#l2.76)
class Y(object, metaclass=func):[](#l2.77)
pass[](#l2.78)
class Z(D, metaclass=func):[](#l2.79)
pass[](#l2.80)
self.assertIs(marker, X)[](#l2.81)
self.assertIs(marker, Y)[](#l2.82)
self.assertIs(marker, Z)[](#l2.83)
# The given metaclass is a class,[](#l2.85)
# but not a descendant of type.[](#l2.86)
prepare_calls = [] # to track __prepare__ calls[](#l2.87)
class ANotMeta:[](#l2.88)
def __new__(mcls, *args, **kwargs):[](#l2.89)
new_calls.append('ANotMeta')[](#l2.90)
return super().__new__(mcls)[](#l2.91)
@classmethod[](#l2.92)
def __prepare__(mcls, name, bases):[](#l2.93)
prepare_calls.append('ANotMeta')[](#l2.94)
return {}[](#l2.95)
class BNotMeta(ANotMeta):[](#l2.96)
def __new__(mcls, *args, **kwargs):[](#l2.97)
new_calls.append('BNotMeta')[](#l2.98)
return super().__new__(mcls)[](#l2.99)
@classmethod[](#l2.100)
def __prepare__(mcls, name, bases):[](#l2.101)
prepare_calls.append('BNotMeta')[](#l2.102)
return super().__prepare__(name, bases)[](#l2.103)
class A(metaclass=ANotMeta):[](#l2.105)
pass[](#l2.106)
self.assertIs(ANotMeta, type(A))[](#l2.107)
self.assertEqual(['ANotMeta'], prepare_calls)[](#l2.108)
prepare_calls.clear()[](#l2.109)
self.assertEqual(['ANotMeta'], new_calls)[](#l2.110)
new_calls.clear()[](#l2.111)
class B(metaclass=BNotMeta):[](#l2.113)
pass[](#l2.114)
self.assertIs(BNotMeta, type(B))[](#l2.115)
self.assertEqual(['BNotMeta', 'ANotMeta'], prepare_calls)[](#l2.116)
prepare_calls.clear()[](#l2.117)
self.assertEqual(['BNotMeta', 'ANotMeta'], new_calls)[](#l2.118)
new_calls.clear()[](#l2.119)
class C(A, B):[](#l2.121)
pass[](#l2.122)
self.assertIs(BNotMeta, type(C))[](#l2.123)
self.assertEqual(['BNotMeta', 'ANotMeta'], new_calls)[](#l2.124)
new_calls.clear()[](#l2.125)
self.assertEqual(['BNotMeta', 'ANotMeta'], prepare_calls)[](#l2.126)
prepare_calls.clear()[](#l2.127)
class C2(B, A):[](#l2.129)
pass[](#l2.130)
self.assertIs(BNotMeta, type(C2))[](#l2.131)
self.assertEqual(['BNotMeta', 'ANotMeta'], new_calls)[](#l2.132)
new_calls.clear()[](#l2.133)
self.assertEqual(['BNotMeta', 'ANotMeta'], prepare_calls)[](#l2.134)
prepare_calls.clear()[](#l2.135)
# This is a TypeError, because of a metaclass conflict:[](#l2.137)
# BNotMeta is neither a subclass, nor a superclass of type[](#l2.138)
with self.assertRaises(TypeError):[](#l2.139)
class D(C, metaclass=type):[](#l2.140)
pass[](#l2.141)
class E(C, metaclass=ANotMeta):[](#l2.143)
pass[](#l2.144)
self.assertIs(BNotMeta, type(E))[](#l2.145)
self.assertEqual(['BNotMeta', 'ANotMeta'], new_calls)[](#l2.146)
new_calls.clear()[](#l2.147)
self.assertEqual(['BNotMeta', 'ANotMeta'], prepare_calls)[](#l2.148)
prepare_calls.clear()[](#l2.149)
class F(object(), C):[](#l2.151)
pass[](#l2.152)
self.assertIs(BNotMeta, type(F))[](#l2.153)
self.assertEqual(['BNotMeta', 'ANotMeta'], new_calls)[](#l2.154)
new_calls.clear()[](#l2.155)
self.assertEqual(['BNotMeta', 'ANotMeta'], prepare_calls)[](#l2.156)
prepare_calls.clear()[](#l2.157)
class F2(C, object()):[](#l2.159)
pass[](#l2.160)
self.assertIs(BNotMeta, type(F2))[](#l2.161)
self.assertEqual(['BNotMeta', 'ANotMeta'], new_calls)[](#l2.162)
new_calls.clear()[](#l2.163)
self.assertEqual(['BNotMeta', 'ANotMeta'], prepare_calls)[](#l2.164)
prepare_calls.clear()[](#l2.165)
# TypeError: BNotMeta is neither a[](#l2.167)
# subclass, nor a superclass of int[](#l2.168)
with self.assertRaises(TypeError):[](#l2.169)
class X(C, int()):[](#l2.170)
pass[](#l2.171)
with self.assertRaises(TypeError):[](#l2.172)
class X(int(), C):[](#l2.173)
pass[](#l2.174)
def test_module_subclasses(self): # Testing Python subclass of module...
--- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,10 @@ What's New in Python 3.3 Alpha 1? Core and Builtins ----------------- +- Issue #1294232: In a few cases involving metaclass inheritance, the
- interpreter would sometimes invoke the wrong metaclass when building a new
- class object. These cases now behave correctly. Patch by Daniel Urban. +
- Issue #12753: Add support for Unicode name aliases and named sequences.
Both :func:
unicodedata.lookup()
and '\N{...}' now resolve aliases, and :func:unicodedata.lookup()
resolves named sequences too.
--- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -1915,6 +1915,42 @@ PyType_GetFlags(PyTypeObject type) return type->tp_flags; } +/ Determine the most derived metatype. */ +PyTypeObject * +_PyType_CalculateMetaclass(PyTypeObject *metatype, PyObject *bases) +{
- /* Determine the proper metatype to deal with this,
and check for metatype conflicts while we're at it.[](#l4.17)
Note that if some other metatype wins to contract,[](#l4.18)
it's possible that its instances are not types. */[](#l4.19)
- nbases = PyTuple_GET_SIZE(bases);
- winner = metatype;
- for (i = 0; i < nbases; i++) {
tmp = PyTuple_GET_ITEM(bases, i);[](#l4.24)
tmptype = Py_TYPE(tmp);[](#l4.25)
if (PyType_IsSubtype(winner, tmptype))[](#l4.26)
continue;[](#l4.27)
if (PyType_IsSubtype(tmptype, winner)) {[](#l4.28)
winner = tmptype;[](#l4.29)
continue;[](#l4.30)
}[](#l4.31)
/* else: */[](#l4.32)
PyErr_SetString(PyExc_TypeError,[](#l4.33)
"metaclass conflict: "[](#l4.34)
"the metaclass of a derived class "[](#l4.35)
"must be a (non-strict) subclass "[](#l4.36)
"of the metaclasses of all its bases");[](#l4.37)
return NULL;[](#l4.38)
- }
- return winner;
+} + static PyObject * type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) { @@ -1958,28 +1994,12 @@ type_new(PyTypeObject *metatype, PyObjec &PyDict_Type, &dict)) return NULL;
- /* Determine the proper metatype to deal with this,
and check for metatype conflicts while we're at it.[](#l4.51)
Note that if some other metatype wins to contract,[](#l4.52)
it's possible that its instances are not types. */[](#l4.53)
- nbases = PyTuple_GET_SIZE(bases);
- winner = metatype;
- for (i = 0; i < nbases; i++) {
tmp = PyTuple_GET_ITEM(bases, i);[](#l4.57)
tmptype = Py_TYPE(tmp);[](#l4.58)
if (PyType_IsSubtype(winner, tmptype))[](#l4.59)
continue;[](#l4.60)
if (PyType_IsSubtype(tmptype, winner)) {[](#l4.61)
winner = tmptype;[](#l4.62)
continue;[](#l4.63)
}[](#l4.64)
PyErr_SetString(PyExc_TypeError,[](#l4.65)
"metaclass conflict: "[](#l4.66)
"the metaclass of a derived class "[](#l4.67)
"must be a (non-strict) subclass "[](#l4.68)
"of the metaclasses of all its bases");[](#l4.69)
- /* Determine the proper metatype to deal with this: */
- winner = _PyType_CalculateMetaclass(metatype, bases);
- if (winner == NULL) { return NULL; } + if (winner != metatype) { if (winner->tp_new != type_new) /* Pass it to the winner */ return winner->tp_new(winner, args, kwds);
@@ -1987,6 +2007,7 @@ type_new(PyTypeObject metatype, PyObjec } / Adjust for empty tuple bases */
- nbases = PyTuple_GET_SIZE(bases); if (nbases == 0) { bases = PyTuple_Pack(1, &PyBaseObject_Type); if (bases == NULL)
--- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -38,9 +38,10 @@ int Py_HasFileSystemDefaultEncoding = 0; static PyObject * builtin___build_class__(PyObject *self, PyObject *args, PyObject *kwds) {
- Py_ssize_t nargs, nbases;
- int isclass; Py_IDENTIFIER(prepare); assert(args != NULL); @@ -85,17 +86,43 @@ builtin___build_class_(PyObject *self, Py_DECREF(bases); return NULL; }
/* metaclass is explicitly given, check if it's indeed a class */[](#l5.20)
} if (meta == NULL) {isclass = PyType_Check(meta);[](#l5.21) }[](#l5.22)
if (PyTuple_GET_SIZE(bases) == 0)[](#l5.25)
/* if there are no bases, use type: */[](#l5.26)
if (PyTuple_GET_SIZE(bases) == 0) {[](#l5.27) meta = (PyObject *) (&PyType_Type);[](#l5.28)
}[](#l5.29)
/* else get the type of the first base */[](#l5.30) else {[](#l5.31) PyObject *base0 = PyTuple_GET_ITEM(bases, 0);[](#l5.32) meta = (PyObject *) (base0->ob_type);[](#l5.33) }[](#l5.34) Py_INCREF(meta);[](#l5.35)
} +isclass = 1; /* meta is really a class */[](#l5.36)
- if (isclass) {
/* meta is really a class, so check for a more derived[](#l5.40)
metaclass, or possible metaclass conflicts: */[](#l5.41)
winner = (PyObject *)_PyType_CalculateMetaclass((PyTypeObject *)meta,[](#l5.42)
bases);[](#l5.43)
if (winner == NULL) {[](#l5.44)
Py_DECREF(meta);[](#l5.45)
Py_XDECREF(mkw);[](#l5.46)
Py_DECREF(bases);[](#l5.47)
return NULL;[](#l5.48)
}[](#l5.49)
if (winner != meta) {[](#l5.50)
Py_DECREF(meta);[](#l5.51)
meta = winner;[](#l5.52)
Py_INCREF(meta);[](#l5.53)
}[](#l5.54)
- }
- /* else: meta is not a class, so we cannot do the metaclass
prep = PyObject_GetAttrId(meta, &PyId___prepare_); if (prep == NULL) { if (PyErr_ExceptionMatches(PyExc_AttributeError)) {calculation, so we will use the explicitly given object as it is */[](#l5.57)