[Python-checkins] cpython: Add collections.Reversible. Patch by Ivan Levkivskyi. Fixes issue #25987.
guido.van.rossum
python-checkins at python.org
Mon Apr 4 14:04:21 EDT 2016
https://hg.python.org/cpython/rev/07f73360ea8e
changeset: 100846:07f73360ea8e
parent: 100844:0e576d094dc4
user: Guido van Rossum <guido at dropbox.com>
date: Mon Apr 04 10:59:29 2016 -0700
summary:
Add collections.Reversible. Patch by Ivan Levkivskyi. Fixes issue #25987.
files:
Doc/library/collections.abc.rst | 7 ++++-
Doc/library/typing.rst | 11 +++----
Lib/_collections_abc.py | 23 ++++++++++++++-
Lib/test/test_collections.py | 31 ++++++++++++++++++--
Lib/test/test_functools.py | 2 +-
5 files changed, 61 insertions(+), 13 deletions(-)
diff --git a/Doc/library/collections.abc.rst b/Doc/library/collections.abc.rst
--- a/Doc/library/collections.abc.rst
+++ b/Doc/library/collections.abc.rst
@@ -40,12 +40,13 @@
: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 @@
: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
diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst
--- a/Doc/library/typing.rst
+++ b/Doc/library/typing.rst
@@ -351,6 +351,10 @@
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 @@
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 @@
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`.
diff --git a/Lib/_collections_abc.py b/Lib/_collections_abc.py
--- a/Lib/_collections_abc.py
+++ b/Lib/_collections_abc.py
@@ -10,7 +10,7 @@
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(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 @@
### SEQUENCES ###
-class Sequence(Sized, Iterable, Container):
+class Sequence(Sized, Reversible, Container):
"""All the operations on a read-only sequence.
diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py
--- a/Lib/test/test_collections.py
+++ b/Lib/test/test_collections.py
@@ -20,7 +20,7 @@
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 @@
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 @@
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__)
diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py
--- a/Lib/test/test_functools.py
+++ b/Lib/test/test_functools.py
@@ -1516,7 +1516,7 @@
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
--
Repository URL: https://hg.python.org/cpython
More information about the Python-checkins
mailing list