##############################################################################
# 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)