diff options
author | Ben Avrahami <avrahami.ben@gmail.com> | 2020-10-06 17:40:50 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-10-06 17:40:50 (GMT) |
commit | bef7d299eb911086ea5a7ccf7a9da337e38a8491 (patch) | |
tree | 25508f320dada76441df02c0ce5b756608524b39 /Lib/test | |
parent | a8bf44d04915f7366d9f8dfbf84822ac37a4bab3 (diff) | |
download | cpython-bef7d299eb911086ea5a7ccf7a9da337e38a8491.zip cpython-bef7d299eb911086ea5a7ccf7a9da337e38a8491.tar.gz cpython-bef7d299eb911086ea5a7ccf7a9da337e38a8491.tar.bz2 |
bpo-41905: Add abc.update_abstractmethods() (GH-22485)
This function recomputes `cls.__abstractmethods__`.
Also update `@dataclass` to use it.
Diffstat (limited to 'Lib/test')
-rw-r--r-- | Lib/test/test_abc.py | 149 | ||||
-rw-r--r-- | Lib/test/test_dataclasses.py | 37 |
2 files changed, 186 insertions, 0 deletions
diff --git a/Lib/test/test_abc.py b/Lib/test/test_abc.py index 7e9c47b..3d603e7 100644 --- a/Lib/test/test_abc.py +++ b/Lib/test/test_abc.py @@ -488,6 +488,155 @@ def test_factory(abc_ABCMeta, abc_get_cache_token): pass self.assertEqual(C.__class__, abc_ABCMeta) + def test_update_del(self): + class A(metaclass=abc_ABCMeta): + @abc.abstractmethod + def foo(self): + pass + + del A.foo + self.assertEqual(A.__abstractmethods__, {'foo'}) + self.assertFalse(hasattr(A, 'foo')) + + abc.update_abstractmethods(A) + + self.assertEqual(A.__abstractmethods__, set()) + A() + + + def test_update_new_abstractmethods(self): + class A(metaclass=abc_ABCMeta): + @abc.abstractmethod + def bar(self): + pass + + @abc.abstractmethod + def updated_foo(self): + pass + + A.foo = updated_foo + abc.update_abstractmethods(A) + self.assertEqual(A.__abstractmethods__, {'foo', 'bar'}) + msg = "class A with abstract methods bar, foo" + self.assertRaisesRegex(TypeError, msg, A) + + def test_update_implementation(self): + class A(metaclass=abc_ABCMeta): + @abc.abstractmethod + def foo(self): + pass + + class B(A): + pass + + msg = "class B with abstract method foo" + self.assertRaisesRegex(TypeError, msg, B) + self.assertEqual(B.__abstractmethods__, {'foo'}) + + B.foo = lambda self: None + + abc.update_abstractmethods(B) + + B() + self.assertEqual(B.__abstractmethods__, set()) + + def test_update_as_decorator(self): + class A(metaclass=abc_ABCMeta): + @abc.abstractmethod + def foo(self): + pass + + def class_decorator(cls): + cls.foo = lambda self: None + return cls + + @abc.update_abstractmethods + @class_decorator + class B(A): + pass + + B() + self.assertEqual(B.__abstractmethods__, set()) + + def test_update_non_abc(self): + class A: + pass + + @abc.abstractmethod + def updated_foo(self): + pass + + A.foo = updated_foo + abc.update_abstractmethods(A) + A() + self.assertFalse(hasattr(A, '__abstractmethods__')) + + def test_update_del_implementation(self): + class A(metaclass=abc_ABCMeta): + @abc.abstractmethod + def foo(self): + pass + + class B(A): + def foo(self): + pass + + B() + + del B.foo + + abc.update_abstractmethods(B) + + msg = "class B with abstract method foo" + self.assertRaisesRegex(TypeError, msg, B) + + def test_update_layered_implementation(self): + class A(metaclass=abc_ABCMeta): + @abc.abstractmethod + def foo(self): + pass + + class B(A): + pass + + class C(B): + def foo(self): + pass + + C() + + del C.foo + + abc.update_abstractmethods(C) + + msg = "class C with abstract method foo" + self.assertRaisesRegex(TypeError, msg, C) + + def test_update_multi_inheritance(self): + class A(metaclass=abc_ABCMeta): + @abc.abstractmethod + def foo(self): + pass + + class B(metaclass=abc_ABCMeta): + def foo(self): + pass + + class C(B, A): + @abc.abstractmethod + def foo(self): + pass + + self.assertEqual(C.__abstractmethods__, {'foo'}) + + del C.foo + + abc.update_abstractmethods(C) + + self.assertEqual(C.__abstractmethods__, set()) + + C() + class TestABCWithInitSubclass(unittest.TestCase): def test_works_with_init_subclass(self): diff --git a/Lib/test/test_dataclasses.py b/Lib/test/test_dataclasses.py index b20103b..b31a469 100644 --- a/Lib/test/test_dataclasses.py +++ b/Lib/test/test_dataclasses.py @@ -4,6 +4,7 @@ from dataclasses import * +import abc import pickle import inspect import builtins @@ -3332,6 +3333,42 @@ class TestReplace(unittest.TestCase): ## replace(c, x=5) +class TestAbstract(unittest.TestCase): + def test_abc_implementation(self): + class Ordered(abc.ABC): + @abc.abstractmethod + def __lt__(self, other): + pass + + @abc.abstractmethod + def __le__(self, other): + pass + + @dataclass(order=True) + class Date(Ordered): + year: int + month: 'Month' + day: 'int' + + self.assertFalse(inspect.isabstract(Date)) + self.assertGreater(Date(2020,12,25), Date(2020,8,31)) + + def test_maintain_abc(self): + class A(abc.ABC): + @abc.abstractmethod + def foo(self): + pass + + @dataclass + class Date(A): + year: int + month: 'Month' + day: 'int' + + self.assertTrue(inspect.isabstract(Date)) + msg = 'class Date with abstract method foo' + self.assertRaisesRegex(TypeError, msg, Date) + if __name__ == '__main__': unittest.main() |