diff -u -r -d cpython.838e3b07a7f8/Include/object.h cpython/Include/object.h --- cpython.838e3b07a7f8/Include/object.h 2011-04-04 11:02:50.000000000 +0200 +++ cpython/Include/object.h 2011-04-04 11:02:50.000000000 +0200 @@ -449,6 +449,7 @@ #ifndef Py_LIMITED_API PyAPI_FUNC(PyObject *) _PyType_Lookup(PyTypeObject *, PyObject *); PyAPI_FUNC(PyObject *) _PyObject_LookupSpecial(PyObject *, char *, PyObject **); +PyAPI_FUNC(PyTypeObject *) _PyType_CalculateWinner(PyTypeObject *, PyObject *); #endif PyAPI_FUNC(unsigned int) PyType_ClearCache(void); PyAPI_FUNC(void) PyType_Modified(PyTypeObject *); diff -u -r -d cpython.838e3b07a7f8/Lib/test/test_descr.py cpython/Lib/test/test_descr.py --- cpython.838e3b07a7f8/Lib/test/test_descr.py 2011-04-04 11:02:50.000000000 +0200 +++ cpython/Lib/test/test_descr.py 2011-04-04 11:02:50.000000000 +0200 @@ -625,6 +625,27 @@ # The most derived metaclass of D is A rather than type. class D(B, C): pass + self.assertIs(A, type(D)) + + # issue1294232: the __prepare__ of the most derived + # metaclass should be called + class A(type): + @classmethod + def __prepare__(mcls, name, bases): + return {'A_was_here': True} + + class B: + pass + + class C(metaclass=A): + pass + + # The most derived metaclass of D is A + class D(B, C): + pass + + # A.__prepare__ should've been called: + self.assertIn('A_was_here', D.__dict__) def test_module_subclasses(self): # Testing Python subclass of module... diff -u -r -d cpython.838e3b07a7f8/Objects/typeobject.c cpython/Objects/typeobject.c --- cpython.838e3b07a7f8/Objects/typeobject.c 2011-04-04 11:02:50.000000000 +0200 +++ cpython/Objects/typeobject.c 2011-04-04 11:02:50.000000000 +0200 @@ -1910,6 +1910,42 @@ return type->tp_flags; } +/* Determine the most derived metatype. */ +PyTypeObject * +_PyType_CalculateWinner(PyTypeObject *metatype, PyObject *bases) +{ + Py_ssize_t i, nbases; + PyTypeObject *winner; + PyObject *tmp; + PyTypeObject *tmptype; + + /* Determine the proper metatype to deal with this, + and check for metatype conflicts while we're at it. + Note that if some other metatype wins to contract, + it's possible that its instances are not types. */ + + nbases = PyTuple_GET_SIZE(bases); + winner = metatype; + for (i = 0; i < nbases; i++) { + tmp = PyTuple_GET_ITEM(bases, i); + tmptype = Py_TYPE(tmp); + if (PyType_IsSubtype(winner, tmptype)) + continue; + if (PyType_IsSubtype(tmptype, winner)) { + winner = tmptype; + continue; + } + /* else: */ + PyErr_SetString(PyExc_TypeError, + "metaclass conflict: " + "the metaclass of a derived class " + "must be a (non-strict) subclass " + "of the metaclasses of all its bases"); + return NULL; + } + return winner; +} + static PyObject * type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) { @@ -1953,28 +1989,12 @@ &PyDict_Type, &dict)) return NULL; - /* Determine the proper metatype to deal with this, - and check for metatype conflicts while we're at it. - Note that if some other metatype wins to contract, - it's possible that its instances are not types. */ - nbases = PyTuple_GET_SIZE(bases); - winner = metatype; - for (i = 0; i < nbases; i++) { - tmp = PyTuple_GET_ITEM(bases, i); - tmptype = Py_TYPE(tmp); - if (PyType_IsSubtype(winner, tmptype)) - continue; - if (PyType_IsSubtype(tmptype, winner)) { - winner = tmptype; - continue; - } - PyErr_SetString(PyExc_TypeError, - "metaclass conflict: " - "the metaclass of a derived class " - "must be a (non-strict) subclass " - "of the metaclasses of all its bases"); + /* Determine the proper metatype to deal with this: */ + winner = _PyType_CalculateWinner(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); @@ -1982,6 +2002,7 @@ } /* Adjust for empty tuple bases */ + nbases = PyTuple_GET_SIZE(bases); if (nbases == 0) { bases = PyTuple_Pack(1, &PyBaseObject_Type); if (bases == NULL) diff -u -r -d cpython.838e3b07a7f8/Python/bltinmodule.c cpython/Python/bltinmodule.c --- cpython.838e3b07a7f8/Python/bltinmodule.c 2011-04-04 11:02:50.000000000 +0200 +++ cpython/Python/bltinmodule.c 2011-04-04 11:02:50.000000000 +0200 @@ -87,8 +87,12 @@ if (PyTuple_GET_SIZE(bases) == 0) meta = (PyObject *) (&PyType_Type); else { - PyObject *base0 = PyTuple_GET_ITEM(bases, 0); - meta = (PyObject *) (base0->ob_type); + meta = (PyObject *)_PyType_CalculateWinner(&PyType_Type, bases); + if (meta == NULL) { + Py_XDECREF(mkw); + Py_DECREF(bases); + return NULL; + } } Py_INCREF(meta); }