From 6c3787cb70d8efa5f70939a5db300f56a724d698 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Thu, 24 Feb 2011 18:03:10 +0000 Subject: Allow usage of SomeABC.register as a class decorator. Patch by Edoardo Spadolini (#10868). --- Doc/library/abc.rst | 3 +++ Lib/abc.py | 8 ++++++-- Lib/test/test_abc.py | 24 +++++++++++++++++++++++- Misc/ACKS | 1 + Misc/NEWS | 3 +++ 5 files changed, 36 insertions(+), 3 deletions(-) diff --git a/Doc/library/abc.rst b/Doc/library/abc.rst index 9fadbd2..2eced45 100644 --- a/Doc/library/abc.rst +++ b/Doc/library/abc.rst @@ -55,6 +55,9 @@ This module provides the following class: assert issubclass(tuple, MyABC) assert isinstance((), MyABC) + .. versionchanged:: 3.3 + Returns the registered subclass, to allow usage as a class decorator. + You can also override this method in an abstract base class: .. method:: __subclasshook__(subclass) diff --git a/Lib/abc.py b/Lib/abc.py index a6c2dc4..40f88b9 100644 --- a/Lib/abc.py +++ b/Lib/abc.py @@ -133,11 +133,14 @@ class ABCMeta(type): return cls def register(cls, subclass): - """Register a virtual subclass of an ABC.""" + """Register a virtual subclass of an ABC. + + Returns the subclass, to allow usage as a class decorator. + """ if not isinstance(subclass, type): raise TypeError("Can only register classes") if issubclass(subclass, cls): - return # Already a subclass + return subclass # Already a subclass # Subtle: test for cycles *after* testing for "already a subclass"; # this means we allow X.register(X) and interpret it as a no-op. if issubclass(cls, subclass): @@ -145,6 +148,7 @@ class ABCMeta(type): raise RuntimeError("Refusing to create an inheritance cycle") cls._abc_registry.add(subclass) ABCMeta._abc_invalidation_counter += 1 # Invalidate negative cache + return subclass def _dump_registry(cls, file=None): """Debug helper to print the ABC registry.""" diff --git a/Lib/test/test_abc.py b/Lib/test/test_abc.py index d86f97c..1319a64 100644 --- a/Lib/test/test_abc.py +++ b/Lib/test/test_abc.py @@ -121,11 +121,32 @@ class TestABC(unittest.TestCase): self.assertFalse(issubclass(B, (A,))) self.assertNotIsInstance(b, A) self.assertNotIsInstance(b, (A,)) - A.register(B) + B1 = A.register(B) + self.assertTrue(issubclass(B, A)) + self.assertTrue(issubclass(B, (A,))) + self.assertIsInstance(b, A) + self.assertIsInstance(b, (A,)) + self.assertIs(B1, B) + class C(B): + pass + c = C() + self.assertTrue(issubclass(C, A)) + self.assertTrue(issubclass(C, (A,))) + self.assertIsInstance(c, A) + self.assertIsInstance(c, (A,)) + + def test_register_as_class_deco(self): + class A(metaclass=abc.ABCMeta): + pass + @A.register + class B(object): + pass + b = B() self.assertTrue(issubclass(B, A)) self.assertTrue(issubclass(B, (A,))) self.assertIsInstance(b, A) self.assertIsInstance(b, (A,)) + @A.register class C(B): pass c = C() @@ -133,6 +154,7 @@ class TestABC(unittest.TestCase): self.assertTrue(issubclass(C, (A,))) self.assertIsInstance(c, A) self.assertIsInstance(c, (A,)) + self.assertIs(C, A.register(C)) def test_isinstance_invalidation(self): class A(metaclass=abc.ABCMeta): diff --git a/Misc/ACKS b/Misc/ACKS index c22e360..8066ebc 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -798,6 +798,7 @@ Rafal Smotrzyk Dirk Soede Paul Sokolovsky Cody Somerville +Edoardo Spadolini Clay Spence Per Spilling Joshua Spoerri diff --git a/Misc/NEWS b/Misc/NEWS index 91a727a..6df1ace 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -30,6 +30,9 @@ Core and Builtins Library ------- +- Issue #10868: Allow usage of the register method of an ABC as a class + decorator. + - Issue #11224: Fixed a regression in tarfile that affected the file-like objects returned by TarFile.extractfile() regarding performance, memory consumption and failures with the stream interface. -- cgit v0.12