266 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			266 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| doctests = """
 | |
| 
 | |
| Basic class construction.
 | |
| 
 | |
|     >>> class C:
 | |
|     ...     def meth(self): print("Hello")
 | |
|     ...
 | |
|     >>> C.__class__ is type
 | |
|     True
 | |
|     >>> a = C()
 | |
|     >>> a.__class__ is C
 | |
|     True
 | |
|     >>> a.meth()
 | |
|     Hello
 | |
|     >>>
 | |
| 
 | |
| Use *args notation for the bases.
 | |
| 
 | |
|     >>> class A: pass
 | |
|     >>> class B: pass
 | |
|     >>> bases = (A, B)
 | |
|     >>> class C(*bases): pass
 | |
|     >>> C.__bases__ == bases
 | |
|     True
 | |
|     >>>
 | |
| 
 | |
| Use a trivial metaclass.
 | |
| 
 | |
|     >>> class M(type):
 | |
|     ...     pass
 | |
|     ...
 | |
|     >>> class C(metaclass=M):
 | |
|     ...    def meth(self): print("Hello")
 | |
|     ...
 | |
|     >>> C.__class__ is M
 | |
|     True
 | |
|     >>> a = C()
 | |
|     >>> a.__class__ is C
 | |
|     True
 | |
|     >>> a.meth()
 | |
|     Hello
 | |
|     >>>
 | |
| 
 | |
| Use **kwds notation for the metaclass keyword.
 | |
| 
 | |
|     >>> kwds = {'metaclass': M}
 | |
|     >>> class C(**kwds): pass
 | |
|     ...
 | |
|     >>> C.__class__ is M
 | |
|     True
 | |
|     >>> a = C()
 | |
|     >>> a.__class__ is C
 | |
|     True
 | |
|     >>>
 | |
| 
 | |
| Use a metaclass with a __prepare__ static method.
 | |
| 
 | |
|     >>> class M(type):
 | |
|     ...    @staticmethod
 | |
|     ...    def __prepare__(*args, **kwds):
 | |
|     ...        print("Prepare called:", args, kwds)
 | |
|     ...        return dict()
 | |
|     ...    def __new__(cls, name, bases, namespace, **kwds):
 | |
|     ...        print("New called:", kwds)
 | |
|     ...        return type.__new__(cls, name, bases, namespace)
 | |
|     ...    def __init__(cls, *args, **kwds):
 | |
|     ...        pass
 | |
|     ...
 | |
|     >>> class C(metaclass=M):
 | |
|     ...     def meth(self): print("Hello")
 | |
|     ...
 | |
|     Prepare called: ('C', ()) {}
 | |
|     New called: {}
 | |
|     >>>
 | |
| 
 | |
| Also pass another keyword.
 | |
| 
 | |
|     >>> class C(object, metaclass=M, other="haha"):
 | |
|     ...     pass
 | |
|     ...
 | |
|     Prepare called: ('C', (<class 'object'>,)) {'other': 'haha'}
 | |
|     New called: {'other': 'haha'}
 | |
|     >>> C.__class__ is M
 | |
|     True
 | |
|     >>> C.__bases__ == (object,)
 | |
|     True
 | |
|     >>> a = C()
 | |
|     >>> a.__class__ is C
 | |
|     True
 | |
|     >>>
 | |
| 
 | |
| Check that build_class doesn't mutate the kwds dict.
 | |
| 
 | |
|     >>> kwds = {'metaclass': type}
 | |
|     >>> class C(**kwds): pass
 | |
|     ...
 | |
|     >>> kwds == {'metaclass': type}
 | |
|     True
 | |
|     >>>
 | |
| 
 | |
| Use various combinations of explicit keywords and **kwds.
 | |
| 
 | |
|     >>> bases = (object,)
 | |
|     >>> kwds = {'metaclass': M, 'other': 'haha'}
 | |
|     >>> class C(*bases, **kwds): pass
 | |
|     ...
 | |
|     Prepare called: ('C', (<class 'object'>,)) {'other': 'haha'}
 | |
|     New called: {'other': 'haha'}
 | |
|     >>> C.__class__ is M
 | |
|     True
 | |
|     >>> C.__bases__ == (object,)
 | |
|     True
 | |
|     >>> class B: pass
 | |
|     >>> kwds = {'other': 'haha'}
 | |
|     >>> class C(B, metaclass=M, *bases, **kwds): pass
 | |
|     ...
 | |
|     Prepare called: ('C', (<class 'test.test_metaclass.B'>, <class 'object'>)) {'other': 'haha'}
 | |
|     New called: {'other': 'haha'}
 | |
|     >>> C.__class__ is M
 | |
|     True
 | |
|     >>> C.__bases__ == (B, object)
 | |
|     True
 | |
|     >>>
 | |
| 
 | |
| Check for duplicate keywords.
 | |
| 
 | |
|     >>> class C(metaclass=type, metaclass=type): pass
 | |
|     ...
 | |
|     Traceback (most recent call last):
 | |
|     [...]
 | |
|     SyntaxError: keyword argument repeated: metaclass
 | |
|     >>>
 | |
| 
 | |
| Another way.
 | |
| 
 | |
|     >>> kwds = {'metaclass': type}
 | |
|     >>> class C(metaclass=type, **kwds): pass
 | |
|     ...
 | |
|     Traceback (most recent call last):
 | |
|     [...]
 | |
|     TypeError: __build_class__() got multiple values for keyword argument 'metaclass'
 | |
|     >>>
 | |
| 
 | |
| Use a __prepare__ method that returns an instrumented dict.
 | |
| 
 | |
|     >>> class LoggingDict(dict):
 | |
|     ...     def __setitem__(self, key, value):
 | |
|     ...         print("d[%r] = %r" % (key, value))
 | |
|     ...         dict.__setitem__(self, key, value)
 | |
|     ...
 | |
|     >>> class Meta(type):
 | |
|     ...    @staticmethod
 | |
|     ...    def __prepare__(name, bases):
 | |
|     ...        return LoggingDict()
 | |
|     ...
 | |
|     >>> class C(metaclass=Meta):
 | |
|     ...     foo = 2+2
 | |
|     ...     foo = 42
 | |
|     ...     bar = 123
 | |
|     ...
 | |
|     d['__module__'] = 'test.test_metaclass'
 | |
|     d['__qualname__'] = 'C'
 | |
|     d['foo'] = 4
 | |
|     d['foo'] = 42
 | |
|     d['bar'] = 123
 | |
|     >>>
 | |
| 
 | |
| Use a metaclass that doesn't derive from type.
 | |
| 
 | |
|     >>> def meta(name, bases, namespace, **kwds):
 | |
|     ...     print("meta:", name, bases)
 | |
|     ...     print("ns:", sorted(namespace.items()))
 | |
|     ...     print("kw:", sorted(kwds.items()))
 | |
|     ...     return namespace
 | |
|     ...
 | |
|     >>> class C(metaclass=meta):
 | |
|     ...     a = 42
 | |
|     ...     b = 24
 | |
|     ...
 | |
|     meta: C ()
 | |
|     ns: [('__module__', 'test.test_metaclass'), ('__qualname__', 'C'), ('a', 42), ('b', 24)]
 | |
|     kw: []
 | |
|     >>> type(C) is dict
 | |
|     True
 | |
|     >>> print(sorted(C.items()))
 | |
|     [('__module__', 'test.test_metaclass'), ('__qualname__', 'C'), ('a', 42), ('b', 24)]
 | |
|     >>>
 | |
| 
 | |
| And again, with a __prepare__ attribute.
 | |
| 
 | |
|     >>> def prepare(name, bases, **kwds):
 | |
|     ...     print("prepare:", name, bases, sorted(kwds.items()))
 | |
|     ...     return LoggingDict()
 | |
|     ...
 | |
|     >>> meta.__prepare__ = prepare
 | |
|     >>> class C(metaclass=meta, other="booh"):
 | |
|     ...    a = 1
 | |
|     ...    a = 2
 | |
|     ...    b = 3
 | |
|     ...
 | |
|     prepare: C () [('other', 'booh')]
 | |
|     d['__module__'] = 'test.test_metaclass'
 | |
|     d['__qualname__'] = 'C'
 | |
|     d['a'] = 1
 | |
|     d['a'] = 2
 | |
|     d['b'] = 3
 | |
|     meta: C ()
 | |
|     ns: [('__module__', 'test.test_metaclass'), ('__qualname__', 'C'), ('a', 2), ('b', 3)]
 | |
|     kw: [('other', 'booh')]
 | |
|     >>>
 | |
| 
 | |
| The default metaclass must define a __prepare__() method.
 | |
| 
 | |
|     >>> type.__prepare__()
 | |
|     {}
 | |
|     >>>
 | |
| 
 | |
| Make sure it works with subclassing.
 | |
| 
 | |
|     >>> class M(type):
 | |
|     ...     @classmethod
 | |
|     ...     def __prepare__(cls, *args, **kwds):
 | |
|     ...         d = super().__prepare__(*args, **kwds)
 | |
|     ...         d["hello"] = 42
 | |
|     ...         return d
 | |
|     ...
 | |
|     >>> class C(metaclass=M):
 | |
|     ...     print(hello)
 | |
|     ...
 | |
|     42
 | |
|     >>> print(C.hello)
 | |
|     42
 | |
|     >>>
 | |
| 
 | |
| Test failures in looking up the __prepare__ method work.
 | |
|     >>> class ObscureException(Exception):
 | |
|     ...     pass
 | |
|     >>> class FailDescr:
 | |
|     ...     def __get__(self, instance, owner):
 | |
|     ...        raise ObscureException
 | |
|     >>> class Meta(type):
 | |
|     ...     __prepare__ = FailDescr()
 | |
|     >>> class X(metaclass=Meta):
 | |
|     ...     pass
 | |
|     Traceback (most recent call last):
 | |
|     [...]
 | |
|     test.test_metaclass.ObscureException
 | |
| 
 | |
| """
 | |
| 
 | |
| import sys
 | |
| 
 | |
| # Trace function introduces __locals__ which causes various tests to fail.
 | |
| if hasattr(sys, 'gettrace') and sys.gettrace():
 | |
|     __test__ = {}
 | |
| else:
 | |
|     __test__ = {'doctests' : doctests}
 | |
| 
 | |
| def test_main(verbose=False):
 | |
|     from test import support
 | |
|     from test import test_metaclass
 | |
|     support.run_doctest(test_metaclass, verbose)
 | |
| 
 | |
| if __name__ == "__main__":
 | |
|     test_main(verbose=True)
 |