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['foo'] = 4 168 d['foo'] = 42 169 d['bar'] = 123 170 >>> 171 172Use 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 193And 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 216The default metaclass must define a __prepare__() method. 217 218 >>> type.__prepare__() 219 {} 220 >>> 221 222Make 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 239Test 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 255import sys 256 257# Trace function introduces __locals__ which causes various tests to fail. 258if hasattr(sys, 'gettrace') and sys.gettrace(): 259 __test__ = {} 260else: 261 __test__ = {'doctests' : doctests} 262 263def load_tests(loader, tests, pattern): 264 tests.addTest(doctest.DocTestSuite()) 265 return tests 266 267 268if __name__ == "__main__": 269 unittest.main() 270