mirror of
				https://github.com/python/cpython.git
				synced 2025-11-02 22:51:25 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			265 lines
		
	
	
	
		
			6.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			265 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
 | 
						|
    >>>
 | 
						|
 | 
						|
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)
 |