summaryrefslogtreecommitdiffstats
path: root/Lib/test
diff options
context:
space:
mode:
authorBen Avrahami <avrahami.ben@gmail.com>2020-10-06 17:40:50 (GMT)
committerGitHub <noreply@github.com>2020-10-06 17:40:50 (GMT)
commitbef7d299eb911086ea5a7ccf7a9da337e38a8491 (patch)
tree25508f320dada76441df02c0ce5b756608524b39 /Lib/test
parenta8bf44d04915f7366d9f8dfbf84822ac37a4bab3 (diff)
downloadcpython-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.py149
-rw-r--r--Lib/test/test_dataclasses.py37
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()