[Python-checkins] cpython: Issue 27598: Add Collections to collections.abc.

guido.van.rossum python-checkins at python.org
Tue Aug 23 13:47:22 EDT 2016


https://hg.python.org/cpython/rev/acd9a465b373
changeset:   102866:acd9a465b373
user:        Guido van Rossum <guido at dropbox.com>
date:        Tue Aug 23 10:47:07 2016 -0700
summary:
  Issue 27598: Add Collections to collections.abc.

Patch by Ivan Levkivskyi, docs by Neil Girdhar.

files:
  Doc/library/collections.abc.rst |  26 +++--
  Lib/_collections_abc.py         |  17 +++-
  Lib/test/test_collections.py    |  90 ++++++++++++++++++++-
  Lib/test/test_functools.py      |  17 ++-
  Misc/NEWS                       |   3 +
  5 files changed, 132 insertions(+), 21 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
@@ -45,10 +45,12 @@
 :class:`Generator`         :class:`Iterator`      ``send``, ``throw``     ``close``, ``__iter__``, ``__next__``
 :class:`Sized`                                    ``__len__``
 :class:`Callable`                                 ``__call__``
+:class:`Collection`        :class:`Sized`,        ``__contains__``,
+                           :class:`Iterable`,     ``__iter__``,
+                           :class:`Container`     ``__len__``
 
-:class:`Sequence`          :class:`Sized`,        ``__getitem__``,        ``__contains__``, ``__iter__``, ``__reversed__``,
-                           :class:`Reversible`,   ``__len__``             ``index``, and ``count``
-                           :class:`Container`
+:class:`Sequence`          :class:`Reversible`,   ``__getitem__``,        ``__contains__``, ``__iter__``, ``__reversed__``,
+                           :class:`Collection`    ``__len__``             ``index``, and ``count``
 
 :class:`MutableSequence`   :class:`Sequence`      ``__getitem__``,        Inherited :class:`Sequence` methods and
                                                   ``__setitem__``,        ``append``, ``reverse``, ``extend``, ``pop``,
@@ -59,9 +61,9 @@
 :class:`ByteString`        :class:`Sequence`      ``__getitem__``,        Inherited :class:`Sequence` methods
                                                   ``__len__``
 
-:class:`Set`               :class:`Sized`,        ``__contains__``,       ``__le__``, ``__lt__``, ``__eq__``, ``__ne__``,
-                           :class:`Iterable`,     ``__iter__``,           ``__gt__``, ``__ge__``, ``__and__``, ``__or__``,
-                           :class:`Container`     ``__len__``             ``__sub__``, ``__xor__``, and ``isdisjoint``
+:class:`Set`               :class:`Collection`    ``__contains__``,       ``__le__``, ``__lt__``, ``__eq__``, ``__ne__``,
+                                                  ``__iter__``,           ``__gt__``, ``__ge__``, ``__and__``, ``__or__``,
+                                                  ``__len__``             ``__sub__``, ``__xor__``, and ``isdisjoint``
 
 :class:`MutableSet`        :class:`Set`           ``__contains__``,       Inherited :class:`Set` methods and
                                                   ``__iter__``,           ``clear``, ``pop``, ``remove``, ``__ior__``,
@@ -69,9 +71,9 @@
                                                   ``add``,
                                                   ``discard``
 
-:class:`Mapping`           :class:`Sized`,        ``__getitem__``,        ``__contains__``, ``keys``, ``items``, ``values``,
-                           :class:`Iterable`,     ``__iter__``,           ``get``, ``__eq__``, and ``__ne__``
-                           :class:`Container`     ``__len__``
+:class:`Mapping`           :class:`Collection`    ``__getitem__``,        ``__contains__``, ``keys``, ``items``, ``values``,
+                                                  ``__iter__``,           ``get``, ``__eq__``, and ``__ne__``
+                                                  ``__len__``
 
 :class:`MutableMapping`    :class:`Mapping`       ``__getitem__``,        Inherited :class:`Mapping` methods and
                                                   ``__setitem__``,        ``pop``, ``popitem``, ``clear``, ``update``,
@@ -106,6 +108,12 @@
    ABC for classes that provide the :meth:`__iter__` method.
    See also the definition of :term:`iterable`.
 
+.. class:: Collection
+
+   ABC for sized iterable container classes.
+
+   .. versionadded:: 3.6
+
 .. class:: Iterator
 
    ABC for classes that provide the :meth:`~iterator.__iter__` and
