[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