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