mirror of
https://github.com/python/cpython.git
synced 2025-09-27 10:50:04 +00:00
Allow usage of SomeABC.register as a class decorator. Patch by Edoardo Spadolini (#10868).
This commit is contained in:
parent
5390d00cc6
commit
6c3787cb70
5 changed files with 36 additions and 3 deletions
|
@ -55,6 +55,9 @@ This module provides the following class:
|
||||||
assert issubclass(tuple, MyABC)
|
assert issubclass(tuple, MyABC)
|
||||||
assert isinstance((), 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:
|
You can also override this method in an abstract base class:
|
||||||
|
|
||||||
.. method:: __subclasshook__(subclass)
|
.. method:: __subclasshook__(subclass)
|
||||||
|
|
|
@ -133,11 +133,14 @@ class ABCMeta(type):
|
||||||
return cls
|
return cls
|
||||||
|
|
||||||
def register(cls, subclass):
|
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):
|
if not isinstance(subclass, type):
|
||||||
raise TypeError("Can only register classes")
|
raise TypeError("Can only register classes")
|
||||||
if issubclass(subclass, cls):
|
if issubclass(subclass, cls):
|
||||||
return # Already a subclass
|
return subclass # Already a subclass
|
||||||
# Subtle: test for cycles *after* testing for "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.
|
# this means we allow X.register(X) and interpret it as a no-op.
|
||||||
if issubclass(cls, subclass):
|
if issubclass(cls, subclass):
|
||||||
|
@ -145,6 +148,7 @@ class ABCMeta(type):
|
||||||
raise RuntimeError("Refusing to create an inheritance cycle")
|
raise RuntimeError("Refusing to create an inheritance cycle")
|
||||||
cls._abc_registry.add(subclass)
|
cls._abc_registry.add(subclass)
|
||||||
ABCMeta._abc_invalidation_counter += 1 # Invalidate negative cache
|
ABCMeta._abc_invalidation_counter += 1 # Invalidate negative cache
|
||||||
|
return subclass
|
||||||
|
|
||||||
def _dump_registry(cls, file=None):
|
def _dump_registry(cls, file=None):
|
||||||
"""Debug helper to print the ABC registry."""
|
"""Debug helper to print the ABC registry."""
|
||||||
|
|
|
@ -121,11 +121,12 @@ class TestABC(unittest.TestCase):
|
||||||
self.assertFalse(issubclass(B, (A,)))
|
self.assertFalse(issubclass(B, (A,)))
|
||||||
self.assertNotIsInstance(b, A)
|
self.assertNotIsInstance(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.assertTrue(issubclass(B, (A,)))
|
self.assertTrue(issubclass(B, (A,)))
|
||||||
self.assertIsInstance(b, A)
|
self.assertIsInstance(b, A)
|
||||||
self.assertIsInstance(b, (A,))
|
self.assertIsInstance(b, (A,))
|
||||||
|
self.assertIs(B1, B)
|
||||||
class C(B):
|
class C(B):
|
||||||
pass
|
pass
|
||||||
c = C()
|
c = C()
|
||||||
|
@ -134,6 +135,27 @@ class TestABC(unittest.TestCase):
|
||||||
self.assertIsInstance(c, A)
|
self.assertIsInstance(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()
|
||||||
|
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):
|
def test_isinstance_invalidation(self):
|
||||||
class A(metaclass=abc.ABCMeta):
|
class A(metaclass=abc.ABCMeta):
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -798,6 +798,7 @@ Rafal Smotrzyk
|
||||||
Dirk Soede
|
Dirk Soede
|
||||||
Paul Sokolovsky
|
Paul Sokolovsky
|
||||||
Cody Somerville
|
Cody Somerville
|
||||||
|
Edoardo Spadolini
|
||||||
Clay Spence
|
Clay Spence
|
||||||
Per Spilling
|
Per Spilling
|
||||||
Joshua Spoerri
|
Joshua Spoerri
|
||||||
|
|
|
@ -30,6 +30,9 @@ Core and Builtins
|
||||||
Library
|
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
|
- Issue #11224: Fixed a regression in tarfile that affected the file-like
|
||||||
objects returned by TarFile.extractfile() regarding performance, memory
|
objects returned by TarFile.extractfile() regarding performance, memory
|
||||||
consumption and failures with the stream interface.
|
consumption and failures with the stream interface.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue