mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 07:31:38 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			283 lines
		
	
	
	
		
			8.1 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			283 lines
		
	
	
	
		
			8.1 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
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(RuntimeError) as cm:
 | 
						|
            class NotGoingToWork:
 | 
						|
                attr = Descriptor()
 | 
						|
 | 
						|
        exc = cm.exception
 | 
						|
        self.assertRegex(str(exc), r'\bNotGoingToWork\b')
 | 
						|
        self.assertRegex(str(exc), r'\battr\b')
 | 
						|
        self.assertRegex(str(exc), r'\bDescriptor\b')
 | 
						|
        self.assertIsInstance(exc.__cause__, ZeroDivisionError)
 | 
						|
 | 
						|
    def test_set_name_wrong(self):
 | 
						|
        class Descriptor:
 | 
						|
            def __set_name__(self):
 | 
						|
                pass
 | 
						|
 | 
						|
        with self.assertRaises(RuntimeError) as cm:
 | 
						|
            class NotGoingToWork:
 | 
						|
                attr = Descriptor()
 | 
						|
 | 
						|
        exc = cm.exception
 | 
						|
        self.assertRegex(str(exc), r'\bNotGoingToWork\b')
 | 
						|
        self.assertRegex(str(exc), r'\battr\b')
 | 
						|
        self.assertRegex(str(exc), r'\bDescriptor\b')
 | 
						|
        self.assertIsInstance(exc.__cause__, TypeError)
 | 
						|
 | 
						|
    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 MyClass(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 MyClass(metaclass=MyMeta, otherarg=1):
 | 
						|
            pass
 | 
						|
 | 
						|
        self.assertEqual(MyClass.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 MyClass(metaclass=MyMeta, otherarg=1):
 | 
						|
            pass
 | 
						|
 | 
						|
        self.assertEqual(MyClass.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()
 |