Skip to content

Commit

Permalink
Update documentation and clarify the relationship of the older mappin…
Browse files Browse the repository at this point in the history
…g and sequence interfaces.
  • Loading branch information
jamadden committed Feb 17, 2020
1 parent 4faeef1 commit d088fd5
Show file tree
Hide file tree
Showing 9 changed files with 193 additions and 64 deletions.
18 changes: 18 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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)
==================
Expand Down
32 changes: 17 additions & 15 deletions docs/api/common.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
=================================

Expand All @@ -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
49 changes: 35 additions & 14 deletions src/zope/interface/common/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,12 @@
# 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):
Expand Down Expand Up @@ -144,6 +145,28 @@ def __init__(self, name, bases, attrs):
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__
Expand All @@ -159,20 +182,18 @@ def ref(c):
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):
Expand Down
9 changes: 9 additions & 0 deletions src/zope/interface/common/builtins.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,21 @@
'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):
"""
Expand Down
19 changes: 16 additions & 3 deletions src/zope/interface/common/collections.py
Original file line number Diff line number Diff line change
Expand Up @@ -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``.
"""

Expand All @@ -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

Expand All @@ -145,7 +152,7 @@ def __reversed__():
"""
Optional method. If this isn't present, the interpreter
will use ``__len__`` and ``__getitem__`` to implement the
`reversed` builtin.`
`reversed` builtin.
"""

class IGenerator(IIterator):
Expand Down Expand Up @@ -176,9 +183,15 @@ def __reversed__():
"""
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
Expand Down
55 changes: 41 additions & 14 deletions src/zope/interface/common/mapping.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -30,8 +43,12 @@ def __getitem__(key):
"""


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):
Expand All @@ -42,6 +59,7 @@ def get(key, default=None):

def __contains__(key):
"""Tell if a key exists in the mapping."""
# Optional in IContainer, required by this interface.


class IWriteMapping(Interface):
Expand All @@ -54,8 +72,12 @@ def __setitem__(key, value):
"""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():
Expand All @@ -74,10 +96,6 @@ def items():
"""Return the items of the mapping object.
"""

def __len__():
"""Return the number of items.
"""

class IMapping(IWriteMapping, IEnumerableMapping):
''' Simple mapping interface '''

Expand Down Expand Up @@ -152,6 +170,15 @@ def popitem():
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``
"""
Loading

0 comments on commit d088fd5

Please sign in to comment.