1import doctest 2import unittest 3 4 5doctests = """ 6 7Basic 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 21Use *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 31Use 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 48Use **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 60Use 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 80Also 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 96Check 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 105Use 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 129Check 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 138Another 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 148Use 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['__firstlineno__'] = 1 168 d['foo'] = 4 169 d['foo'] = 42 170 d['bar'] = 123 171 d['__static_attributes__'] = () 172 >>> 173 174Use a metaclass that doesn't derive from type. 175 176 >>> def meta(name, bases, namespace, **kwds): 177 ... print("meta:", name, bases) 178 ... print("ns:", sorted(namespace.items())) 179 ... print("kw:", sorted(kwds.items())) 180 ... return namespace 181 ... 182 >>> class C(metaclass=meta): 183 ... a = 42 184 ... b = 24 185 ... 186 meta: C () 187 ns: [('__firstlineno__', 1), ('__module__', 'test.test_metaclass'), ('__qualname__', 'C'), ('__static_attributes__', ()), ('a', 42), ('b', 24)] 188 kw: [] 189 >>> type(C) is dict 190 True 191 >>> print(sorted(C.items())) 192 [('__firstlineno__', 1), ('__module__', 'test.test_metaclass'), ('__qualname__', 'C'), ('__static_attributes__', ()), ('a', 42), ('b', 24)] 193 >>> 194 195And again, with a __prepare__ attribute. 196 197 >>> def prepare(name, bases, **kwds): 198 ... print("prepare:", name, bases, sorted(kwds.items())) 199 ... return LoggingDict() 200 ... 201 >>> meta.__prepare__ = prepare 202 >>> class C(metaclass=meta, other="booh"): 203 ... a = 1 204 ... a = 2 205 ... b = 3 206 ... 207 prepare: C () [('other', 'booh')] 208 d['__module__'] = 'test.test_metaclass' 209 d['__qualname__'] = 'C' 210 d['__firstlineno__'] = 1 211 d['a'] = 1 212 d['a'] = 2 213 d['b'] = 3 214 d['__static_attributes__'] = () 215 meta: C () 216 ns: [('__firstlineno__', 1), ('__module__', 'test.test_metaclass'), ('__qualname__', 'C'), ('__static_attributes__', ()), ('a', 2), ('b', 3)] 217 kw: [('other', 'booh')] 218 >>> 219 220The default metaclass must define a __prepare__() method. 221 222 >>> type.__prepare__() 223 {} 224 >>> 225 226Make sure it works with subclassing. 227 228 >>> class M(type): 229 ... @classmethod 230 ... def __prepare__(cls, *args, **kwds): 231 ... d = super().__prepare__(*args, **kwds) 232 ... d["hello"] = 42 233 ... return d 234 ... 235 >>> class C(metaclass=M): 236 ... print(hello) 237 ... 238 42 239 >>> print(C.hello) 240 42 241 >>> 242 243Test failures in looking up the __prepare__ method work. 244 >>> class ObscureException(Exception): 245 ... pass 246 >>> class FailDescr: 247 ... def __get__(self, instance, owner): 248 ... raise ObscureException 249 >>> class Meta(type): 250 ... __prepare__ = FailDescr() 251 >>> class X(metaclass=Meta): 252 ... pass 253 Traceback (most recent call last): 254 [...] 255 test.test_metaclass.ObscureException 256 257""" 258 259import sys 260 261# Trace function introduces __locals__ which causes various tests to fail. 262if hasattr(sys, 'gettrace') and sys.gettrace(): 263 __test__ = {} 264else: 265 __test__ = {'doctests' : doctests} 266 267def load_tests(loader, tests, pattern): 268 tests.addTest(doctest.DocTestSuite()) 269 return tests 270 271 272if __name__ == "__main__": 273 unittest.main() 274