Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 9715d26

Browse files
committed
Merge issue 1294232 patch from 3.2
2 parents a0e0e23 + de31b19 commit 9715d26

5 files changed

Lines changed: 244 additions & 23 deletions

File tree

Include/object.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,7 @@ PyAPI_FUNC(PyObject *) PyType_GenericNew(PyTypeObject *,
449449
#ifndef Py_LIMITED_API
450450
PyAPI_FUNC(PyObject *) _PyType_Lookup(PyTypeObject *, PyObject *);
451451
PyAPI_FUNC(PyObject *) _PyObject_LookupSpecial(PyObject *, char *, PyObject **);
452+
PyAPI_FUNC(PyTypeObject *) _PyType_CalculateMetaclass(PyTypeObject *, PyObject *);
452453
#endif
453454
PyAPI_FUNC(unsigned int) PyType_ClearCache(void);
454455
PyAPI_FUNC(void) PyType_Modified(PyTypeObject *);

Lib/test/test_descr.py

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -625,6 +625,174 @@ class C(object, metaclass=A):
625625
# The most derived metaclass of D is A rather than type.
626626
class D(B, C):
627627
pass
628+
self.assertIs(A, type(D))
629+
630+
# issue1294232: correct metaclass calculation
631+
new_calls = [] # to check the order of __new__ calls
632+
class AMeta(type):
633+
@staticmethod
634+
def __new__(mcls, name, bases, ns):
635+
new_calls.append('AMeta')
636+
return super().__new__(mcls, name, bases, ns)
637+
@classmethod
638+
def __prepare__(mcls, name, bases):
639+
return {}
640+
641+
class BMeta(AMeta):
642+
@staticmethod
643+
def __new__(mcls, name, bases, ns):
644+
new_calls.append('BMeta')
645+
return super().__new__(mcls, name, bases, ns)
646+
@classmethod
647+
def __prepare__(mcls, name, bases):
648+
ns = super().__prepare__(name, bases)
649+
ns['BMeta_was_here'] = True
650+
return ns
651+
652+
class A(metaclass=AMeta):
653+
pass
654+
self.assertEqual(['AMeta'], new_calls)
655+
new_calls.clear()
656+
657+
class B(metaclass=BMeta):
658+
pass
659+
# BMeta.__new__ calls AMeta.__new__ with super:
660+
self.assertEqual(['BMeta', 'AMeta'], new_calls)
661+
new_calls.clear()
662+
663+
class C(A, B):
664+
pass
665+
# The most derived metaclass is BMeta:
666+
self.assertEqual(['BMeta', 'AMeta'], new_calls)
667+
new_calls.clear()
668+
# BMeta.__prepare__ should've been called:
669+
self.assertIn('BMeta_was_here', C.__dict__)
670+
671+
# The order of the bases shouldn't matter:
672+
class C2(B, A):
673+
pass
674+
self.assertEqual(['BMeta', 'AMeta'], new_calls)
675+
new_calls.clear()
676+
self.assertIn('BMeta_was_here', C2.__dict__)
677+
678+
# Check correct metaclass calculation when a metaclass is declared:
679+
class D(C, metaclass=type):
680+
pass
681+
self.assertEqual(['BMeta', 'AMeta'], new_calls)
682+
new_calls.clear()
683+
self.assertIn('BMeta_was_here', D.__dict__)
684+
685+
class E(C, metaclass=AMeta):
686+
pass
687+
self.assertEqual(['BMeta', 'AMeta'], new_calls)
688+
new_calls.clear()
689+
self.assertIn('BMeta_was_here', E.__dict__)
690+
691+
# Special case: the given metaclass isn't a class,
692+
# so there is no metaclass calculation.
693+
marker = object()
694+
def func(*args, **kwargs):
695+
return marker
696+
class X(metaclass=func):
697+
pass
698+
class Y(object, metaclass=func):
699+
pass
700+
class Z(D, metaclass=func):
701+
pass
702+
self.assertIs(marker, X)
703+
self.assertIs(marker, Y)
704+
self.assertIs(marker, Z)
705+
706+
# The given metaclass is a class,
707+
# but not a descendant of type.
708+
prepare_calls = [] # to track __prepare__ calls
709+
class ANotMeta:
710+
def __new__(mcls, *args, **kwargs):
711+
new_calls.append('ANotMeta')
712+
return super().__new__(mcls)
713+
@classmethod
714+
def __prepare__(mcls, name, bases):
715+
prepare_calls.append('ANotMeta')
716+
return {}
717+
class BNotMeta(ANotMeta):
718+
def __new__(mcls, *args, **kwargs):
719+
new_calls.append('BNotMeta')
720+
return super().__new__(mcls)
721+
@classmethod
722+
def __prepare__(mcls, name, bases):
723+
prepare_calls.append('BNotMeta')
724+
return super().__prepare__(name, bases)
725+
726+
class A(metaclass=ANotMeta):
727+
pass
728+
self.assertIs(ANotMeta, type(A))
729+
self.assertEqual(['ANotMeta'], prepare_calls)
730+
prepare_calls.clear()
731+
self.assertEqual(['ANotMeta'], new_calls)
732+
new_calls.clear()
733+
734+
class B(metaclass=BNotMeta):
735+
pass
736+
self.assertIs(BNotMeta, type(B))
737+
self.assertEqual(['BNotMeta', 'ANotMeta'], prepare_calls)
738+
prepare_calls.clear()
739+
self.assertEqual(['BNotMeta', 'ANotMeta'], new_calls)
740+
new_calls.clear()
741+
742+
class C(A, B):
743+
pass
744+
self.assertIs(BNotMeta, type(C))
745+
self.assertEqual(['BNotMeta', 'ANotMeta'], new_calls)
746+
new_calls.clear()
747+
self.assertEqual(['BNotMeta', 'ANotMeta'], prepare_calls)
748+
prepare_calls.clear()
749+
750+
class C2(B, A):
751+
pass
752+
self.assertIs(BNotMeta, type(C2))
753+
self.assertEqual(['BNotMeta', 'ANotMeta'], new_calls)
754+
new_calls.clear()
755+
self.assertEqual(['BNotMeta', 'ANotMeta'], prepare_calls)
756+
prepare_calls.clear()
757+
758+
# This is a TypeError, because of a metaclass conflict:
759+
# BNotMeta is neither a subclass, nor a superclass of type
760+
with self.assertRaises(TypeError):
761+
class D(C, metaclass=type):
762+
pass
763+
764+
class E(C, metaclass=ANotMeta):
765+
pass
766+
self.assertIs(BNotMeta, type(E))
767+
self.assertEqual(['BNotMeta', 'ANotMeta'], new_calls)
768+
new_calls.clear()
769+
self.assertEqual(['BNotMeta', 'ANotMeta'], prepare_calls)
770+
prepare_calls.clear()
771+
772+
class F(object(), C):
773+
pass
774+
self.assertIs(BNotMeta, type(F))
775+
self.assertEqual(['BNotMeta', 'ANotMeta'], new_calls)
776+
new_calls.clear()
777+
self.assertEqual(['BNotMeta', 'ANotMeta'], prepare_calls)
778+
prepare_calls.clear()
779+
780+
class F2(C, object()):
781+
pass
782+
self.assertIs(BNotMeta, type(F2))
783+
self.assertEqual(['BNotMeta', 'ANotMeta'], new_calls)
784+
new_calls.clear()
785+
self.assertEqual(['BNotMeta', 'ANotMeta'], prepare_calls)
786+
prepare_calls.clear()
787+
788+
# TypeError: BNotMeta is neither a
789+
# subclass, nor a superclass of int
790+
with self.assertRaises(TypeError):
791+
class X(C, int()):
792+
pass
793+
with self.assertRaises(TypeError):
794+
class X(int(), C):
795+
pass
628796

629797
def test_module_subclasses(self):
630798
# Testing Python subclass of module...

Misc/NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ What's New in Python 3.3 Alpha 1?
1010
Core and Builtins
1111
-----------------
1212

13+
- Issue #1294232: In a few cases involving metaclass inheritance, the
14+
interpreter would sometimes invoke the wrong metaclass when building a new
15+
class object. These cases now behave correctly. Patch by Daniel Urban.
16+
1317
- Issue #12753: Add support for Unicode name aliases and named sequences.
1418
Both :func:`unicodedata.lookup()` and '\N{...}' now resolve aliases,
1519
and :func:`unicodedata.lookup()` resolves named sequences too.

Objects/typeobject.c

Lines changed: 41 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1915,6 +1915,42 @@ PyType_GetFlags(PyTypeObject *type)
19151915
return type->tp_flags;
19161916
}
19171917

