Viewing file: test_subclassinit.py (8.04 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
import types import unittest
class Test(unittest.TestCase): def test_init_subclass(self): class A: 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): class A(dict): 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): class A: 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): class A: def __init_subclass__(cls): raise RuntimeError
with self.assertRaises(RuntimeError): class B(A): pass
def test_init_subclass_wrong(self): class A: def __init_subclass__(cls, whatever): pass
with self.assertRaises(TypeError): class B(A): pass
def test_init_subclass_skipped(self): class BaseWithInit: 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): class Base: def __init_subclass__(cls, **kwargs): super().__init_subclass__(**kwargs) cls.calls = []
class Left(Base): pass
class Middle: 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
class A: 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
class Descriptor: def __set_name__(self, owner, name): self.owner = owner self.name = name
class A(metaclass=Meta): d = Descriptor() self.assertEqual(A, 0)
def test_set_name_error(self): class Descriptor: def __set_name__(self, owner, name): 1/0
with self.assertRaises(ZeroDivisionError) as cm: class NotGoingToWork: attr = Descriptor()
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')
def test_set_name_wrong(self): class Descriptor: def __set_name__(self): pass
with self.assertRaises(TypeError) as cm: class NotGoingToWork: attr = Descriptor()
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')
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')
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
class A: 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')
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'])
def test_errors(self): class MyMeta(type): pass
with self.assertRaises(TypeError): class MyClass(metaclass=MyMeta, otherarg=1): 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): class MyClass2(metaclass=MyMeta, otherarg=1): 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
class MyClass3(metaclass=MyMeta, otherarg=1): pass
self.assertEqual(MyClass3.otherarg, 1)
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): class MyClass(metaclass=MyMeta): pass
class MyMeta(type): def __new__(cls, name, bases, namespace, otherarg): self = super().__new__(cls, name, bases, namespace) self.otherarg = otherarg return self
class MyClass2(metaclass=MyMeta, otherarg=1): pass
self.assertEqual(MyClass2.otherarg, 1)
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__": unittest.main()
|