| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269 |
- import doctest
- import unittest
- 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 load_tests(loader, tests, pattern):
- tests.addTest(doctest.DocTestSuite())
- return tests
- if __name__ == "__main__":
- unittest.main()
|