| 
									
										
										
										
											2016-07-30 16:26:03 +10:00
										 |  |  | import types | 
					
						
							| 
									
										
										
										
											2016-07-30 14:06:15 +03:00
										 |  |  | import unittest | 
					
						
							| 
									
										
										
										
											2016-07-30 16:26:03 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-30 14:06:15 +03:00
										 |  |  | class Test(unittest.TestCase): | 
					
						
							| 
									
										
										
										
											2016-07-30 16:26:03 +10:00
										 |  |  |     def test_init_subclass(self): | 
					
						
							| 
									
										
										
										
											2016-07-30 14:06:15 +03:00
										 |  |  |         class A: | 
					
						
							| 
									
										
										
										
											2016-07-30 16:26:03 +10:00
										 |  |  |             initialized = False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             def __init_subclass__(cls): | 
					
						
							|  |  |  |                 super().__init_subclass__() | 
					
						
							|  |  |  |                 cls.initialized = True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         class B(A): | 
					
						
							|  |  |  |             pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.assertFalse(A.initialized) | 
					
						
							|  |  |  |         self.assertTrue(B.initialized) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_init_subclass_dict(self): | 
					
						
							| 
									
										
										
										
											2016-07-30 14:06:15 +03:00
										 |  |  |         class A(dict): | 
					
						
							| 
									
										
										
										
											2016-07-30 16:26:03 +10:00
										 |  |  |             initialized = False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             def __init_subclass__(cls): | 
					
						
							|  |  |  |                 super().__init_subclass__() | 
					
						
							|  |  |  |                 cls.initialized = True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         class B(A): | 
					
						
							|  |  |  |             pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.assertFalse(A.initialized) | 
					
						
							|  |  |  |         self.assertTrue(B.initialized) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_init_subclass_kwargs(self): | 
					
						
							| 
									
										
										
										
											2016-07-30 14:06:15 +03:00
										 |  |  |         class A: | 
					
						
							| 
									
										
										
										
											2016-07-30 16:26:03 +10:00
										 |  |  |             def __init_subclass__(cls, **kwargs): | 
					
						
							|  |  |  |                 cls.kwargs = kwargs | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         class B(A, x=3): | 
					
						
							|  |  |  |             pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.assertEqual(B.kwargs, dict(x=3)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_init_subclass_error(self): | 
					
						
							| 
									
										
										
										
											2016-07-30 14:06:15 +03:00
										 |  |  |         class A: | 
					
						
							| 
									
										
										
										
											2016-07-30 16:26:03 +10:00
										 |  |  |             def __init_subclass__(cls): | 
					
						
							|  |  |  |                 raise RuntimeError | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         with self.assertRaises(RuntimeError): | 
					
						
							|  |  |  |             class B(A): | 
					
						
							|  |  |  |                 pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_init_subclass_wrong(self): | 
					
						
							| 
									
										
										
										
											2016-07-30 14:06:15 +03:00
										 |  |  |         class A: | 
					
						
							| 
									
										
										
										
											2016-07-30 16:26:03 +10:00
										 |  |  |             def __init_subclass__(cls, whatever): | 
					
						
							|  |  |  |                 pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         with self.assertRaises(TypeError): | 
					
						
							|  |  |  |             class B(A): | 
					
						
							|  |  |  |                 pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_init_subclass_skipped(self): | 
					
						
							| 
									
										
										
										
											2016-07-30 14:06:15 +03:00
										 |  |  |         class BaseWithInit: | 
					
						
							| 
									
										
										
										
											2016-07-30 16:26:03 +10:00
										 |  |  |             def __init_subclass__(cls, **kwargs): | 
					
						
							|  |  |  |                 super().__init_subclass__(**kwargs) | 
					
						
							|  |  |  |                 cls.initialized = cls | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         class BaseWithoutInit(BaseWithInit): | 
					
						
							|  |  |  |             pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         class A(BaseWithoutInit): | 
					
						
							|  |  |  |             pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.assertIs(A.initialized, A) | 
					
						
							|  |  |  |         self.assertIs(BaseWithoutInit.initialized, BaseWithoutInit) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_init_subclass_diamond(self): | 
					
						
							| 
									
										
										
										
											2016-07-30 14:06:15 +03:00
										 |  |  |         class Base: | 
					
						
							| 
									
										
										
										
											2016-07-30 16:26:03 +10:00
										 |  |  |             def __init_subclass__(cls, **kwargs): | 
					
						
							|  |  |  |                 super().__init_subclass__(**kwargs) | 
					
						
							|  |  |  |                 cls.calls = [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         class Left(Base): | 
					
						
							|  |  |  |             pass | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-30 14:06:15 +03:00
										 |  |  |         class Middle: | 
					
						
							| 
									
										
										
										
											2016-07-30 16:26:03 +10:00
										 |  |  |             def __init_subclass__(cls, middle, **kwargs): | 
					
						
							|  |  |  |                 super().__init_subclass__(**kwargs) | 
					
						
							|  |  |  |                 cls.calls += [middle] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         class Right(Base): | 
					
						
							|  |  |  |             def __init_subclass__(cls, right="right", **kwargs): | 
					
						
							|  |  |  |                 super().__init_subclass__(**kwargs) | 
					
						
							|  |  |  |                 cls.calls += [right] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         class A(Left, Middle, Right, middle="middle"): | 
					
						
							|  |  |  |             pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.assertEqual(A.calls, ["right", "middle"]) | 
					
						
							|  |  |  |         self.assertEqual(Left.calls, []) | 
					
						
							|  |  |  |         self.assertEqual(Right.calls, []) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_set_name(self): | 
					
						
							|  |  |  |         class Descriptor: | 
					
						
							|  |  |  |             def __set_name__(self, owner, name): | 
					
						
							|  |  |  |                 self.owner = owner | 
					
						
							|  |  |  |                 self.name = name | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-30 14:06:15 +03:00
										 |  |  |         class A: | 
					
						
							| 
									
										
										
										
											2016-07-30 16:26:03 +10:00
										 |  |  |             d = Descriptor() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.assertEqual(A.d.name, "d") | 
					
						
							|  |  |  |         self.assertIs(A.d.owner, A) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_set_name_metaclass(self): | 
					
						
							|  |  |  |         class Meta(type): | 
					
						
							|  |  |  |             def __new__(cls, name, bases, ns): | 
					
						
							|  |  |  |                 ret = super().__new__(cls, name, bases, ns) | 
					
						
							|  |  |  |                 self.assertEqual(ret.d.name, "d") | 
					
						
							|  |  |  |                 self.assertIs(ret.d.owner, ret) | 
					
						
							|  |  |  |                 return 0 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-30 14:06:15 +03:00
										 |  |  |         class Descriptor: | 
					
						
							| 
									
										
										
										
											2016-07-30 16:26:03 +10:00
										 |  |  |             def __set_name__(self, owner, name): | 
					
						
							|  |  |  |                 self.owner = owner | 
					
						
							|  |  |  |                 self.name = name | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-30 14:06:15 +03:00
										 |  |  |         class A(metaclass=Meta): | 
					
						
							| 
									
										
										
										
											2016-07-30 16:26:03 +10:00
										 |  |  |             d = Descriptor() | 
					
						
							|  |  |  |         self.assertEqual(A, 0) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_set_name_error(self): | 
					
						
							|  |  |  |         class Descriptor: | 
					
						
							|  |  |  |             def __set_name__(self, owner, name): | 
					
						
							| 
									
										
										
										
											2016-10-21 17:13:31 +03:00
										 |  |  |                 1/0 | 
					
						
							| 
									
										
										
										
											2016-07-30 16:26:03 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-11 11:53:06 +01:00
										 |  |  |         with self.assertRaises(ZeroDivisionError) as cm: | 
					
						
							| 
									
										
										
										
											2016-10-21 17:13:31 +03:00
										 |  |  |             class NotGoingToWork: | 
					
						
							|  |  |  |                 attr = Descriptor() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-11 11:53:06 +01:00
										 |  |  |         notes = cm.exception.__notes__ | 
					
						
							|  |  |  |         self.assertRegex(str(notes), r'\bNotGoingToWork\b') | 
					
						
							|  |  |  |         self.assertRegex(str(notes), r'\battr\b') | 
					
						
							|  |  |  |         self.assertRegex(str(notes), r'\bDescriptor\b') | 
					
						
							| 
									
										
										
										
											2016-07-30 16:26:03 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_set_name_wrong(self): | 
					
						
							|  |  |  |         class Descriptor: | 
					
						
							|  |  |  |             def __set_name__(self): | 
					
						
							|  |  |  |                 pass | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-11 11:53:06 +01:00
										 |  |  |         with self.assertRaises(TypeError) as cm: | 
					
						
							| 
									
										
										
										
											2016-10-21 17:13:31 +03:00
										 |  |  |             class NotGoingToWork: | 
					
						
							|  |  |  |                 attr = Descriptor() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-11 11:53:06 +01:00
										 |  |  |         notes = cm.exception.__notes__ | 
					
						
							|  |  |  |         self.assertRegex(str(notes), r'\bNotGoingToWork\b') | 
					
						
							|  |  |  |         self.assertRegex(str(notes), r'\battr\b') | 
					
						
							|  |  |  |         self.assertRegex(str(notes), r'\bDescriptor\b') | 
					
						
							| 
									
										
										
										
											2016-07-30 16:26:03 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-21 15:54:59 +03:00
										 |  |  |     def test_set_name_lookup(self): | 
					
						
							|  |  |  |         resolved = [] | 
					
						
							|  |  |  |         class NonDescriptor: | 
					
						
							|  |  |  |             def __getattr__(self, name): | 
					
						
							|  |  |  |                 resolved.append(name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         class A: | 
					
						
							|  |  |  |             d = NonDescriptor() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.assertNotIn('__set_name__', resolved, | 
					
						
							|  |  |  |                          '__set_name__ is looked up in instance dict') | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-30 16:26:03 +10:00
										 |  |  |     def test_set_name_init_subclass(self): | 
					
						
							|  |  |  |         class Descriptor: | 
					
						
							|  |  |  |             def __set_name__(self, owner, name): | 
					
						
							|  |  |  |                 self.owner = owner | 
					
						
							|  |  |  |                 self.name = name | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         class Meta(type): | 
					
						
							|  |  |  |             def __new__(cls, name, bases, ns): | 
					
						
							|  |  |  |                 self = super().__new__(cls, name, bases, ns) | 
					
						
							|  |  |  |                 self.meta_owner = self.owner | 
					
						
							|  |  |  |                 self.meta_name = self.name | 
					
						
							|  |  |  |                 return self | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-30 14:06:15 +03:00
										 |  |  |         class A: | 
					
						
							| 
									
										
										
										
											2016-07-30 16:26:03 +10:00
										 |  |  |             def __init_subclass__(cls): | 
					
						
							|  |  |  |                 cls.owner = cls.d.owner | 
					
						
							|  |  |  |                 cls.name = cls.d.name | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         class B(A, metaclass=Meta): | 
					
						
							|  |  |  |             d = Descriptor() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.assertIs(B.owner, B) | 
					
						
							|  |  |  |         self.assertEqual(B.name, 'd') | 
					
						
							|  |  |  |         self.assertIs(B.meta_owner, B) | 
					
						
							|  |  |  |         self.assertEqual(B.name, 'd') | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-29 09:54:17 +02:00
										 |  |  |     def test_set_name_modifying_dict(self): | 
					
						
							|  |  |  |         notified = [] | 
					
						
							|  |  |  |         class Descriptor: | 
					
						
							|  |  |  |             def __set_name__(self, owner, name): | 
					
						
							|  |  |  |                 setattr(owner, name + 'x', None) | 
					
						
							|  |  |  |                 notified.append(name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         class A: | 
					
						
							|  |  |  |             a = Descriptor() | 
					
						
							|  |  |  |             b = Descriptor() | 
					
						
							|  |  |  |             c = Descriptor() | 
					
						
							|  |  |  |             d = Descriptor() | 
					
						
							|  |  |  |             e = Descriptor() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.assertCountEqual(notified, ['a', 'b', 'c', 'd', 'e']) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-30 16:26:03 +10:00
										 |  |  |     def test_errors(self): | 
					
						
							|  |  |  |         class MyMeta(type): | 
					
						
							|  |  |  |             pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         with self.assertRaises(TypeError): | 
					
						
							| 
									
										
										
										
											2016-07-30 14:06:15 +03:00
										 |  |  |             class MyClass(metaclass=MyMeta, otherarg=1): | 
					
						
							| 
									
										
										
										
											2016-07-30 16:26:03 +10:00
										 |  |  |                 pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         with self.assertRaises(TypeError): | 
					
						
							|  |  |  |             types.new_class("MyClass", (object,), | 
					
						
							|  |  |  |                             dict(metaclass=MyMeta, otherarg=1)) | 
					
						
							|  |  |  |         types.prepare_class("MyClass", (object,), | 
					
						
							|  |  |  |                             dict(metaclass=MyMeta, otherarg=1)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         class MyMeta(type): | 
					
						
							|  |  |  |             def __init__(self, name, bases, namespace, otherarg): | 
					
						
							|  |  |  |                 super().__init__(name, bases, namespace) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         with self.assertRaises(TypeError): | 
					
						
							| 
									
										
										
										
											2023-10-16 15:57:01 +01:00
										 |  |  |             class MyClass2(metaclass=MyMeta, otherarg=1): | 
					
						
							| 
									
										
										
										
											2016-07-30 16:26:03 +10:00
										 |  |  |                 pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         class MyMeta(type): | 
					
						
							|  |  |  |             def __new__(cls, name, bases, namespace, otherarg): | 
					
						
							|  |  |  |                 return super().__new__(cls, name, bases, namespace) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             def __init__(self, name, bases, namespace, otherarg): | 
					
						
							|  |  |  |                 super().__init__(name, bases, namespace) | 
					
						
							|  |  |  |                 self.otherarg = otherarg | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-16 15:57:01 +01:00
										 |  |  |         class MyClass3(metaclass=MyMeta, otherarg=1): | 
					
						
							| 
									
										
										
										
											2016-07-30 16:26:03 +10:00
										 |  |  |             pass | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-16 15:57:01 +01:00
										 |  |  |         self.assertEqual(MyClass3.otherarg, 1) | 
					
						
							| 
									
										
										
										
											2016-07-30 16:26:03 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_errors_changed_pep487(self): | 
					
						
							|  |  |  |         # These tests failed before Python 3.6, PEP 487 | 
					
						
							|  |  |  |         class MyMeta(type): | 
					
						
							|  |  |  |             def __new__(cls, name, bases, namespace): | 
					
						
							|  |  |  |                 return super().__new__(cls, name=name, bases=bases, | 
					
						
							|  |  |  |                                        dict=namespace) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         with self.assertRaises(TypeError): | 
					
						
							| 
									
										
										
										
											2016-07-30 14:06:15 +03:00
										 |  |  |             class MyClass(metaclass=MyMeta): | 
					
						
							| 
									
										
										
										
											2016-07-30 16:26:03 +10:00
										 |  |  |                 pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         class MyMeta(type): | 
					
						
							|  |  |  |             def __new__(cls, name, bases, namespace, otherarg): | 
					
						
							|  |  |  |                 self = super().__new__(cls, name, bases, namespace) | 
					
						
							|  |  |  |                 self.otherarg = otherarg | 
					
						
							|  |  |  |                 return self | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-16 15:57:01 +01:00
										 |  |  |         class MyClass2(metaclass=MyMeta, otherarg=1): | 
					
						
							| 
									
										
										
										
											2016-07-30 16:26:03 +10:00
										 |  |  |             pass | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-16 15:57:01 +01:00
										 |  |  |         self.assertEqual(MyClass2.otherarg, 1) | 
					
						
							| 
									
										
										
										
											2016-07-30 16:26:03 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_type(self): | 
					
						
							|  |  |  |         t = type('NewClass', (object,), {}) | 
					
						
							|  |  |  |         self.assertIsInstance(t, type) | 
					
						
							|  |  |  |         self.assertEqual(t.__name__, 'NewClass') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         with self.assertRaises(TypeError): | 
					
						
							|  |  |  |             type(name='NewClass', bases=(object,), dict={}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if __name__ == "__main__": | 
					
						
							| 
									
										
										
										
											2016-07-30 14:06:15 +03:00
										 |  |  |     unittest.main() |