Issue 27598: Add Collections to collections.abc.
Patch by Ivan Levkivskyi, docs by Neil Girdhar.
This commit is contained in:
parent
9ff4fb3619
commit
f0666949fd
|
@ -45,10 +45,12 @@ ABC Inherits from Abstract Methods Mixin
|
|||
: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 @@ ABC Inherits from Abstract Methods Mixin
|
|||
: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 @@ ABC Inherits from Abstract Methods Mixin
|
|||
``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 Inherits from Abstract Methods Mixin
|
|||
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
|
||||
|
|
|
@ -11,7 +11,7 @@ import sys
|
|||
|
||||
__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 @@ class Container(metaclass=ABCMeta):
|
|||
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 @@ class Callable(metaclass=ABCMeta):
|
|||
### SETS ###
|
||||
|
||||
|
||||
class Set(Sized, Iterable, Container):
|
||||
class Set(Collection):
|
||||
|
||||
"""A set is a finite, iterable container.
|
||||
|
||||
|
@ -570,7 +579,7 @@ MutableSet.register(set)
|
|||
### MAPPINGS ###
|
||||
|
||||
|
||||
class Mapping(Sized, Iterable, Container):
|
||||
class Mapping(Collection):
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
|
@ -794,7 +803,7 @@ MutableMapping.register(dict)
|
|||
### SEQUENCES ###
|
||||
|
||||
|
||||
class Sequence(Sized, Reversible, Container):
|
||||
class Sequence(Reversible, Collection):
|
||||
|
||||
"""All the operations on a read-only sequence.
|
||||
|
||||
|
|
|
@ -21,7 +21,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, 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 @@ class TestOneTrickPonyABCs(ABCTestCase):
|
|||
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:
|
||||
|
|
|
@ -1548,13 +1548,15 @@ class TestSingleDispatch(unittest.TestCase):
|
|||
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 @@ class TestSingleDispatch(unittest.TestCase):
|
|||
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 @@ class TestSingleDispatch(unittest.TestCase):
|
|||
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
|
||||
|
|
|
@ -126,6 +126,9 @@ Core and Builtins
|
|||
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
|
||||
|
|
Loading…
Reference in New Issue