1import sys 2import types 3import unittest 4 5 6class Test(unittest.TestCase): 7 def test_init_subclass(self): 8 class A: 9 initialized = False 10 11 def __init_subclass__(cls): 12 super().__init_subclass__() 13 cls.initialized = True 14 15 class B(A): 16 pass 17 18 self.assertFalse(A.initialized) 19 self.assertTrue(B.initialized) 20 21 def test_init_subclass_dict(self): 22 class A(dict): 23 initialized = False 24 25 def __init_subclass__(cls): 26 super().__init_subclass__() 27 cls.initialized = True 28 29 class B(A): 30 pass 31 32 self.assertFalse(A.initialized) 33 self.assertTrue(B.initialized) 34 35 def test_init_subclass_kwargs(self): 36 class A: 37 def __init_subclass__(cls, **kwargs): 38 cls.kwargs = kwargs 39 40 class B(A, x=3): 41 pass 42 43 self.assertEqual(B.kwargs, dict(x=3)) 44 45 def test_init_subclass_error(self): 46 class A: 47 def __init_subclass__(cls): 48 raise RuntimeError 49 50 with self.assertRaises(RuntimeError): 51 class B(A): 52 pass 53 54 def test_init_subclass_wrong(self): 55 class A: 56 def __init_subclass__(cls, whatever): 57 pass 58 59 with self.assertRaises(TypeError): 60 class B(A): 61 pass 62 63 def test_init_subclass_skipped(self): 64 class BaseWithInit: 65 def __init_subclass__(cls, **kwargs): 66 super().__init_subclass__(**kwargs) 67 cls.initialized = cls 68 69 class BaseWithoutInit(BaseWithInit): 70 pass 71 72 class A(BaseWithoutInit): 73 pass 74 75 self.assertIs(A.initialized, A) 76 self.assertIs(BaseWithoutInit.initialized, BaseWithoutInit) 77 78 def test_init_subclass_diamond(self): 79 class Base: 80 def __init_subclass__(cls, **kwargs): 81 super().__init_subclass__(**kwargs) 82 cls.calls = [] 83 84 class Left(Base): 85 pass 86 87 class Middle: 88 def __init_subclass__(cls, middle, **kwargs): 89 super().__init_subclass__(**kwargs) 90 cls.calls += [middle] 91 92 class Right(Base): 93 def __init_subclass__(cls, right="right", **kwargs): 94 super().__init_subclass__(**kwargs) 95 cls.calls += [right] 96 97 class A(Left, Middle, Right, middle="middle"): 98 pass 99 100 self.assertEqual(A.calls, ["right", "middle"]) 101 self.assertEqual(Left.calls, []) 102 self.assertEqual(Right.calls, []) 103 104 def test_set_name(self): 105 class Descriptor: 106 def __set_name__(self, owner, name): 107 self.owner = owner 108 self.name = name 109 110 class A: 111 d = Descriptor() 112 113 self.assertEqual(A.d.name, "d") 114 self.assertIs(A.d.owner, A) 115 116 def test_set_name_metaclass(self): 117 class Meta(type): 118 def __new__(cls, name, bases, ns): 119 ret = super().__new__(cls, name, bases, ns) 120 self.assertEqual(ret.d.name, "d") 121 self.assertIs(ret.d.owner, ret) 122 return 0 123 124 class Descriptor: 125 def __set_name__(self, owner, name): 126 self.owner = owner 127 self.name = name 128 129 class A(metaclass=Meta): 130 d = Descriptor() 131 self.assertEqual(A, 0) 132 133 def test_set_name_error(self): 134 class Descriptor: 135 def __set_name__(self, owner, name): 136 1/0 137 138 with self.assertRaises(RuntimeError) as cm: 139 class NotGoingToWork: 140 attr = Descriptor() 141 142 exc = cm.exception 143 self.assertRegex(str(exc), r'\bNotGoingToWork\b') 144 self.assertRegex(str(exc), r'\battr\b') 145 self.assertRegex(str(exc), r'\bDescriptor\b') 146 self.assertIsInstance(exc.__cause__, ZeroDivisionError) 147 148 def test_set_name_wrong(self): 149 class Descriptor: 150 def __set_name__(self): 151 pass 152 153 with self.assertRaises(RuntimeError) as cm: 154 class NotGoingToWork: 155 attr = Descriptor() 156 157 exc = cm.exception 158 self.assertRegex(str(exc), r'\bNotGoingToWork\b') 159 self.assertRegex(str(exc), r'\battr\b') 160 self.assertRegex(str(exc), r'\bDescriptor\b') 161 self.assertIsInstance(exc.__cause__, TypeError) 162 163 def test_set_name_lookup(self): 164 resolved = [] 165 class NonDescriptor: 166 def __getattr__(self, name): 167 resolved.append(name) 168 169 class A: 170 d = NonDescriptor() 171 172 self.assertNotIn('__set_name__', resolved, 173 '__set_name__ is looked up in instance dict') 174 175 def test_set_name_init_subclass(self): 176 class Descriptor: 177 def __set_name__(self, owner, name): 178 self.owner = owner 179 self.name = name 180 181 class Meta(type): 182 def __new__(cls, name, bases, ns): 183 self = super().__new__(cls, name, bases, ns) 184 self.meta_owner = self.owner 185 self.meta_name = self.name 186 return self 187 188 class A: 189 def __init_subclass__(cls): 190 cls.owner = cls.d.owner 191 cls.name = cls.d.name 192 193 class B(A, metaclass=Meta): 194 d = Descriptor() 195 196 self.assertIs(B.owner, B) 197 self.assertEqual(B.name, 'd') 198 self.assertIs(B.meta_owner, B) 199 self.assertEqual(B.name, 'd') 200 201 def test_set_name_modifying_dict(self): 202 notified = [] 203 class Descriptor: 204 def __set_name__(self, owner, name): 205 setattr(owner, name + 'x', None) 206 notified.append(name) 207 208 class A: 209 a = Descriptor() 210 b = Descriptor() 211 c = Descriptor() 212 d = Descriptor() 213 e = Descriptor() 214 215 self.assertCountEqual(notified, ['a', 'b', 'c', 'd', 'e']) 216 217 def test_errors(self): 218 class MyMeta(type): 219 pass 220 221 with self.assertRaises(TypeError): 222 class MyClass(metaclass=MyMeta, otherarg=1): 223 pass 224 225 with self.assertRaises(TypeError): 226 types.new_class("MyClass", (object,), 227 dict(metaclass=MyMeta, otherarg=1)) 228 types.prepare_class("MyClass", (object,), 229 dict(metaclass=MyMeta, otherarg=1)) 230 231 class MyMeta(type): 232 def __init__(self, name, bases, namespace, otherarg): 233 super().__init__(name, bases, namespace) 234 235 with self.assertRaises(TypeError): 236 class MyClass(metaclass=MyMeta, otherarg=1): 237 pass 238 239 class MyMeta(type): 240 def __new__(cls, name, bases, namespace, otherarg): 241 return super().__new__(cls, name, bases, namespace) 242 243 def __init__(self, name, bases, namespace, otherarg): 244 super().__init__(name, bases, namespace) 245 self.otherarg = otherarg 246 247 class MyClass(metaclass=MyMeta, otherarg=1): 248 pass 249 250 self.assertEqual(MyClass.otherarg, 1) 251 252 def test_errors_changed_pep487(self): 253 # These tests failed before Python 3.6, PEP 487 254 class MyMeta(type): 255 def __new__(cls, name, bases, namespace): 256 return super().__new__(cls, name=name, bases=bases, 257 dict=namespace) 258 259 with self.assertRaises(TypeError): 260 class MyClass(metaclass=MyMeta): 261 pass 262 263 class MyMeta(type): 264 def __new__(cls, name, bases, namespace, otherarg): 265 self = super().__new__(cls, name, bases, namespace) 266 self.otherarg = otherarg 267 return self 268 269 class MyClass(metaclass=MyMeta, otherarg=1): 270 pass 271 272 self.assertEqual(MyClass.otherarg, 1) 273 274 def test_type(self): 275 t = type('NewClass', (object,), {}) 276 self.assertIsInstance(t, type) 277 self.assertEqual(t.__name__, 'NewClass') 278 279 with self.assertRaises(TypeError): 280 type(name='NewClass', bases=(object,), dict={}) 281 282 283if __name__ == "__main__": 284 unittest.main() 285