287 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			287 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| import unittest
 | |
| from test import support
 | |
| 
 | |
| 
 | |
| class TestMROEntry(unittest.TestCase):
 | |
|     def test_mro_entry_signature(self):
 | |
|         tested = []
 | |
|         class B: ...
 | |
|         class C:
 | |
|             def __mro_entries__(self, *args, **kwargs):
 | |
|                 tested.extend([args, kwargs])
 | |
|                 return (C,)
 | |
|         c = C()
 | |
|         self.assertEqual(tested, [])
 | |
|         class D(B, c): ...
 | |
|         self.assertEqual(tested[0], ((B, c),))
 | |
|         self.assertEqual(tested[1], {})
 | |
| 
 | |
|     def test_mro_entry(self):
 | |
|         tested = []
 | |
|         class A: ...
 | |
|         class B: ...
 | |
|         class C:
 | |
|             def __mro_entries__(self, bases):
 | |
|                 tested.append(bases)
 | |
|                 return (self.__class__,)
 | |
|         c = C()
 | |
|         self.assertEqual(tested, [])
 | |
|         class D(A, c, B): ...
 | |
|         self.assertEqual(tested[-1], (A, c, B))
 | |
|         self.assertEqual(D.__bases__, (A, C, B))
 | |
|         self.assertEqual(D.__orig_bases__, (A, c, B))
 | |
|         self.assertEqual(D.__mro__, (D, A, C, B, object))
 | |
|         d = D()
 | |
|         class E(d): ...
 | |
|         self.assertEqual(tested[-1], (d,))
 | |
|         self.assertEqual(E.__bases__, (D,))
 | |
| 
 | |
|     def test_mro_entry_none(self):
 | |
|         tested = []
 | |
|         class A: ...
 | |
|         class B: ...
 | |
|         class C:
 | |
|             def __mro_entries__(self, bases):
 | |
|                 tested.append(bases)
 | |
|                 return ()
 | |
|         c = C()
 | |
|         self.assertEqual(tested, [])
 | |
|         class D(A, c, B): ...
 | |
|         self.assertEqual(tested[-1], (A, c, B))
 | |
|         self.assertEqual(D.__bases__, (A, B))
 | |
|         self.assertEqual(D.__orig_bases__, (A, c, B))
 | |
|         self.assertEqual(D.__mro__, (D, A, B, object))
 | |
|         class E(c): ...
 | |
|         self.assertEqual(tested[-1], (c,))
 | |
|         self.assertEqual(E.__bases__, (object,))
 | |
|         self.assertEqual(E.__orig_bases__, (c,))
 | |
|         self.assertEqual(E.__mro__, (E, object))
 | |
| 
 | |
|     def test_mro_entry_with_builtins(self):
 | |
|         tested = []
 | |
|         class A: ...
 | |
|         class C:
 | |
|             def __mro_entries__(self, bases):
 | |
|                 tested.append(bases)
 | |
|                 return (dict,)
 | |
|         c = C()
 | |
|         self.assertEqual(tested, [])
 | |
|         class D(A, c): ...
 | |
|         self.assertEqual(tested[-1], (A, c))
 | |
|         self.assertEqual(D.__bases__, (A, dict))
 | |
|         self.assertEqual(D.__orig_bases__, (A, c))
 | |
|         self.assertEqual(D.__mro__, (D, A, dict, object))
 | |
| 
 | |
|     def test_mro_entry_with_builtins_2(self):
 | |
|         tested = []
 | |
|         class C:
 | |
|             def __mro_entries__(self, bases):
 | |
|                 tested.append(bases)
 | |
|                 return (C,)
 | |
|         c = C()
 | |
|         self.assertEqual(tested, [])
 | |
|         class D(c, dict): ...
 | |
|         self.assertEqual(tested[-1], (c, dict))
 | |
|         self.assertEqual(D.__bases__, (C, dict))
 | |
|         self.assertEqual(D.__orig_bases__, (c, dict))
 | |
|         self.assertEqual(D.__mro__, (D, C, dict, object))
 | |
| 
 | |
|     def test_mro_entry_errors(self):
 | |
|         class C_too_many:
 | |
|             def __mro_entries__(self, bases, something, other):
 | |
|                 return ()
 | |
|         c = C_too_many()
 | |
|         with self.assertRaises(TypeError):
 | |
|             class D(c): ...
 | |
|         class C_too_few:
 | |
|             def __mro_entries__(self):
 | |
|                 return ()
 | |
|         d = C_too_few()
 | |
|         with self.assertRaises(TypeError):
 | |
|             class D(d): ...
 | |
| 
 | |
|     def test_mro_entry_errors_2(self):
 | |
|         class C_not_callable:
 | |
|             __mro_entries__ = "Surprise!"
 | |
|         c = C_not_callable()
 | |
|         with self.assertRaises(TypeError):
 | |
|             class D(c): ...
 | |
|         class C_not_tuple:
 | |
|             def __mro_entries__(self):
 | |
|                 return object
 | |
|         c = C_not_tuple()
 | |
|         with self.assertRaises(TypeError):
 | |
|             class D(c): ...
 | |
| 
 | |
|     def test_mro_entry_metaclass(self):
 | |
|         meta_args = []
 | |
|         class Meta(type):
 | |
|             def __new__(mcls, name, bases, ns):
 | |
|                 meta_args.extend([mcls, name, bases, ns])
 | |
|                 return super().__new__(mcls, name, bases, ns)
 | |
|         class A: ...
 | |
|         class C:
 | |
|             def __mro_entries__(self, bases):
 | |
|                 return (A,)
 | |
|         c = C()
 | |
|         class D(c, metaclass=Meta):
 | |
|             x = 1
 | |
|         self.assertEqual(meta_args[0], Meta)
 | |
|         self.assertEqual(meta_args[1], 'D')
 | |
|         self.assertEqual(meta_args[2], (A,))
 | |
|         self.assertEqual(meta_args[3]['x'], 1)
 | |
|         self.assertEqual(D.__bases__, (A,))
 | |
|         self.assertEqual(D.__orig_bases__, (c,))
 | |
|         self.assertEqual(D.__mro__, (D, A, object))
 | |
|         self.assertEqual(D.__class__, Meta)
 | |
| 
 | |
|     def test_mro_entry_type_call(self):
 | |
|         # Substitution should _not_ happen in direct type call
 | |
|         class C:
 | |
|             def __mro_entries__(self, bases):
 | |
|                 return ()
 | |
|         c = C()
 | |
|         with self.assertRaisesRegex(TypeError,
 | |
|                                     "MRO entry resolution; "
 | |
|                                     "use types.new_class()"):
 | |
|             type('Bad', (c,), {})
 | |
| 
 | |
| 
 | |
| class TestClassGetitem(unittest.TestCase):
 | |
|     def test_class_getitem(self):
 | |
|         getitem_args = []
 | |
|         class C:
 | |
|             def __class_getitem__(*args, **kwargs):
 | |
|                 getitem_args.extend([args, kwargs])
 | |
|                 return None
 | |
|         C[int, str]
 | |
|         self.assertEqual(getitem_args[0], (C, (int, str)))
 | |
|         self.assertEqual(getitem_args[1], {})
 | |
| 
 | |
|     def test_class_getitem_format(self):
 | |
|         class C:
 | |
|             def __class_getitem__(cls, item):
 | |
|                 return f'C[{item.__name__}]'
 | |
|         self.assertEqual(C[int], 'C[int]')
 | |
|         self.assertEqual(C[C], 'C[C]')
 | |
| 
 | |
|     def test_class_getitem_inheritance(self):
 | |
|         class C:
 | |
|             def __class_getitem__(cls, item):
 | |
|                 return f'{cls.__name__}[{item.__name__}]'
 | |
|         class D(C): ...
 | |
|         self.assertEqual(D[int], 'D[int]')
 | |
|         self.assertEqual(D[D], 'D[D]')
 | |
| 
 | |
|     def test_class_getitem_inheritance_2(self):
 | |
|         class C:
 | |
|             def __class_getitem__(cls, item):
 | |
|                 return 'Should not see this'
 | |
|         class D(C):
 | |
|             def __class_getitem__(cls, item):
 | |
|                 return f'{cls.__name__}[{item.__name__}]'
 | |
|         self.assertEqual(D[int], 'D[int]')
 | |
|         self.assertEqual(D[D], 'D[D]')
 | |
| 
 | |
|     def test_class_getitem_classmethod(self):
 | |
|         class C:
 | |
|             @classmethod
 | |
|             def __class_getitem__(cls, item):
 | |
|                 return f'{cls.__name__}[{item.__name__}]'
 | |
