| import unittest |
| |
| |
| 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(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_patched(self): |
| class C: |
| def __init_subclass__(cls): |
| def __class_getitem__(cls, item): |
| return f'{cls.__name__}[{item.__name__}]' |
| cls.__class_getitem__ = __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_metaclass_2(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') |
| |
| |
| if __name__ == "__main__": |
| unittest.main() |