diff --git a/Lib/_collections_abc.py b/Lib/_collections_abc.py
--- a/Lib/_collections_abc.py
+++ b/Lib/_collections_abc.py
@@ -11,7 +11,7 @@
 
 __all__ = ["Awaitable", "Coroutine", "AsyncIterable", "AsyncIterator",
            "Hashable", "Iterable", "Iterator", "Generator", "Reversible",
-           "Sized", "Container", "Callable",
+           "Sized", "Container", "Callable", "Collection",
            "Set", "MutableSet",
            "Mapping", "MutableMapping",
            "MappingView", "KeysView", "ItemsView", "ValuesView",
@@ -326,6 +326,15 @@
             return _check_methods(C, "__contains__")
         return NotImplemented
 
+class Collection(Sized, Iterable, Container):
+
+    __slots__ = ()
+
+    @classmethod
+    def __subclasshook__(cls, C):
+        if cls is Collection:
+            return _check_methods(C,  "__len__", "__iter__", "__contains__")
+        return NotImplemented
 
 class Callable(metaclass=ABCMeta):
 
@@ -345,7 +354,7 @@
 ### SETS ###
 
 
-class Set(Sized, Iterable, Container):
+class Set(Collection):
 
     """A set is a finite, iterable container.
 
@@ -570,7 +579,7 @@
 ### MAPPINGS ###
 
 
-class Mapping(Sized, Iterable, Container):
+class Mapping(Collection):
 
     __slots__ = ()
 
@@ -794,7 +803,7 @@
 ### SEQUENCES ###
 
 
-class Sequence(Sized, Reversible, Container):
+class Sequence(Reversible, Collection):
 
     """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
@@ -21,7 +21,7 @@
 from collections import deque
 from collections.abc import Awaitable, Coroutine, AsyncIterator, AsyncIterable
 from collections.abc import Hashable, Iterable, Iterator, Generator, Reversible
-from collections.abc import Sized, Container, Callable
+from collections.abc import Sized, Container, Callable, Collection
 from collections.abc import Set, MutableSet
 from collections.abc import Mapping, MutableMapping, KeysView, ItemsView, ValuesView
 from collections.abc import Sequence, MutableSequence
@@ -771,6 +771,94 @@
         self.assertFalse(issubclass(RevRevBlocked, Reversible))
         self.assertFalse(isinstance(RevRevBlocked(), Reversible))
 
+    def test_Collection(self):
+        # Check some non-collections
+        non_collections = [None, 42, 3.14, 1j, lambda x: 2*x]
+        for x in non_collections:
+            self.assertNotIsInstance(x, Collection)
+            self.assertFalse(issubclass(type(x), Collection), repr(type(x)))
+        # Check some non-collection iterables
+        non_col_iterables = [_test_gen(), iter(b''), iter(bytearray()),
+                             (x for x in []), dict().values()]
+        for x in non_col_iterables:
+            self.assertNotIsInstance(x, Collection)
+            self.assertFalse(issubclass(type(x), Collection), repr(type(x)))
+        # Check some collections
+        samples = [set(), frozenset(), dict(), bytes(), str(), tuple(),
+                   list(), dict().keys(), dict().items()]
+        for x in samples:
+            self.assertIsInstance(x, Collection)
+            self.assertTrue(issubclass(type(x), Collection), repr(type(x)))
+        # Check also Mapping, MutableMapping, etc.
+        self.assertTrue(issubclass(Sequence, Collection), repr(Sequence))
+        self.assertTrue(issubclass(Mapping, Collection), repr(Mapping))
+        self.assertTrue(issubclass(MutableMapping, Collection),
+                                    repr(MutableMapping))
+        self.assertTrue(issubclass(Set, Collection), repr(Set))
+        self.assertTrue(issubclass(MutableSet, Collection), repr(MutableSet))
+        self.assertTrue(issubclass(Sequence, Collection), repr(MutableSet))
+        # Check direct subclassing
+        class Col(Collection):
+            def __iter__(self):
+                return iter(list())
+            def __len__(self):
+                return 0
+            def __contains__(self, item):
+                return False
+        class DerCol(Col): pass
+        self.assertEqual(list(iter(Col())), [])
+        self.assertFalse(issubclass(list, Col))
+        self.assertFalse(issubclass(set, Col))
+        self.assertFalse(issubclass(float, Col))
+        self.assertEqual(list(iter(DerCol())), [])
+        self.assertFalse(issubclass(list, DerCol))
+        self.assertFalse(issubclass(set, DerCol))
+        self.assertFalse(issubclass(float, DerCol))
+        self.validate_abstract_methods(Collection, '__len__', '__iter__',
+                                                   '__contains__')
+        # Check sized container non-iterable (which is not Collection) etc.
+        class ColNoIter:
+            def __len__(self): return 0
+            def __contains__(self, item): return False
+        class ColNoSize:
+            def __iter__(self): return iter([])
+            def __contains__(self, item): return False
+        class ColNoCont:
+            def __iter__(self): return iter([])
+            def __len__(self): return 0
+        self.assertFalse(issubclass(ColNoIter, Collection))
+        self.assertFalse(isinstance(ColNoIter(), Collection))
+        self.assertFalse(issubclass(ColNoSize, Collection))
+        self.assertFalse(isinstance(ColNoSize(), Collection))
+        self.assertFalse(issubclass(ColNoCont, Collection))
+        self.assertFalse(isinstance(ColNoCont(), Collection))
+        # Check None blocking
+        class SizeBlock:
+            def __iter__(self): return iter([])
+            def __contains__(self): return False
+            __len__ = None
+        class IterBlock:
+            def __len__(self): return 0
+            def __contains__(self): return True
+            __iter__ = None
+        self.assertFalse(issubclass(SizeBlock, Collection))
+        self.assertFalse(isinstance(SizeBlock(), Collection))
+        self.assertFalse(issubclass(IterBlock, Collection))
+        self.assertFalse(isinstance(IterBlock(), Collection))
+        # Check None blocking in subclass
+        class ColImpl:
+            def __iter__(self):
+                return iter(list())
+            def __len__(self):
+                return 0
+            def __contains__(self, item):
+                return False
+        class NonCol(ColImpl):
+            __contains__ = None
+        self.assertFalse(issubclass(NonCol, Collection))
+        self.assertFalse(isinstance(NonCol(), Collection))
+
+
     def test_Iterator(self):
         non_samples = [None, 42, 3.14, 1j, b"", "", (), [], {}, set()]
         for x in non_samples:
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
@@ -1548,13 +1548,15 @@
         bases = [c.Sequence, c.MutableMapping, c.Mapping, c.Set]
         for haystack in permutations(bases):
             m = mro(dict, haystack)
-            self.assertEqual(m, [dict, c.MutableMapping, c.Mapping, c.Sized,
-                                 c.Iterable, c.Container, object])
+            self.assertEqual(m, [dict, c.MutableMapping, c.Mapping,
+                                 c.Collection, c.Sized, c.Iterable,
+                                 c.Container, object])
         bases = [c.Container, c.Mapping, c.MutableMapping, c.OrderedDict]
         for haystack in permutations(bases):
             m = mro(c.ChainMap, haystack)
             self.assertEqual(m, [c.ChainMap, c.MutableMapping, c.Mapping,
-                                 c.Sized, c.Iterable, c.Container, object])
+                                 c.Collection, c.Sized, c.Iterable,
+                                 c.Container, object])
 
         # If there's a generic function with implementations registered for
         # both Sized and Container, passing a defaultdict to it results in an
