mirror of https://github.com/python/cpython
Add collections.Reversible. Patch by Ivan Levkivskyi. Fixes issue #25987.
This commit is contained in:
parent
9ad764601b
commit
16ca06b8cb
|
@ -40,12 +40,13 @@ ABC Inherits from Abstract Methods Mixin
|
|||
:class:`Hashable` ``__hash__``
|
||||
:class:`Iterable` ``__iter__``
|
||||
:class:`Iterator` :class:`Iterable` ``__next__`` ``__iter__``
|
||||
:class:`Reversible` :class:`Iterable` ``__reversed__``
|
||||
:class:`Generator` :class:`Iterator` ``send``, ``throw`` ``close``, ``__iter__``, ``__next__``
|
||||
:class:`Sized` ``__len__``
|
||||
:class:`Callable` ``__call__``
|
||||
|
||||
:class:`Sequence` :class:`Sized`, ``__getitem__``, ``__contains__``, ``__iter__``, ``__reversed__``,
|
||||
:class:`Iterable`, ``__len__`` ``index``, and ``count``
|
||||
:class:`Reversible`, ``__len__`` ``index``, and ``count``
|
||||
:class:`Container`
|
||||
|
||||
:class:`MutableSequence` :class:`Sequence` ``__getitem__``, Inherited :class:`Sequence` methods and
|
||||
|
@ -107,6 +108,10 @@ ABC Inherits from Abstract Methods Mixin
|
|||
:meth:`~iterator.__next__` methods. See also the definition of
|
||||
:term:`iterator`.
|
||||
|
||||
.. class:: Reversible
|
||||
|
||||
ABC for classes that provide the :meth:`__reversed__` method.
|
||||
|
||||
.. class:: Generator
|
||||
|
||||
ABC for generator classes that implement the protocol defined in
|
||||
|
|
|
@ -351,6 +351,10 @@ The module defines the following classes, functions and decorators:
|
|||
|
||||
A generic version of the :class:`collections.abc.Iterator`.
|
||||
|
||||
.. class:: Reversible(Iterable[T_co])
|
||||
|
||||
A generic version of the :class:`collections.abc.Reversible`.
|
||||
|
||||
.. class:: SupportsInt
|
||||
|
||||
An ABC with one abstract method ``__int__``.
|
||||
|
@ -369,11 +373,6 @@ The module defines the following classes, functions and decorators:
|
|||
An ABC with one abstract method ``__round__``
|
||||
that is covariant in its return type.
|
||||
|
||||
.. class:: Reversible
|
||||
|
||||
An ABC with one abstract method ``__reversed__`` returning
|
||||
an ``Iterator[T_co]``.
|
||||
|
||||
.. class:: Container(Generic[T_co])
|
||||
|
||||
A generic version of :class:`collections.abc.Container`.
|
||||
|
@ -394,7 +393,7 @@ The module defines the following classes, functions and decorators:
|
|||
|
||||
A generic version of :class:`collections.abc.MutableMapping`.
|
||||
|
||||
.. class:: Sequence(Sized, Iterable[T_co], Container[T_co])
|
||||
.. class:: Sequence(Sized, Reversible[T_co], Container[T_co])
|
||||
|
||||
A generic version of :class:`collections.abc.Sequence`.
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ from abc import ABCMeta, abstractmethod
|
|||
import sys
|
||||
|
||||
__all__ = ["Awaitable", "Coroutine", "AsyncIterable", "AsyncIterator",
|
||||
"Hashable", "Iterable", "Iterator", "Generator",
|
||||
"Hashable", "Iterable", "Iterator", "Generator", "Reversible",
|
||||
"Sized", "Container", "Callable",
|
||||
"Set", "MutableSet",
|
||||
"Mapping", "MutableMapping",
|
||||
|
@ -240,6 +240,25 @@ Iterator.register(tuple_iterator)
|
|||
Iterator.register(zip_iterator)
|
||||
|
||||
|
||||
class Reversible(Iterable):
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
@abstractmethod
|
||||
def __reversed__(self):
|
||||
return NotImplemented
|
||||
|
||||
@classmethod
|
||||
def __subclasshook__(cls, C):
|
||||
if cls is Reversible:
|
||||
for B in C.__mro__:
|
||||
if "__reversed__" in B.__dict__:
|
||||
if B.__dict__["__reversed__"] is not None:
|
||||
return True
|
||||
break
|
||||
return NotImplemented
|
||||
|
||||
|
||||
class Generator(Iterator):
|
||||
|
||||
__slots__ = ()
|
||||
|
@ -794,7 +813,7 @@ MutableMapping.register(dict)
|
|||
### SEQUENCES ###
|
||||
|
||||
|
||||
class Sequence(Sized, Iterable, Container):
|
||||
class Sequence(Sized, Reversible, Container):
|
||||
|
||||
"""All the operations on a read-only sequence.
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ from collections import UserDict, UserString, UserList
|
|||
from collections import ChainMap
|
||||
from collections import deque
|
||||
from collections.abc import Awaitable, Coroutine, AsyncIterator, AsyncIterable
|
||||
from collections.abc import Hashable, Iterable, Iterator, Generator
|
||||
from collections.abc import Hashable, Iterable, Iterator, Generator, Reversible
|
||||
from collections.abc import Sized, Container, Callable
|
||||
from collections.abc import Set, MutableSet
|
||||
from collections.abc import Mapping, MutableMapping, KeysView, ItemsView
|
||||
|
@ -689,6 +689,31 @@ class TestOneTrickPonyABCs(ABCTestCase):
|
|||
self.validate_abstract_methods(Iterable, '__iter__')
|
||||
self.validate_isinstance(Iterable, '__iter__')
|
||||
|
||||
def test_Reversible(self):
|
||||
# Check some non-reversibles
|
||||
non_samples = [None, 42, 3.14, 1j, dict(), set(), frozenset()]
|
||||
for x in non_samples:
|
||||
self.assertNotIsInstance(x, Reversible)
|
||||
self.assertFalse(issubclass(type(x), Reversible), repr(type(x)))
|
||||
# Check some reversibles
|
||||
samples = [tuple(), list()]
|
||||
for x in samples:
|
||||
self.assertIsInstance(x, Reversible)
|
||||
self.assertTrue(issubclass(type(x), Reversible), repr(type(x)))
|
||||
# Check also Mapping, MutableMapping, and Sequence
|
||||
self.assertTrue(issubclass(Sequence, Reversible), repr(Sequence))
|
||||
self.assertFalse(issubclass(Mapping, Reversible), repr(Mapping))
|
||||
self.assertFalse(issubclass(MutableMapping, Reversible), repr(MutableMapping))
|
||||
# Check direct subclassing
|
||||
class R(Reversible):
|
||||
def __iter__(self):
|
||||
return iter(list())
|
||||
def __reversed__(self):
|
||||
return iter(list())
|
||||
self.assertEqual(list(reversed(R())), [])
|
||||
self.assertFalse(issubclass(float, R))
|
||||
self.validate_abstract_methods(Reversible, '__reversed__', '__iter__')
|
||||
|
||||
def test_Iterator(self):
|
||||
non_samples = [None, 42, 3.14, 1j, b"", "", (), [], {}, set()]
|
||||
for x in non_samples:
|
||||
|
@ -842,14 +867,14 @@ class TestOneTrickPonyABCs(ABCTestCase):
|
|||
self.validate_isinstance(Callable, '__call__')
|
||||
|
||||
def test_direct_subclassing(self):
|
||||
for B in Hashable, Iterable, Iterator, Sized, Container, Callable:
|
||||
for B in Hashable, Iterable, Iterator, Reversible, Sized, Container, Callable:
|
||||
class C(B):
|
||||
pass
|
||||
self.assertTrue(issubclass(C, B))
|
||||
self.assertFalse(issubclass(int, C))
|
||||
|
||||
def test_registration(self):
|
||||
for B in Hashable, Iterable, Iterator, Sized, Container, Callable:
|
||||
for B in Hashable, Iterable, Iterator, Reversible, Sized, Container, Callable:
|
||||
class C:
|
||||
__hash__ = None # Make sure it isn't hashable by default
|
||||
self.assertFalse(issubclass(C, B), B.__name__)
|
||||
|
|
|
@ -1516,7 +1516,7 @@ class TestSingleDispatch(unittest.TestCase):
|
|||
m = mro(D, bases)
|
||||
self.assertEqual(m, [D, c.MutableSequence, c.Sequence,
|
||||
c.defaultdict, dict, c.MutableMapping,
|
||||
c.Mapping, c.Sized, c.Iterable, c.Container,
|
||||
c.Mapping, c.Sized, c.Reversible, c.Iterable, c.Container,
|
||||
object])
|
||||
|
||||
# Container and Callable are registered on different base classes and
|
||||
|
|
Loading…
Reference in New Issue