Source code for zope.interface.common.collections

##############################################################################
# Copyright (c) 2020 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
##############################################################################
"""
Interface definitions paralleling the abstract base classes defined in
:mod:`collections.abc`.

After this module is imported, the standard library types will declare
that they implement the appropriate interface. While most standard
library types will properly implement that interface (that
is, ``verifyObject(ISequence, list()))`` will pass, for example), a few might not:

    - `memoryview` doesn't feature all the defined methods of
      ``ISequence`` such as ``count``; it is still declared to provide
      ``ISequence`` though.

    - `collections.deque.pop` doesn't accept the ``index`` argument of
      `collections.abc.MutableSequence.pop`

    - `range.index` does not accept the ``start`` and ``stop`` arguments.

.. versionadded:: 5.0.0
"""
from __future__ import absolute_import

import sys

from abc import ABCMeta
# The collections imports are here, and not in
# zope.interface._compat to avoid importing collections
# unless requested. It's a big import.
try:
    from collections import abc
except ImportError:
    import collections as abc
from collections import OrderedDict
try:
    # On Python 3, all of these extend the appropriate collection ABC,
    # but on Python 2, UserDict does not (though it is registered as a
    # MutableMapping). (Importantly, UserDict on Python 2 is *not*
    # registered, because it's not iterable.) Extending the ABC is not
    # taken into account for interface declarations, though, so we
    # need to be explicit about it.
    from collections import UserList
    from collections import UserDict
    from collections import UserString
except ImportError:
    # Python 2
    from UserList import UserList
    from UserDict import IterableUserDict as UserDict
    from UserString import UserString

from zope.interface._compat import PYTHON2 as PY2
from zope.interface._compat import PYTHON3 as PY3
from zope.interface.common import ABCInterface
from zope.interface.common import optional

# pylint:disable=inherit-non-class,
# pylint:disable=no-self-argument,no-method-argument
# pylint:disable=unexpected-special-method-signature
# pylint:disable=no-value-for-parameter

PY35 = sys.version_info[:2] >= (3, 5)
PY36 = sys.version_info[:2] >= (3, 6)

def _new_in_ver(name, ver,
                bases_if_missing=(ABCMeta,),
                register_if_missing=()):
    if ver:
        return getattr(abc, name)

    # TODO: It's a shame to have to repeat the bases when
    # the ABC is missing. Can we DRY that?
    missing = ABCMeta(name, bases_if_missing, {
        '__doc__': "The ABC %s is not defined in this version of Python." % (
            name
        ),
    })

    for c in register_if_missing:
        missing.register(c)

    return missing

__all__ = [
    'IAsyncGenerator',
    'IAsyncIterable',
    'IAsyncIterator',
    'IAwaitable',
    'ICollection',
    'IContainer',
    'ICoroutine',
    'IGenerator',
    'IHashable',
    'IItemsView',
    'IIterable',
    'IIterator',
    'IKeysView',
    'IMapping',
    'IMappingView',
    'IMutableMapping',
    'IMutableSequence',
    'IMutableSet',
    'IReversible',
    'ISequence',
    'ISet',
    'ISized',
    'IValuesView',
]

[docs] class IContainer(ABCInterface): abc = abc.Container @optional def __contains__(other): """ Optional method. If not provided, the interpreter will use ``__iter__`` or the old ``__getitem__`` protocol to implement ``in``. """
[docs] class IHashable(ABCInterface): abc = abc.Hashable
[docs] class IIterable(ABCInterface): abc = abc.Iterable @optional def __iter__(): """ Optional method. If not provided, the interpreter will implement `iter` using the old ``__getitem__`` protocol. """
[docs] class IIterator(IIterable): abc = abc.Iterator
[docs] class IReversible(IIterable): abc = _new_in_ver('Reversible', PY36, (IIterable.getABC(),)) @optional def __reversed__(): """ Optional method. If this isn't present, the interpreter will use ``__len__`` and ``__getitem__`` to implement the `reversed` builtin. """
[docs] class IGenerator(IIterator): # New in 3.5 abc = _new_in_ver('Generator', PY35, (IIterator.getABC(),))
[docs] class ISized(ABCInterface): abc = abc.Sized
# ICallable is not defined because there's no standard signature.
[docs] class ICollection(ISized, IIterable, IContainer): abc = _new_in_ver('Collection', PY36, (ISized.getABC(), IIterable.getABC(), IContainer.getABC()))
[docs] class ISequence(IReversible, ICollection): abc = abc.Sequence extra_classes = (UserString,) # On Python 2, basestring is registered as an ISequence, and # its subclass str is an IByteString. If we also register str as # an ISequence, that tends to lead to inconsistent resolution order. ignored_classes = (basestring,) if str is bytes else () # pylint:disable=undefined-variable @optional def __reversed__(): """ Optional method. If this isn't present, the interpreter will use ``__len__`` and ``__getitem__`` to implement the `reversed` builtin. """ @optional def __iter__(): """ Optional method. If not provided, the interpreter will implement `iter` using the old ``__getitem__`` protocol. """
[docs] class IMutableSequence(ISequence): abc = abc.MutableSequence extra_classes = (UserList,)
class IByteString(ISequence): """ This unifies `bytes` and `bytearray`. """ abc = _new_in_ver('ByteString', PY3, (ISequence.getABC(),), (bytes, bytearray))
[docs] class ISet(ICollection): abc = abc.Set
[docs] class IMutableSet(ISet): abc = abc.MutableSet
[docs] class IMapping(ICollection): abc = abc.Mapping extra_classes = (dict,) # OrderedDict is a subclass of dict. On CPython 2, # it winds up registered as a IMutableMapping, which # produces an inconsistent IRO if we also try to register it # here. ignored_classes = (OrderedDict,) if PY2: @optional def __eq__(other): """ The interpreter will supply one. """ __ne__ = __eq__
[docs] class IMutableMapping(IMapping): abc = abc.MutableMapping extra_classes = (dict, UserDict,) ignored_classes = (OrderedDict,)
[docs] class IMappingView(ISized): abc = abc.MappingView
[docs] class IItemsView(IMappingView, ISet): abc = abc.ItemsView
[docs] class IKeysView(IMappingView, ISet): abc = abc.KeysView
[docs] class IValuesView(IMappingView, ICollection): abc = abc.ValuesView @optional def __contains__(other): """ Optional method. If not provided, the interpreter will use ``__iter__`` or the old ``__len__`` and ``__getitem__`` protocol to implement ``in``. """
[docs] class IAwaitable(ABCInterface): abc = _new_in_ver('Awaitable', PY35)
[docs] class ICoroutine(IAwaitable): abc = _new_in_ver('Coroutine', PY35)
[docs] class IAsyncIterable(ABCInterface): abc = _new_in_ver('AsyncIterable', PY35)
[docs] class IAsyncIterator(IAsyncIterable): abc = _new_in_ver('AsyncIterator', PY35)
[docs] class IAsyncGenerator(IAsyncIterator): abc = _new_in_ver('AsyncGenerator', PY36)