|         class D(C): ...
 | |
|         self.assertEqual(D[int], 'D[int]')
 | |
|         self.assertEqual(D[D], 'D[D]')
 | |
| 
 | |
|     def test_class_getitem_patched(self):
 | |
|         class C:
 | |
|             def __init_subclass__(cls):
 | |
|                 def __class_getitem__(cls, item):
 | |
|                     return f'{cls.__name__}[{item.__name__}]'
 | |
|                 cls.__class_getitem__ = classmethod(__class_getitem__)
 | |
|         class D(C): ...
 | |
|         self.assertEqual(D[int], 'D[int]')
 | |
|         self.assertEqual(D[D], 'D[D]')
 | |
| 
 | |
|     def test_class_getitem_with_builtins(self):
 | |
|         class A(dict):
 | |
|             called_with = None
 | |
| 
 | |
|             def __class_getitem__(cls, item):
 | |
|                 cls.called_with = item
 | |
|         class B(A):
 | |
|             pass
 | |
|         self.assertIs(B.called_with, None)
 | |
|         B[int]
 | |
|         self.assertIs(B.called_with, int)
 | |
| 
 | |
|     def test_class_getitem_errors(self):
 | |
|         class C_too_few:
 | |
|             def __class_getitem__(cls):
 | |
|                 return None
 | |
|         with self.assertRaises(TypeError):
 | |
|             C_too_few[int]
 | |
|         class C_too_many:
 | |
|             def __class_getitem__(cls, one, two):
 | |
|                 return None
 | |
|         with self.assertRaises(TypeError):
 | |
|             C_too_many[int]
 | |
| 
 | |
|     def test_class_getitem_errors_2(self):
 | |
|         class C:
 | |
|             def __class_getitem__(cls, item):
 | |
|                 return None
 | |
|         with self.assertRaises(TypeError):
 | |
|             C()[int]
 | |
|         class E: ...
 | |
|         e = E()
 | |
|         e.__class_getitem__ = lambda cls, item: 'This will not work'
 | |
|         with self.assertRaises(TypeError):
 | |
|             e[int]
 | |
|         class C_not_callable:
 | |
|             __class_getitem__ = "Surprise!"
 | |
|         with self.assertRaises(TypeError):
 | |
|             C_not_callable[int]
 | |
| 
 | |
|     def test_class_getitem_metaclass(self):
 | |
|         class Meta(type):
 | |
|             def __class_getitem__(cls, item):
 | |
|                 return f'{cls.__name__}[{item.__name__}]'
 | |
|         self.assertEqual(Meta[int], 'Meta[int]')
 | |
| 
 | |
|     def test_class_getitem_with_metaclass(self):
 | |
|         class Meta(type): pass
 | |
|         class C(metaclass=Meta):
 | |
|             def __class_getitem__(cls, item):
 | |
|                 return f'{cls.__name__}[{item.__name__}]'
 | |
|         self.assertEqual(C[int], 'C[int]')
 | |
| 
 | |
|     def test_class_getitem_metaclass_first(self):
 | |
|         class Meta(type):
 | |
|             def __getitem__(cls, item):
 | |
|                 return 'from metaclass'
 | |
|         class C(metaclass=Meta):
 | |
|             def __class_getitem__(cls, item):
 | |
|                 return 'from __class_getitem__'
 | |
|         self.assertEqual(C[int], 'from metaclass')
 | |
| 
 | |
| 
 | |
| @support.cpython_only
 | |
| class CAPITest(unittest.TestCase):
 | |
| 
 | |
|     def test_c_class(self):
 | |
|         from _testcapi import Generic, GenericAlias
 | |
|         self.assertIsInstance(Generic.__class_getitem__(int), GenericAlias)
 | |
| 
 | |
|         IntGeneric = Generic[int]
 | |
|         self.assertIs(type(IntGeneric), GenericAlias)
 | |
|         self.assertEqual(IntGeneric.__mro_entries__(()), (int,))
 | |
|         class C(IntGeneric):
 | |
|             pass
 | |
|         self.assertEqual(C.__bases__, (int,))
 | |
|         self.assertEqual(C.__orig_bases__, (IntGeneric,))
 | |
|         self.assertEqual(C.__mro__, (C, int, object))
 | |
| 
 | |
| 
 | |
| if __name__ == "__main__":
 | |
|     unittest.main()
 |