Allow usage of SomeABC.register as a class decorator. Patch by Edoardo Spadolini (#10868).

This commit is contained in:
Éric Araujo 2011-02-24 18:03:10 +00:00
parent 5390d00cc6
commit 6c3787cb70
5 changed files with 36 additions and 3 deletions

View File

@ -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)

View File

@ -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."""

View File

@ -121,11 +121,12 @@ 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()
@ -134,6 +135,27 @@ class TestABC(unittest.TestCase):
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()
self.assertTrue(issubclass(C, A))
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):
pass

View File

@ -798,6 +798,7 @@ Rafal Smotrzyk
Dirk Soede
Paul Sokolovsky
Cody Somerville
Edoardo Spadolini
Clay Spence
Per Spilling
Joshua Spoerri

View File

@ -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.