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