1918+
/* Determine the most derived metatype. */
1919+
PyTypeObject *
1920+
_PyType_CalculateMetaclass(PyTypeObject *metatype, PyObject *bases)
1921+
{
1922+
Py_ssize_t i, nbases;
1923+
PyTypeObject *winner;
1924+
PyObject *tmp;
1925+
PyTypeObject *tmptype;
1926+
1927+
/* Determine the proper metatype to deal with this,
1928+
and check for metatype conflicts while we're at it.
1929+
Note that if some other metatype wins to contract,
1930+
it's possible that its instances are not types. */
1931+
1932+
nbases = PyTuple_GET_SIZE(bases);
1933+
winner = metatype;
1934+
for (i = 0; i < nbases; i++) {
1935+
tmp = PyTuple_GET_ITEM(bases, i);
1936+
tmptype = Py_TYPE(tmp);
1937+
if (PyType_IsSubtype(winner, tmptype))
1938+
continue;
1939+
if (PyType_IsSubtype(tmptype, winner)) {
1940+
winner = tmptype;
1941+
continue;
1942+
}
1943+
/* else: */
1944+
PyErr_SetString(PyExc_TypeError,
1945+
"metaclass conflict: "
1946+
"the metaclass of a derived class "
1947+
"must be a (non-strict) subclass "
1948+
"of the metaclasses of all its bases");
1949+
return NULL;
1950+
}
1951+
return winner;
1952+
}
1953+
19181954
static PyObject *
19191955
type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
19201956
{
@@ -1958,35 +1994,20 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
19581994
&PyDict_Type, &dict))
19591995
return NULL;
19601996

