• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1doctests = """
2
3Basic class construction.
4
5    >>> class C:
6    ...     def meth(self): print("Hello")
7    ...
8    >>> C.__class__ is type
9    True
10    >>> a = C()
11    >>> a.__class__ is C
12    True
13    >>> a.meth()
14    Hello
15    >>>
16
17Use *args notation for the bases.
18
19    >>> class A: pass
20    >>> class B: pass
21    >>> bases = (A, B)
22    >>> class C(*bases): pass
23    >>> C.__bases__ == bases
24    True
25    >>>
26
27Use a trivial metaclass.
28
29    >>> class M(type):
30    ...     pass
31    ...
32    >>> class C(metaclass=M):
33    ...    def meth(self): print("Hello")
34    ...
35    >>> C.__class__ is M
36    True
37    >>> a = C()
38    >>> a.__class__ is C
39    True
40    >>> a.meth()
41    Hello
42    >>>
43
44Use **kwds notation for the metaclass keyword.
45
46    >>> kwds = {'metaclass': M}
47    >>> class C(**kwds): pass
48    ...
49    >>> C.__class__ is M
50    True
51    >>> a = C()
52    >>> a.__class__ is C
53    True
54    >>>
55
56Use a metaclass with a __prepare__ static method.
57
58    >>> class M(type):
59    ...    @staticmethod
60    ...    def __prepare__(*args, **kwds):
61    ...        print("Prepare called:", args, kwds)
62    ...        return dict()
63    ...    def __new__(cls, name, bases, namespace, **kwds):
64    ...        print("New called:", kwds)
65    ...        return type.__new__(cls, name, bases, namespace)
66    ...    def __init__(cls, *args, **kwds):
67    ...        pass
68    ...
69    >>> class C(metaclass=M):
70    ...     def meth(self): print("Hello")
71    ...
72    Prepare called: ('C', ()) {}
73    New called: {}
74    >>>
75
76Also pass another keyword.
77
78    >>> class C(object, metaclass=M, other="haha"):
79    ...     pass
80    ...
81    Prepare called: ('C', (<class 'object'>,)) {'other': 'haha'}
82    New called: {'other': 'haha'}
83    >>> C.__class__ is M
84    True
85    >>> C.__bases__ == (object,)
86    True
87    >>> a = C()
88    >>> a.__class__ is C
89    True
90    >>>
91
92Check that build_class doesn't mutate the kwds dict.
93
94    >>> kwds = {'metaclass': type}
95    >>> class C(**kwds): pass
96    ...
97    >>> kwds == {'metaclass': type}
98    True
99    >>>
100
101Use various combinations of explicit keywords and **kwds.
102
103    >>> bases = (object,)
104    >>> kwds = {'metaclass': M, 'other': 'haha'}
105    >>> class C(*bases, **kwds): pass
106    ...
107    Prepare called: ('C', (<class 'object'>,)) {'other': 'haha'}
108    New called: {'other': 'haha'}
109    >>> C.__class__ is M
110    True
111    >>> C.__bases__ == (object,)
112    True
113    >>> class B: pass
114    >>> kwds = {'other': 'haha'}
115    >>> class C(B, metaclass=M, *bases, **kwds): pass
116    ...
117    Prepare called: ('C', (<class 'test.test_metaclass.B'>, <class 'object'>)) {'other': 'haha'}
118    New called: {'other': 'haha'}
119    >>> C.__class__ is M
120    True
121    >>> C.__bases__ == (B, object)
122    True
123    >>>
124
125Check for duplicate keywords.
126
127    >>> class C(metaclass=type, metaclass=type): pass
128    ...
129    Traceback (most recent call last):
130    [...]
131    SyntaxError: keyword argument repeated: metaclass
132    >>>
133
134Another way.
135
136    >>> kwds = {'metaclass': type}
137    >>> class C(metaclass=type, **kwds): pass
138    ...
139    Traceback (most recent call last):
140    [...]
141    TypeError: __build_class__() got multiple values for keyword argument 'metaclass'
142    >>>
143
144Use a __prepare__ method that returns an instrumented dict.
145
146    >>> class LoggingDict(dict):
147    ...     def __setitem__(self, key, value):
148    ...         print("d[%r] = %r" % (key, value))
149    ...         dict.__setitem__(self, key, value)
150    ...
151    >>> class Meta(type):
152    ...    @staticmethod
153    ...    def __prepare__(name, bases):
154    ...        return LoggingDict()
155    ...
156    >>> class C(metaclass=Meta):
157    ...     foo = 2+2
158    ...     foo = 42
159    ...     bar = 123
160    ...
161    d['__module__'] = 'test.test_metaclass'
162    d['__qualname__'] = 'C'
163    d['foo'] = 4
164    d['foo'] = 42
165    d['bar'] = 123
166    >>>
167
168Use a metaclass that doesn't derive from type.
169
170    >>> def meta(name, bases, namespace, **kwds):
171    ...     print("meta:", name, bases)
172    ...     print("ns:", sorted(namespace.items()))
173    ...     print("kw:", sorted(kwds.items()))
174    ...     return namespace
175    ...
176    >>> class C(metaclass=meta):
177    ...     a = 42
178    ...     b = 24
179    ...
180    meta: C ()
181    ns: [('__module__', 'test.test_metaclass'), ('__qualname__', 'C'), ('a', 42), ('b', 24)]
182    kw: []
183    >>> type(C) is dict
184    True
185    >>> print(sorted(C.items()))
186    [('__module__', 'test.test_metaclass'), ('__qualname__', 'C'), ('a', 42), ('b', 24)]
187    >>>
188
189And again, with a __prepare__ attribute.
190
191    >>> def prepare(name, bases, **kwds):
192    ...     print("prepare:", name, bases, sorted(kwds.items()))
193    ...     return LoggingDict()
194    ...
195    >>> meta.__prepare__ = prepare
196    >>> class C(metaclass=meta, other="booh"):
197    ...    a = 1
198    ...    a = 2
199    ...    b = 3
200    ...
201    prepare: C () [('other', 'booh')]
202    d['__module__'] = 'test.test_metaclass'
203    d['__qualname__'] = 'C'
204    d['a'] = 1
205    d['a'] = 2
206    d['b'] = 3
207    meta: C ()
208    ns: [('__module__', 'test.test_metaclass'), ('__qualname__', 'C'), ('a', 2), ('b', 3)]
209    kw: [('other', 'booh')]
210    >>>
211
212The default metaclass must define a __prepare__() method.
213
214    >>> type.__prepare__()
215    {}
216    >>>
217
218Make sure it works with subclassing.
219
220    >>> class M(type):
221    ...     @classmethod
222    ...     def __prepare__(cls, *args, **kwds):
223    ...         d = super().__prepare__(*args, **kwds)
224    ...         d["hello"] = 42
225    ...         return d
226    ...
227    >>> class C(metaclass=M):
228    ...     print(hello)
229    ...
230    42
231    >>> print(C.hello)
232    42
233    >>>
234
235Test failures in looking up the __prepare__ method work.
236    >>> class ObscureException(Exception):
237    ...     pass
238    >>> class FailDescr:
239    ...     def __get__(self, instance, owner):
240    ...        raise ObscureException
241    >>> class Meta(type):
242    ...     __prepare__ = FailDescr()
243    >>> class X(metaclass=Meta):
244    ...     pass
245    Traceback (most recent call last):
246    [...]
247    test.test_metaclass.ObscureException
248
249"""
250
251import sys
252
253# Trace function introduces __locals__ which causes various tests to fail.
254if hasattr(sys, 'gettrace') and sys.gettrace():
255    __test__ = {}
256else:
257    __test__ = {'doctests' : doctests}
258
259def test_main(verbose=False):
260    from test import support
261    from test import test_metaclass
262    support.run_doctest(test_metaclass, verbose)
263
264if __name__ == "__main__":
265    test_main(verbose=True)
266