blob: 219ab99840b150c88c46e242efec3e2ef87c4317 [file] [log] [blame]
Guido van Rossum52cc1d82007-03-18 15:41:51 +00001doctests = """
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)
Guido van Rossumd8faa362007-04-27 19:54:29 +000066 ... def __init__(cls, *args, **kwds):
67 ... pass
Guido van Rossum52cc1d82007-03-18 15:41:51 +000068 ...
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 ...
Martin v. Löwis250ad612008-04-07 05:43:42 +000081 Prepare called: ('C', (<class 'object'>,)) {'other': 'haha'}
Guido van Rossum52cc1d82007-03-18 15:41:51 +000082 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 ...
Martin v. Löwis250ad612008-04-07 05:43:42 +0000107 Prepare called: ('C', (<class 'object'>,)) {'other': 'haha'}
Guido van Rossum52cc1d82007-03-18 15:41:51 +0000108 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 ...
Martin v. Löwis250ad612008-04-07 05:43:42 +0000117 Prepare called: ('C', (<class 'test.test_metaclass.B'>, <class 'object'>)) {'other': 'haha'}
Guido van Rossum52cc1d82007-03-18 15:41:51 +0000118 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 [...]
Benjamin Peterson13b6d0d2008-07-01 20:26:36 +0000131 SyntaxError: keyword argument repeated
Guido van Rossum52cc1d82007-03-18 15:41:51 +0000132 >>>
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['foo'] = 4
163 d['foo'] = 42
164 d['bar'] = 123
165 >>>
166
167Use a metaclass that doesn't derive from type.
168
169 >>> def meta(name, bases, namespace, **kwds):
170 ... print("meta:", name, bases)
171 ... print("ns:", sorted(namespace.items()))
172 ... print("kw:", sorted(kwds.items()))
173 ... return namespace
174 ...
175 >>> class C(metaclass=meta):
176 ... a = 42
177 ... b = 24
178 ...
179 meta: C ()
180 ns: [('__module__', 'test.test_metaclass'), ('a', 42), ('b', 24)]
181 kw: []
182 >>> type(C) is dict
183 True
184 >>> print(sorted(C.items()))
185 [('__module__', 'test.test_metaclass'), ('a', 42), ('b', 24)]
186 >>>
187
188And again, with a __prepare__ attribute.
189
190 >>> def prepare(name, bases, **kwds):
191 ... print("prepare:", name, bases, sorted(kwds.items()))
192 ... return LoggingDict()
193 ...
194 >>> meta.__prepare__ = prepare
195 >>> class C(metaclass=meta, other="booh"):
196 ... a = 1
197 ... a = 2
198 ... b = 3
199 ...
200 prepare: C () [('other', 'booh')]
201 d['__module__'] = 'test.test_metaclass'
202 d['a'] = 1
203 d['a'] = 2
204 d['b'] = 3
205 meta: C ()
206 ns: [('__module__', 'test.test_metaclass'), ('a', 2), ('b', 3)]
207 kw: [('other', 'booh')]
208 >>>
209
Guido van Rossum47374822007-08-02 16:48:17 +0000210The default metaclass must define a __prepare__() method.
211
212 >>> type.__prepare__()
213 {}
214 >>>
215
216Make sure it works with subclassing.
217
218 >>> class M(type):
219 ... @classmethod
220 ... def __prepare__(cls, *args, **kwds):
221 ... d = super().__prepare__(*args, **kwds)
222 ... d["hello"] = 42
223 ... return d
224 ...
225 >>> class C(metaclass=M):
226 ... print(hello)
227 ...
228 42
229 >>> print(C.hello)
230 42
231 >>>
232
Benjamin Peterson23e018a2010-02-27 17:40:01 +0000233Test failures in looking up the __prepare__ method work.
234 >>> class ObscureException(Exception):
235 ... pass
236 >>> class FailDescr:
237 ... def __get__(self, instance, owner):
238 ... raise ObscureException
239 >>> class Meta(type):
240 ... __prepare__ = FailDescr()
241 >>> class X(metaclass=Meta):
242 ... pass
243 Traceback (most recent call last):
244 [...]
245 test.test_metaclass.ObscureException
246
Guido van Rossum52cc1d82007-03-18 15:41:51 +0000247"""
248
249__test__ = {'doctests' : doctests}
250
251def test_main(verbose=False):
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000252 from test import support
Guido van Rossum52cc1d82007-03-18 15:41:51 +0000253 from test import test_metaclass
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000254 support.run_doctest(test_metaclass, verbose)
Guido van Rossum52cc1d82007-03-18 15:41:51 +0000255
256if __name__ == "__main__":
257 test_main(verbose=True)