Update documentation and clarify the relationship of the older mapping and sequence interfaces.
This commit is contained in:
parent
4faeef1fe8
commit
d088fd500d
18
CHANGES.rst
18
CHANGES.rst
|
@ -116,6 +116,24 @@
|
|||
Like the above, this will break consumers depending on the exact
|
||||
output of error messages if more than one error is present.
|
||||
|
||||
- Add ``zope.interface.common.collections``,
|
||||
``zope.interface.common.numbers``, and ``zope.interface.common.io``.
|
||||
These modules define interfaces based on the ABCs defined in the
|
||||
standard library ``collections.abc``, ``numbers`` and ``io``
|
||||
modules, respectively. Importing these modules will make the
|
||||
standard library concrete classes that are registered with those
|
||||
ABCs declare the appropriate interface. See `issue 138
|
||||
<https://github.com/zopefoundation/zope.interface/issues/138>`_.
|
||||
|
||||
- Add ``zope.interface.common.builtins``. This module defines
|
||||
interfaces of common builtin types, such as ``ITextString`` and
|
||||
``IByteString``, ``IDict``, etc. These interfaces extend the
|
||||
appropriate interfaces from ``collections`` and ``numbers``, and the
|
||||
standard library classes implement them after importing this module.
|
||||
This is intended as a replacement for third-party packages like
|
||||
`dolmen.builtins <https://pypi.org/project/dolmen.builtins/>`_.
|
||||
See `issue 138 <https://github.com/zopefoundation/zope.interface/issues/138>`_.
|
||||
|
||||
|
||||
4.7.1 (2019-11-11)
|
||||
==================
|
||||
|
|
|
@ -4,29 +4,19 @@
|
|||
|
||||
The ``zope.interface.common`` package provides interfaces for objects
|
||||
distributed as part of the Python standard library. Importing these
|
||||
modules has the effect of making the standard library objects
|
||||
modules (usually) has the effect of making the standard library objects
|
||||
implement the correct interface.
|
||||
|
||||
``zope.interface.common.interfaces``
|
||||
====================================
|
||||
zope.interface.common.interface
|
||||
===============================
|
||||
|
||||
.. automodule:: zope.interface.common.interfaces
|
||||
|
||||
``zope.interface.common.idatetime``
|
||||
===================================
|
||||
zope.interface.common.idatetime
|
||||
===============================
|
||||
|
||||
.. automodule:: zope.interface.common.idatetime
|
||||
|
||||
``zope.interface.common.mapping``
|
||||
=================================
|
||||
|
||||
.. automodule:: zope.interface.common.mapping
|
||||
|
||||
``zope.interface.common.sequence``
|
||||
==================================
|
||||
|
||||
.. automodule:: zope.interface.common.sequence
|
||||
|
||||
zope.interface.common.collections
|
||||
=================================
|
||||
|
||||
|
@ -46,3 +36,15 @@ zope.interface.common.io
|
|||
========================
|
||||
|
||||
.. automodule:: zope.interface.common.io
|
||||
|
||||
.. Deprecated or discouraged modules below this
|
||||
|
||||
zope.interface.common.mapping
|
||||
=============================
|
||||
|
||||
.. automodule:: zope.interface.common.mapping
|
||||
|
||||
zope.interface.common.sequence
|
||||
==============================
|
||||
|
||||
.. automodule:: zope.interface.common.sequence
|
||||
|
|
|
@ -28,11 +28,12 @@ __all__ = [
|
|||
# pylint:disable=no-self-argument,no-method-argument
|
||||
# pylint:disable=unexpected-special-method-signature
|
||||
|
||||
def optional(meth):
|
||||
class optional(object):
|
||||
# Apply this decorator to a method definition to make it
|
||||
# optional (remove it from the list of required names), overriding
|
||||
# the definition inherited from the ABC.
|
||||
return _decorator_non_return
|
||||
def __init__(self, method):
|
||||
self.__doc__ = method.__doc__
|
||||
|
||||
|
||||
class ABCInterfaceClass(InterfaceClass):
|
||||
|
@ -144,6 +145,28 @@ class ABCInterfaceClass(InterfaceClass):
|
|||
and not self.__is_reverse_protocol_name(k)
|
||||
}
|
||||
|
||||
methods['__doc__'] = self.__create_class_doc(attrs)
|
||||
# Anything specified in the body takes precedence.
|
||||
methods.update(attrs)
|
||||
InterfaceClass.__init__(self, name, bases, methods)
|
||||
self.__register_classes()
|
||||
|
||||
@staticmethod
|
||||
def __optional_methods_to_docs(attrs):
|
||||
optionals = {k: v for k, v in attrs.items() if isinstance(v, optional)}
|
||||
for k in optionals:
|
||||
attrs[k] = _decorator_non_return
|
||||
|
||||
if not optionals:
|
||||
return ''
|
||||
|
||||
docs = "\n\nThe following methods are optional:\n - " + "\n-".join(
|
||||
"%s\n%s" % (k, v.__doc__) for k, v in optionals.items()
|
||||
)
|
||||
return docs
|
||||
|
||||
def __create_class_doc(self, attrs):
|
||||
based_on = self.__abc
|
||||
def ref(c):
|
||||
mod = c.__module__
|
||||
name = c.__name__
|
||||
|
@ -159,20 +182,18 @@ class ABCInterfaceClass(InterfaceClass):
|
|||
if implementations_doc:
|
||||
implementations_doc = "\n\nKnown implementations are:\n\n - " + implementations_doc
|
||||
|
||||
methods['__doc__'] = """Interface for the ABC `%s.%s`.%s""" % (
|
||||
based_on.__module__,
|
||||
based_on.__name__,
|
||||
based_on_doc = (based_on.__doc__ or '')
|
||||
based_on_doc = based_on_doc.splitlines()
|
||||
based_on_doc = based_on_doc[0] if based_on_doc else ''
|
||||
|
||||
doc = """Interface for the ABC `%s.%s`.\n\n%s%s%s""" % (
|
||||
based_on.__module__, based_on.__name__,
|
||||
attrs.get('__doc__', based_on_doc),
|
||||
self.__optional_methods_to_docs(attrs),
|
||||
implementations_doc
|
||||
)
|
||||
# Anything specified in the body takes precedence.
|
||||
# This lets us remove things that are rarely, if ever,
|
||||
# actually implemented. For example, ``tuple`` is registered
|
||||
# as an Sequence, but doesn't implement the required ``__reversed__``
|
||||
# method, but that's OK, it still works with the ``reversed()`` builtin
|
||||
# because it has ``__len__`` and ``__getitem__``.
|
||||
methods.update(attrs)
|
||||
InterfaceClass.__init__(self, name, bases, methods)
|
||||
self.__register_classes()
|
||||
return doc
|
||||
|
||||
|
||||
@staticmethod
|
||||
def __is_private_name(name):
|
||||
|
|
|
@ -36,12 +36,21 @@ __all__ = [
|
|||
'IFile',
|
||||
]
|
||||
|
||||
# pylint:disable=no-self-argument
|
||||
|
||||
class IList(collections.IMutableSequence):
|
||||
"""
|
||||
Interface for :class:`list`
|
||||
"""
|
||||
extra_classes = (list,)
|
||||
|
||||
def sort(key=None, reverse=False):
|
||||
"""
|
||||
Sort the list in place and return None.
|
||||
|
||||
*key* and *reverse* must be passed by name only.
|
||||
"""
|
||||
|
||||
|
||||
class ITuple(collections.ISequence):
|
||||
"""
|
||||
|
|
|
@ -124,7 +124,7 @@ class IContainer(ABCInterface):
|
|||
def __contains__(other):
|
||||
"""
|
||||
Optional method. If not provided, the interpreter will use
|
||||
``__iter__`` or the old ``__len__`` and ``__getitem__`` protocol
|
||||
``__iter__`` or the old ``__getitem__`` protocol
|
||||
to implement ``in``.
|
||||
"""
|
||||
|
||||
|
@ -134,6 +134,13 @@ class IHashable(ABCInterface):
|
|||
class IIterable(ABCInterface):
|
||||
abc = abc.Iterable
|
||||
|
||||
@optional
|
||||
def __iter__():
|
||||
"""
|
||||
Optional method. If not provided, the interpreter will
|
||||
implement `iter` using the old ``__getitem__`` protocol.
|
||||
"""
|
||||
|
||||
class IIterator(IIterable):
|
||||
abc = abc.Iterator
|
||||
|
||||
|
@ -145,7 +152,7 @@ class IReversible(IIterable):
|
|||
"""
|
||||
Optional method. If this isn't present, the interpreter
|
||||
will use ``__len__`` and ``__getitem__`` to implement the
|
||||
`reversed` builtin.`
|
||||
`reversed` builtin.
|
||||
"""
|
||||
|
||||
class IGenerator(IIterator):
|
||||
|
@ -176,9 +183,15 @@ class ISequence(IReversible,
|
|||
"""
|
||||
Optional method. If this isn't present, the interpreter
|
||||
will use ``__len__`` and ``__getitem__`` to implement the
|
||||
`reversed` builtin.`
|
||||
`reversed` builtin.
|
||||
"""
|
||||
|
||||
@optional
|
||||
def __iter__():
|
||||
"""
|
||||
Optional method. If not provided, the interpreter will
|
||||
implement `iter` using the old ``__getitem__`` protocol.
|
||||
"""
|
||||
|
||||
class IMutableSequence(ISequence):
|
||||
abc = abc.MutableSequence
|
||||
|
|
|
@ -11,13 +11,26 @@
|
|||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
##############################################################################
|
||||
"""Mapping Interfaces.
|
||||
"""
|
||||
Mapping Interfaces.
|
||||
|
||||
Importing this module does *not* mark any standard classes
|
||||
as implementing any of these interfaces.
|
||||
Importing this module does *not* mark any standard classes as
|
||||
implementing any of these interfaces.
|
||||
|
||||
While this module is not deprecated, new code should generally use
|
||||
:mod:`zope.interface.common.collections`, specifically
|
||||
:class:`~zope.interface.common.collections.IMapping` and
|
||||
:class:`~zope.interface.common.collections.IMutableMapping`. This
|
||||
module is occasionally useful for its extremely fine grained breakdown
|
||||
of interfaces.
|
||||
|
||||
The standard library :class:`dict` and :class:`collections.UserDict`
|
||||
implement ``IMutableMapping``, but *do not* implement any of the
|
||||
interfaces in this module.
|
||||
"""
|
||||
from zope.interface import Interface
|
||||
from zope.interface._compat import PYTHON2 as PY2
|
||||
from zope.interface.common import collections
|
||||
|
||||
class IItemMapping(Interface):
|
||||
"""Simplest readable mapping object
|
||||
|
@ -30,8 +43,12 @@ class IItemMapping(Interface):
|
|||
"""
|
||||
|
||||
|
||||
class IReadMapping(IItemMapping):
|
||||
"""Basic mapping interface
|
||||
class IReadMapping(IItemMapping, collections.IContainer):
|
||||
"""
|
||||
Basic mapping interface.
|
||||
|
||||
.. versionchanged:: 5.0.0
|
||||
Extend ``IContainer``
|
||||
"""
|
||||
|
||||
def get(key, default=None):
|
||||
|
@ -42,6 +59,7 @@ class IReadMapping(IItemMapping):
|
|||
|
||||
def __contains__(key):
|
||||
"""Tell if a key exists in the mapping."""
|
||||
# Optional in IContainer, required by this interface.
|
||||
|
||||
|
||||
class IWriteMapping(Interface):
|
||||
|
@ -54,8 +72,12 @@ class IWriteMapping(Interface):
|
|||
"""Set a new item in the mapping."""
|
||||
|
||||
|
||||
class IEnumerableMapping(IReadMapping):
|
||||
"""Mapping objects whose items can be enumerated.
|
||||
class IEnumerableMapping(IReadMapping, collections.ISized):
|
||||
"""
|
||||
Mapping objects whose items can be enumerated.
|
||||
|
||||
.. versionchanged:: 5.0.0
|
||||
Extend ``ISized``
|
||||
"""
|
||||
|
||||
def keys():
|
||||
|
@ -74,10 +96,6 @@ class IEnumerableMapping(IReadMapping):
|
|||
"""Return the items of the mapping object.
|
||||
"""
|
||||
|
||||
def __len__():
|
||||
"""Return the number of items.
|
||||
"""
|
||||
|
||||
class IMapping(IWriteMapping, IEnumerableMapping):
|
||||
''' Simple mapping interface '''
|
||||
|
||||
|
@ -152,6 +170,15 @@ class IExtendedWriteMapping(IWriteMapping):
|
|||
2-tuple; but raise KeyError if mapping is empty"""
|
||||
|
||||
class IFullMapping(
|
||||
IExtendedReadMapping, IExtendedWriteMapping, IClonableMapping, IMapping):
|
||||
''' Full mapping interface ''' # IMapping included so tests for IMapping
|
||||
# succeed with IFullMapping
|
||||
collections.IMutableMapping,
|
||||
IExtendedReadMapping, IExtendedWriteMapping, IClonableMapping, IMapping):
|
||||
"""
|
||||
Full mapping interface.
|
||||
|
||||
Most uses of this interface should instead use
|
||||
:class:`~zope.interface.commons.collections.IMutableMapping` (one of the
|
||||
bases of this interface). The required methods are the same.
|
||||
|
||||
.. versionchanged:: 5.0.0
|
||||
Extend ``IMutableMapping``
|
||||
"""
|
||||
|
|
|
@ -11,17 +11,30 @@
|
|||
# FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
##############################################################################
|
||||
"""Sequence Interfaces
|
||||
"""
|
||||
Sequence Interfaces
|
||||
|
||||
Importing this module does *not* mark any standard classes
|
||||
as implementing any of these interfaces.
|
||||
Importing this module does *not* mark any standard classes as
|
||||
implementing any of these interfaces.
|
||||
|
||||
While this module is not deprecated, new code should generally use
|
||||
:mod:`zope.interface.common.collections`, specifically
|
||||
:class:`~zope.interface.common.collections.ISequence` and
|
||||
:class:`~zope.interface.common.collections.IMutableSequence`. This
|
||||
module is occasionally useful for its fine-grained breakdown of interfaces.
|
||||
|
||||
The standard library :class:`list`, :class:`tuple` and
|
||||
:class:`collections.UserList`, among others, implement ``ISequence``
|
||||
or ``IMutableSequence`` but *do not* implement any of the interfaces
|
||||
in this module.
|
||||
"""
|
||||
|
||||
__docformat__ = 'restructuredtext'
|
||||
from zope.interface import Interface
|
||||
from zope.interface.common import collections
|
||||
from zope.interface._compat import PYTHON2 as PY2
|
||||
|
||||
class IMinimalSequence(Interface):
|
||||
class IMinimalSequence(collections.IIterable):
|
||||
"""Most basic sequence interface.
|
||||
|
||||
All sequences are iterable. This requires at least one of the
|
||||
|
@ -42,16 +55,30 @@ class IMinimalSequence(Interface):
|
|||
Declaring this interface does not specify whether `__getitem__`
|
||||
supports slice objects."""
|
||||
|
||||
class IFiniteSequence(IMinimalSequence):
|
||||
class IFiniteSequence(collections.ISized, IMinimalSequence):
|
||||
"""
|
||||
A sequence of bound size.
|
||||
|
||||
def __len__():
|
||||
"""``x.__len__() <==> len(x)``"""
|
||||
.. versionchanged:: 5.0.0
|
||||
Extend ``ISized``
|
||||
"""
|
||||
|
||||
class IReadSequence(IFiniteSequence):
|
||||
"""read interface shared by tuple and list"""
|
||||
class IReadSequence(collections.IContainer, IFiniteSequence):
|
||||
"""
|
||||
read interface shared by tuple and list
|
||||
|
||||
This interface is similar to
|
||||
:class:`~zope.interface.common.collections.ISequence`, but
|
||||
requires that all instances be totally ordered. Most users
|
||||
should prefer ``ISequence``.
|
||||
|
||||
.. versionchanged:: 5.0.0
|
||||
Extend ``IContainer``
|
||||
"""
|
||||
|
||||
def __contains__(item):
|
||||
"""``x.__contains__(item) <==> item in x``"""
|
||||
# Optional in IContainer, required here.
|
||||
|
||||
def __lt__(other):
|
||||
"""``x.__lt__(other) <==> x < other``"""
|
||||
|
@ -166,4 +193,23 @@ class IWriteSequence(IUniqueMemberWriteSequence):
|
|||
"""``x.__imul__(n) <==> x *= n``"""
|
||||
|
||||
class ISequence(IReadSequence, IWriteSequence):
|
||||
"""Full sequence contract"""
|
||||
"""
|
||||
Full sequence contract.
|
||||
|
||||
New code should prefer
|
||||
:class:`~zope.interface.common.collections.IMutableSequence`.
|
||||
|
||||
Compared to that interface, which is implemented by :class:`list`
|
||||
(:class:`~zope.interface.common.builtins.IList`), among others,
|
||||
this interface is missing the following methods:
|
||||
|
||||
- clear
|
||||
|
||||
- count
|
||||
|
||||
- index
|
||||
|
||||
This interface adds the following methods:
|
||||
|
||||
- sort
|
||||
"""
|
||||
|
|
|
@ -27,12 +27,6 @@ class TestVerifyClass(VerifyClassMixin,
|
|||
)
|
||||
FILE_IMPL = ()
|
||||
if PY2:
|
||||
UNVERIFIABLE += (
|
||||
# On both CPython and PyPy, there's no
|
||||
# exposed __iter__ method for strings or unicode.
|
||||
unicode,
|
||||
str,
|
||||
)
|
||||
FILE_IMPL = ((file, builtins.IFile),)
|
||||
@classmethod
|
||||
def create_tests(cls):
|
||||
|
|
|
@ -117,8 +117,6 @@ class TestVerifyClass(VerifyClassMixin, unittest.TestCase):
|
|||
# These two are missing Set.isdisjoint()
|
||||
type({}.viewitems()),
|
||||
type({}.viewkeys()),
|
||||
# str is missing __iter__!
|
||||
str,
|
||||
})
|
||||
|
||||
add_abc_interface_tests(TestVerifyClass, collections.ISet.__module__)
|
||||
|
@ -145,6 +143,7 @@ class TestVerifyObject(VerifyObjectMixin,
|
|||
type(reversed([])): lambda: reversed([]),
|
||||
'longrange_iterator': unittest.SkipTest,
|
||||
'range_iterator': lambda: iter(range(3)),
|
||||
'rangeiterator': lambda: iter(range(3)),
|
||||
type(iter(set())): lambda: iter(set()),
|
||||
type(iter('')): lambda: iter(''),
|
||||
'async_generator': unittest.SkipTest,
|
||||
|
|
Loading…
Reference in New Issue