@@ -1575,9 +1577,9 @@
         bases = [c.MutableSequence, c.MutableMapping]
         for haystack in permutations(bases):
             m = mro(D, bases)
-            self.assertEqual(m, [D, c.MutableSequence, c.Sequence,
-                                 c.defaultdict, dict, c.MutableMapping,
-                                 c.Mapping, c.Sized, c.Reversible, c.Iterable, c.Container,
+            self.assertEqual(m, [D, c.MutableSequence, c.Sequence, c.Reversible,
+                                 c.defaultdict, dict, c.MutableMapping, c.Mapping,
+                                 c.Collection, c.Sized, c.Iterable, c.Container,
                                  object])
 
         # Container and Callable are registered on different base classes and
@@ -1590,7 +1592,8 @@
         for haystack in permutations(bases):
             m = mro(C, haystack)
             self.assertEqual(m, [C, c.Callable, c.defaultdict, dict, c.Mapping,
-                                 c.Sized, c.Iterable, c.Container, object])
+                                 c.Collection, c.Sized, c.Iterable,
+                                 c.Container, object])
 
     def test_register_abc(self):
         c = collections
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -126,6 +126,9 @@
 Library
 -------
 
+- Issue 27598: Add Collections to collections.abc.
+  Patch by Ivan Levkivskyi, docs by Neil Girdhar.
+
 - Issue #25958: Support "anti-registration" of special methods from
   various ABCs, like __hash__, __iter__ or __len__.  All these (and
   several more) can be set to None in an implementation class and the

-- 
Repository URL: https://hg.python.org/cpython


More information about the Python-checkins mailing list