@@ -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 [:] = []
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 [:] = []
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 [:] = []
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 [:] = []
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 [:] = []
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 [:] = []
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 [:] = []
731+ self .assertEqual (['ANotMeta' ], new_calls )
732+ new_calls [:] = []
733+
734+ class B (metaclass = BNotMeta ):
735+ pass
736+ self .assertIs (BNotMeta , type (B ))
737+ self .assertEqual (['BNotMeta' , 'ANotMeta' ], prepare_calls )
738+ prepare_calls [:] = []
739+ self .assertEqual (['BNotMeta' , 'ANotMeta' ], new_calls )
740+ new_calls [:] = []
741+
742+ class C (A , B ):
743+ pass
744+ self .assertIs (BNotMeta , type (C ))
745+ self .assertEqual (['BNotMeta' , 'ANotMeta' ], new_calls )
746+ new_calls [:] = []
747+ self .assertEqual (['BNotMeta' , 'ANotMeta' ], prepare_calls )
748+ prepare_calls [:] = []
749+
750+ class C2 (B , A ):
751+ pass
752+ self .assertIs (BNotMeta , type (C2 ))
753+ self .assertEqual (['BNotMeta' , 'ANotMeta' ], new_calls )
754+ new_calls [:] = []
755+ self .assertEqual (['BNotMeta' , 'ANotMeta' ], prepare_calls )
756+ prepare_calls [:] = []
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 [:] = []
769+ self .assertEqual (['BNotMeta' , 'ANotMeta' ], prepare_calls )
770+ prepare_calls [:] = []
771+
772+ class F (object (), C ):
773+ pass
774+ self .assertIs (BNotMeta , type (F ))
775+ self .assertEqual (['BNotMeta' , 'ANotMeta' ], new_calls )
776+ new_calls [:] = []
777+ self .assertEqual (['BNotMeta' , 'ANotMeta' ], prepare_calls )
778+ prepare_calls [:] = []
779+
780+ class F2 (C , object ()):
781+ pass
782+ self .assertIs (BNotMeta , type (F2 ))
783+ self .assertEqual (['BNotMeta' , 'ANotMeta' ], new_calls )
784+ new_calls [:] = []
785+ self .assertEqual (['BNotMeta' , 'ANotMeta' ], prepare_calls )
786+ prepare_calls [:] = []
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...
0 commit comments