From: Nick Coghlan Date: Sun, 23 Oct 2011 12:36:42 +0000 (+1000) Subject: Merge issue 1294232 patch from 3.2 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=9715d26305f7cf6c4db91f3670f1b7e99f04dd2d;p=python Merge issue 1294232 patch from 3.2 --- 9715d26305f7cf6c4db91f3670f1b7e99f04dd2d diff --cc Lib/test/test_descr.py index 2a9f88083b,b214996aa9..15219db570 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@@ -625,6 -625,174 +625,174 @@@ class ClassPropertiesAndMethods(unittes # The most derived metaclass of D is A rather than type. class D(B, C): pass + self.assertIs(A, type(D)) + + # issue1294232: correct metaclass calculation + new_calls = [] # to check the order of __new__ calls + class AMeta(type): + @staticmethod + def __new__(mcls, name, bases, ns): + new_calls.append('AMeta') + return super().__new__(mcls, name, bases, ns) + @classmethod + def __prepare__(mcls, name, bases): + return {} + + class BMeta(AMeta): + @staticmethod + def __new__(mcls, name, bases, ns): + new_calls.append('BMeta') + return super().__new__(mcls, name, bases, ns) + @classmethod + def __prepare__(mcls, name, bases): + ns = super().__prepare__(name, bases) + ns['BMeta_was_here'] = True + return ns + + class A(metaclass=AMeta): + pass + self.assertEqual(['AMeta'], new_calls) - new_calls[:] = [] ++ new_calls.clear() + + class B(metaclass=BMeta): + pass + # BMeta.__new__ calls AMeta.__new__ with super: + self.assertEqual(['BMeta', 'AMeta'], new_calls) - new_calls[:] = [] ++ new_calls.clear() + + class C(A, B): + pass + # The most derived metaclass is BMeta: + self.assertEqual(['BMeta', 'AMeta'], new_calls) - new_calls[:] = [] ++ new_calls.clear() + # BMeta.__prepare__ should've been called: + self.assertIn('BMeta_was_here', C.__dict__) + + # The order of the bases shouldn't matter: + class C2(B, A): + pass + self.assertEqual(['BMeta', 'AMeta'], new_calls) - new_calls[:] = [] ++ new_calls.clear() + self.assertIn('BMeta_was_here', C2.__dict__) + + # Check correct metaclass calculation when a metaclass is declared: + class D(C, metaclass=type): + pass + self.assertEqual(['BMeta', 'AMeta'], new_calls) - new_calls[:] = [] ++ new_calls.clear() + self.assertIn('BMeta_was_here', D.__dict__) + + class E(C, metaclass=AMeta): + pass + self.assertEqual(['BMeta', 'AMeta'], new_calls) - new_calls[:] = [] ++ new_calls.clear() + self.assertIn('BMeta_was_here', E.__dict__) + + # Special case: the given metaclass isn't a class, + # so there is no metaclass calculation. + marker = object() + def func(*args, **kwargs): + return marker + class X(metaclass=func): + pass + class Y(object, metaclass=func): + pass + class Z(D, metaclass=func): + pass + self.assertIs(marker, X) + self.assertIs(marker, Y) + self.assertIs(marker, Z) + + # The given metaclass is a class, + # but not a descendant of type. + prepare_calls = [] # to track __prepare__ calls + class ANotMeta: + def __new__(mcls, *args, **kwargs): + new_calls.append('ANotMeta') + return super().__new__(mcls) + @classmethod + def __prepare__(mcls, name, bases): + prepare_calls.append('ANotMeta') + return {} + class BNotMeta(ANotMeta): + def __new__(mcls, *args, **kwargs): + new_calls.append('BNotMeta') + return super().__new__(mcls) + @classmethod + def __prepare__(mcls, name, bases): + prepare_calls.append('BNotMeta') + return super().__prepare__(name, bases) + + class A(metaclass=ANotMeta): + pass + self.assertIs(ANotMeta, type(A)) + self.assertEqual(['ANotMeta'], prepare_calls) - prepare_calls[:] = [] ++ prepare_calls.clear() + self.assertEqual(['ANotMeta'], new_calls) - new_calls[:] = [] ++ new_calls.clear() + + class B(metaclass=BNotMeta): + pass + self.assertIs(BNotMeta, type(B)) + self.assertEqual(['BNotMeta', 'ANotMeta'], prepare_calls) - prepare_calls[:] = [] ++ prepare_calls.clear() + self.assertEqual(['BNotMeta', 'ANotMeta'], new_calls) - new_calls[:] = [] ++ new_calls.clear() + + class C(A, B): + pass + self.assertIs(BNotMeta, type(C)) + self.assertEqual(['BNotMeta', 'ANotMeta'], new_calls) - new_calls[:] = [] ++ new_calls.clear() + self.assertEqual(['BNotMeta', 'ANotMeta'], prepare_calls) - prepare_calls[:] = [] ++ prepare_calls.clear() + + class C2(B, A): + pass + self.assertIs(BNotMeta, type(C2)) + self.assertEqual(['BNotMeta', 'ANotMeta'], new_calls) - new_calls[:] = [] ++ new_calls.clear() + self.assertEqual(['BNotMeta', 'ANotMeta'], prepare_calls) - prepare_calls[:] = [] ++ prepare_calls.clear() + + # This is a TypeError, because of a metaclass conflict: + # BNotMeta is neither a subclass, nor a superclass of type + with self.assertRaises(TypeError): + class D(C, metaclass=type): + pass + + class E(C, metaclass=ANotMeta): + pass + self.assertIs(BNotMeta, type(E)) + self.assertEqual(['BNotMeta', 'ANotMeta'], new_calls) - new_calls[:] = [] ++ new_calls.clear() + self.assertEqual(['BNotMeta', 'ANotMeta'], prepare_calls) - prepare_calls[:] = [] ++ prepare_calls.clear() + + class F(object(), C): + pass + self.assertIs(BNotMeta, type(F)) + self.assertEqual(['BNotMeta', 'ANotMeta'], new_calls) - new_calls[:] = [] ++ new_calls.clear() + self.assertEqual(['BNotMeta', 'ANotMeta'], prepare_calls) - prepare_calls[:] = [] ++ prepare_calls.clear() + + class F2(C, object()): + pass + self.assertIs(BNotMeta, type(F2)) + self.assertEqual(['BNotMeta', 'ANotMeta'], new_calls) - new_calls[:] = [] ++ new_calls.clear() + self.assertEqual(['BNotMeta', 'ANotMeta'], prepare_calls) - prepare_calls[:] = [] ++ prepare_calls.clear() + + # TypeError: BNotMeta is neither a + # subclass, nor a superclass of int + with self.assertRaises(TypeError): + class X(C, int()): + pass + with self.assertRaises(TypeError): + class X(int(), C): + pass def test_module_subclasses(self): # Testing Python subclass of module... diff --cc Misc/NEWS index b9ff2fa52e,c078623d3b..8e6eff7903 --- a/Misc/NEWS +++ b/Misc/NEWS @@@ -10,14 -10,10 +10,18 @@@ 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. + +- Issue #12170: The count(), find(), rfind(), index() and rindex() methods + of bytes and bytearray objects now accept an integer between 0 and 255 + as their first argument. Patch by Petri Lehtinen. + - Issue #12604: VTRACE macro expanded to no-op in _sre.c to avoid compiler warnings. Patch by Josh Triplett and Petri Lehtinen. diff --cc Python/bltinmodule.c index e68f02500f,9c50b88da8..fd242b7408 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@@ -38,10 -35,10 +38,11 @@@ _Py_IDENTIFIER(flush) static PyObject * builtin___build_class__(PyObject *self, PyObject *args, PyObject *kwds) { - PyObject *func, *name, *bases, *mkw, *meta, *prep, *ns, *cell; + PyObject *func, *name, *bases, *mkw, *meta, *winner, *prep, *ns, *cell; PyObject *cls = NULL; - Py_ssize_t nargs; + Py_ssize_t nargs, nbases; + int isclass; + _Py_IDENTIFIER(__prepare__); assert(args != NULL); if (!PyTuple_Check(args)) { @@@ -95,8 -98,28 +101,29 @@@ meta = (PyObject *) (base0->ob_type); } Py_INCREF(meta); + isclass = 1; /* meta is really a class */ + } ++ + if (isclass) { + /* meta is really a class, so check for a more derived + metaclass, or possible metaclass conflicts: */ + winner = (PyObject *)_PyType_CalculateMetaclass((PyTypeObject *)meta, + bases); + if (winner == NULL) { + Py_DECREF(meta); + Py_XDECREF(mkw); + Py_DECREF(bases); + return NULL; + } + if (winner != meta) { + Py_DECREF(meta); + meta = winner; + Py_INCREF(meta); + } } + /* else: meta is not a class, so we cannot do the metaclass + calculation, so we will use the explicitly given object as it is */ - prep = PyObject_GetAttrString(meta, "__prepare__"); + prep = _PyObject_GetAttrId(meta, &PyId___prepare__); if (prep == NULL) { if (PyErr_ExceptionMatches(PyExc_AttributeError)) { PyErr_Clear();