mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	Merge issue 1294232 patch from 3.2
This commit is contained in:
		
						commit
						9715d26305
					
				
					 5 changed files with 244 additions and 23 deletions
				
			
		|  | @ -449,6 +449,7 @@ PyAPI_FUNC(PyObject *) PyType_GenericNew(PyTypeObject *, | |||
| #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 *); | ||||
|  |  | |||
|  | @ -625,6 +625,174 @@ class C(object, metaclass=A): | |||
|         # 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.clear() | ||||
| 
 | ||||
|         class B(metaclass=BMeta): | ||||
|             pass | ||||
|         # BMeta.__new__ calls AMeta.__new__ with super: | ||||
|         self.assertEqual(['BMeta', 'AMeta'], new_calls) | ||||
|         new_calls.clear() | ||||
| 
 | ||||
|         class C(A, B): | ||||
|             pass | ||||
|         # The most derived metaclass is BMeta: | ||||
|         self.assertEqual(['BMeta', 'AMeta'], 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.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.clear() | ||||
|         self.assertIn('BMeta_was_here', D.__dict__) | ||||
| 
 | ||||
|         class E(C, metaclass=AMeta): | ||||
|             pass | ||||
|         self.assertEqual(['BMeta', 'AMeta'], 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.clear() | ||||
|         self.assertEqual(['ANotMeta'], new_calls) | ||||
|         new_calls.clear() | ||||
| 
 | ||||
|         class B(metaclass=BNotMeta): | ||||
|             pass | ||||
|         self.assertIs(BNotMeta, type(B)) | ||||
|         self.assertEqual(['BNotMeta', 'ANotMeta'], prepare_calls) | ||||
|         prepare_calls.clear() | ||||
|         self.assertEqual(['BNotMeta', 'ANotMeta'], new_calls) | ||||
|         new_calls.clear() | ||||
| 
 | ||||
|         class C(A, B): | ||||
|             pass | ||||
|         self.assertIs(BNotMeta, type(C)) | ||||
|         self.assertEqual(['BNotMeta', 'ANotMeta'], new_calls) | ||||
|         new_calls.clear() | ||||
|         self.assertEqual(['BNotMeta', 'ANotMeta'], prepare_calls) | ||||
|         prepare_calls.clear() | ||||
| 
 | ||||
|         class C2(B, A): | ||||
|             pass | ||||
|         self.assertIs(BNotMeta, type(C2)) | ||||
|         self.assertEqual(['BNotMeta', 'ANotMeta'], new_calls) | ||||
|         new_calls.clear() | ||||
|         self.assertEqual(['BNotMeta', 'ANotMeta'], 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.clear() | ||||
|         self.assertEqual(['BNotMeta', 'ANotMeta'], prepare_calls) | ||||
|         prepare_calls.clear() | ||||
| 
 | ||||
|         class F(object(), C): | ||||
|             pass | ||||
|         self.assertIs(BNotMeta, type(F)) | ||||
|         self.assertEqual(['BNotMeta', 'ANotMeta'], new_calls) | ||||
|         new_calls.clear() | ||||
|         self.assertEqual(['BNotMeta', 'ANotMeta'], prepare_calls) | ||||
|         prepare_calls.clear() | ||||
| 
 | ||||
|         class F2(C, object()): | ||||
|             pass | ||||
|         self.assertIs(BNotMeta, type(F2)) | ||||
|         self.assertEqual(['BNotMeta', 'ANotMeta'], new_calls) | ||||
|         new_calls.clear() | ||||
|         self.assertEqual(['BNotMeta', 'ANotMeta'], 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... | ||||
|  |  | |||
|  | @ -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. | ||||
|  |  | |||
|  | @ -1915,6 +1915,42 @@ PyType_GetFlags(PyTypeObject *type) | |||
|     return type->tp_flags; | ||||
| } | ||||
| 
 | ||||
| /* Determine the most derived metatype. */ | ||||
| PyTypeObject * | ||||
| _PyType_CalculateMetaclass(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) | ||||
| { | ||||
|  | @ -1958,28 +1994,12 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) | |||
|                                      &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_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, PyObject *args, PyObject *kwds) | |||
|     } | ||||
| 
 | ||||
|     /* Adjust for empty tuple bases */ | ||||
|     nbases = PyTuple_GET_SIZE(bases); | ||||
|     if (nbases == 0) { | ||||
|         bases = PyTuple_Pack(1, &PyBaseObject_Type); | ||||
|         if (bases == NULL) | ||||
|  |  | |||
|  | @ -38,9 +38,10 @@ _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); | ||||
|  | @ -85,17 +86,43 @@ builtin___build_class__(PyObject *self, PyObject *args, PyObject *kwds) | |||
|                 Py_DECREF(bases); | ||||
|                 return NULL; | ||||
|             } | ||||
|             /* metaclass is explicitly given, check if it's indeed a class */ | ||||
|             isclass = PyType_Check(meta); | ||||
|         } | ||||
|     } | ||||
|     if (meta == NULL) { | ||||
|         if (PyTuple_GET_SIZE(bases) == 0) | ||||
|         /* if there are no bases, use type: */ | ||||
|         if (PyTuple_GET_SIZE(bases) == 0) { | ||||
|             meta = (PyObject *) (&PyType_Type); | ||||
|         } | ||||
|         /* else get the type of the first base */ | ||||
|         else { | ||||
|             PyObject *base0 = PyTuple_GET_ITEM(bases, 0); | ||||
|             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_GetAttrId(meta, &PyId___prepare__); | ||||
|     if (prep == NULL) { | ||||
|         if (PyErr_ExceptionMatches(PyExc_AttributeError)) { | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Nick Coghlan
						Nick Coghlan