Home | History | Annotate | Download | only in test
      1 import types
      2 import unittest
      3 
      4 
      5 class 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 
    282 if __name__ == "__main__":
    283     unittest.main()
    284