@@ -816,20 +816,93 @@ def wrapper(f):
816816 wraps = functools .wraps
817817
818818
819+ class _WithMetaclass (type ):
820+ """
821+ Metaclasses to be used in six.with_metaclass: instances of
822+ _WithMetaclass are metaclasses, so this is a metametaclass.
823+ """
824+ @classmethod
825+ def get (cls , meta , bases ):
826+ """
827+ Return a metaclass (an instance of _WithMetaclass) which, if an
828+ instance of it is used as base class, will create a class with
829+ metaclass "meta" and bases "bases".
830+ """
831+ # We use "meta" as base class instead of "type" to support the
832+ # following wrong usage:
833+ #
834+ # class X(six.with_metaclass(M)):
835+ # __metaclass__ = M
836+ #
837+ return cls ("metaclass" , (meta ,), dict (meta = meta , bases = bases ))
838+
839+ def instance (self ):
840+ """
841+ Return an instance of the metaclass "self".
842+ """
843+ return self .__new__ (self , "temporary_class" , (object ,), {})
844+
845+ @property
846+ def __prepare__ (self ):
847+ # We forward __prepare__ to __prepare which is the actual
848+ # implementation.
849+ #
850+ # This is a property for 2 reasons:
851+ #
852+ # First of all, if the metaclass does not define __prepare__, we
853+ # pretend that we don't have a __prepare__ method either.
854+ # PEP 3115 says that a __prepare__ method is not required to
855+ # exist. Also, some user code might call __prepare__ on Py2 and
856+ # we gracefully handle that.
857+ #
858+ # Second, this is a property for a technical reason: an ordinary
859+ # __prepare__ method on the metametaclass would not be called,
860+ # since it is overridden by type.__prepare__ (as an application
861+ # of the general principle that instance attributes override
862+ # type attributes). A property works because it bypasses
863+ # attribute lookup on the instance.
864+
865+ # Check for __prepare__ attribute, propagate AttributeError
866+ self .meta .__prepare__
867+ return self .__prepare
868+
869+ def __prepare (self , name , __bases , ** kwargs ):
870+ """
871+ Ensure that metaclass.__prepare__ is called with the correct
872+ arguments.
873+ """
874+ return self .meta .__prepare__ (name , self .bases , ** kwargs )
875+
876+ def __call__ (self , name , __bases , d ):
877+ """
878+ Create the eventual class with metaclass "self.meta" and bases
879+ "self.bases".
880+ """
881+ if "__metaclass__" in d :
882+ from warnings import warn
883+ warn ("when using six.with_metaclass, remove __metaclass__ from your class" , DeprecationWarning )
884+ return self .meta (name , self .bases , d )
885+
886+
819887def with_metaclass (meta , * bases ):
820888 """Create a base class with a metaclass."""
821- # This requires a bit of explanation: the basic idea is to make a dummy
822- # metaclass for one level of class instantiation that replaces itself with
823- # the actual metaclass.
824- class metaclass (type ):
825-
826- def __new__ (cls , name , this_bases , d ):
827- return meta (name , bases , d )
828-
829- @classmethod
830- def __prepare__ (cls , name , this_bases ):
831- return meta .__prepare__ (name , bases )
832- return type .__new__ (metaclass , 'temporary_class' , (), {})
889+ # This requires a bit of explanation: with_metaclass() returns
890+ # a temporary class which will setup the correct metaclass when
891+ # this temporary class is used as base class.
892+ #
893+ # In detail: let T = with_metaclass(meta, *bases). When the user
894+ # does "class X(with_metaclass(meta, *bases))", Python will first
895+ # determine the metaclass of X from its bases. In our case, there is
896+ # a single base class T. Therefore, the metaclass will be type(T).
897+ #
898+ # Next, Python will call type(T)("X", (T,), methods) and it is this
899+ # call that we want to override. So we need to define a __call__
900+ # method in the metaclass of type(T), which needs a metametaclass,
901+ # called "_WithMetaclass".
902+ # The metaclass type(T) is returned by _WithMetaclass.get(...) and
903+ # the instance() method creates an instance of this metaclass, which
904+ # is a regular class.
905+ return _WithMetaclass .get (meta , bases ).instance ()
833906
834907
835908def add_metaclass (metaclass ):
0 commit comments