1961-
/* Determine the proper metatype to deal with this,
1962-
and check for metatype conflicts while we're at it.
1963-
Note that if some other metatype wins to contract,
1964-
it's possible that its instances are not types. */
1965-
nbases = PyTuple_GET_SIZE(bases);
1966-
winner = metatype;
1967-
for (i = 0; i < nbases; i++) {
1968-
tmp = PyTuple_GET_ITEM(bases, i);
1969-
tmptype = Py_TYPE(tmp);
1970-
if (PyType_IsSubtype(winner, tmptype))
1971-
continue;
1972-
if (PyType_IsSubtype(tmptype, winner)) {
1973-
winner = tmptype;
1974-
continue;
1975-
}
1976-
PyErr_SetString(PyExc_TypeError,
1977-
"metaclass conflict: "
1978-
"the metaclass of a derived class "
1979-
"must be a (non-strict) subclass "
1980-
"of the metaclasses of all its bases");
1997+
/* Determine the proper metatype to deal with this: */
1998+
winner = _PyType_CalculateMetaclass(metatype, bases);
1999+
if (winner == NULL) {
19812000
return NULL;
19822001
}
2002+
19832003
if (winner != metatype) {
19842004
if (winner->tp_new != type_new) /* Pass it to the winner */
19852005
return winner->tp_new(winner, args, kwds);
19862006
metatype = winner;
19872007
}
19882008

19892009
/* Adjust for empty tuple bases */
2010+
nbases = PyTuple_GET_SIZE(bases);
19902011
if (nbases == 0) {
19912012
bases = PyTuple_Pack(1, &PyBaseObject_Type);
19922013
if (bases == NULL)

Python/bltinmodule.c

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,10 @@ _Py_IDENTIFIER(flush);
3838
static PyObject *
3939
builtin___build_class__(PyObject *self, PyObject *args, PyObject *kwds)
4040
{
41-
PyObject *func, *name, *bases, *mkw, *meta, *prep, *ns, *cell;
41+
PyObject *func, *name, *bases, *mkw, *meta, *winner, *prep, *ns, *cell;
4242
PyObject *cls = NULL;
43-
Py_ssize_t nargs;
43+
Py_ssize_t nargs, nbases;
44+
int isclass;
4445
_Py_IDENTIFIER(__prepare__);
4546

4647
assert(args != NULL);
@@ -85,17 +86,43 @@ builtin___build_class__(PyObject *self, PyObject *args, PyObject *kwds)
8586
Py_DECREF(bases);
8687
return NULL;
8788
}
89+
/* metaclass is explicitly given, check if it's indeed a class */
90+
isclass = PyType_Check(meta);
8891
}
8992
}
9093
if (meta == NULL) {
91-
if (PyTuple_GET_SIZE(bases) == 0)
94+
/* if there are no bases, use type: */
95+
if (PyTuple_GET_SIZE(bases) == 0) {
9296
meta = (PyObject *) (&PyType_Type);
97+
}
98+
/* else get the type of the first base */
9399
else {
94100
PyObject *base0 = PyTuple_GET_ITEM(bases, 0);
95101
meta = (PyObject *) (base0->ob_type);
96102
}
97103
Py_INCREF(meta);
104+
isclass = 1; /* meta is really a class */
105+
}
106+
107+
if (isclass) {
108+
/* meta is really a class, so check for a more derived
109+
metaclass, or possible metaclass conflicts: */
110+
winner = (PyObject *)_PyType_CalculateMetaclass((PyTypeObject *)meta,
111+
bases);
112+
if (winner == NULL) {
113+
Py_DECREF(meta);
114+
Py_XDECREF(mkw);
115+
Py_DECREF(bases);
116+
return NULL;
117+
}
118+
if (winner != meta) {
119+
Py_DECREF(meta);
120+
meta = winner;
121+
Py_INCREF(meta);
122+
}
98123
}
124+
/* else: meta is not a class, so we cannot do the metaclass
125+
calculation, so we will use the explicitly given object as it is */
99126
prep = _PyObject_GetAttrId(meta, &PyId___prepare__);
100127
if (prep == NULL) {
101128
if (PyErr_ExceptionMatches(PyExc_AttributeError)) {

0 commit comments

Comments
 (0)