diff options
author | shmel1k <shmel1k@ydb.tech> | 2023-11-26 18:16:14 +0300 |
---|---|---|
committer | shmel1k <shmel1k@ydb.tech> | 2023-11-26 18:43:30 +0300 |
commit | b8cf9e88f4c5c64d9406af533d8948deb050d695 (patch) | |
tree | 218eb61fb3c3b96ec08b4d8cdfef383104a87d63 /contrib/python/zope.interface/py3/zope/interface | |
parent | 523f645a83a0ec97a0332dbc3863bb354c92a328 (diff) | |
download | ydb-b8cf9e88f4c5c64d9406af533d8948deb050d695.tar.gz |
add kikimr_configure
Diffstat (limited to 'contrib/python/zope.interface/py3/zope/interface')
23 files changed, 11191 insertions, 0 deletions
diff --git a/contrib/python/zope.interface/py3/zope/interface/__init__.py b/contrib/python/zope.interface/py3/zope/interface/__init__.py new file mode 100644 index 0000000000..17a272f1da --- /dev/null +++ b/contrib/python/zope.interface/py3/zope/interface/__init__.py @@ -0,0 +1,93 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 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. +# +############################################################################## +"""Interfaces + +This package implements the Python "scarecrow" proposal. + +The package exports two objects, `Interface` and `Attribute` directly. It also +exports several helper methods. Interface is used to create an interface with +a class statement, as in: + + class IMyInterface(Interface): + '''Interface documentation + ''' + + def meth(arg1, arg2): + '''Documentation for meth + ''' + + # Note that there is no self argument + +To find out what you can do with interfaces, see the interface +interface, `IInterface` in the `interfaces` module. + +The package has several public modules: + + o `declarations` provides utilities to declare interfaces on objects. It + also provides a wide range of helpful utilities that aid in managing + declared interfaces. Most of its public names are however imported here. + + o `document` has a utility for documenting an interface as structured text. + + o `exceptions` has the interface-defined exceptions + + o `interfaces` contains a list of all public interfaces for this package. + + o `verify` has utilities for verifying implementations of interfaces. + +See the module doc strings for more information. +""" +__docformat__ = 'restructuredtext' +# pylint:disable=wrong-import-position,unused-import +from zope.interface.interface import Interface +from zope.interface.interface import _wire + +# Need to actually get the interface elements to implement the right interfaces +_wire() +del _wire + +from zope.interface.declarations import Declaration +from zope.interface.declarations import alsoProvides +from zope.interface.declarations import classImplements +from zope.interface.declarations import classImplementsFirst +from zope.interface.declarations import classImplementsOnly +from zope.interface.declarations import directlyProvidedBy +from zope.interface.declarations import directlyProvides +from zope.interface.declarations import implementedBy +from zope.interface.declarations import implementer +from zope.interface.declarations import implementer_only +from zope.interface.declarations import moduleProvides +from zope.interface.declarations import named +from zope.interface.declarations import noLongerProvides +from zope.interface.declarations import providedBy +from zope.interface.declarations import provider + +from zope.interface.exceptions import Invalid + +from zope.interface.interface import Attribute +from zope.interface.interface import interfacemethod +from zope.interface.interface import invariant +from zope.interface.interface import taggedValue + +# The following are to make spec pickles cleaner +from zope.interface.declarations import Provides + + +from zope.interface.interfaces import IInterfaceDeclaration + +moduleProvides(IInterfaceDeclaration) + +__all__ = ('Interface', 'Attribute') + tuple(IInterfaceDeclaration) + +assert all(k in globals() for k in __all__) diff --git a/contrib/python/zope.interface/py3/zope/interface/_compat.py b/contrib/python/zope.interface/py3/zope/interface/_compat.py new file mode 100644 index 0000000000..2ff8d83eaf --- /dev/null +++ b/contrib/python/zope.interface/py3/zope/interface/_compat.py @@ -0,0 +1,135 @@ +############################################################################## +# +# Copyright (c) 2006 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. +# +############################################################################## +""" +Support functions for dealing with differences in platforms, including Python +versions and implementations. + +This file should have no imports from the rest of zope.interface because it is +used during early bootstrapping. +""" +import os +import sys + + +def _normalize_name(name): + if isinstance(name, bytes): + name = str(name, 'ascii') + if isinstance(name, str): + return name + raise TypeError("name must be a string or ASCII-only bytes") + +PYPY = hasattr(sys, 'pypy_version_info') + + +def _c_optimizations_required(): + """ + Return a true value if the C optimizations are required. + + This uses the ``PURE_PYTHON`` variable as documented in `_use_c_impl`. + """ + pure_env = os.environ.get('PURE_PYTHON') + require_c = pure_env == "0" + return require_c + + +def _c_optimizations_available(): + """ + Return the C optimization module, if available, otherwise + a false value. + + If the optimizations are required but not available, this + raises the ImportError. + + This does not say whether they should be used or not. + """ + catch = () if _c_optimizations_required() else (ImportError,) + try: + from zope.interface import _zope_interface_coptimizations as c_opt + return c_opt + except catch: # pragma: no cover (only Jython doesn't build extensions) + return False + + +def _c_optimizations_ignored(): + """ + The opposite of `_c_optimizations_required`. + """ + pure_env = os.environ.get('PURE_PYTHON') + return pure_env is not None and pure_env != "0" + + +def _should_attempt_c_optimizations(): + """ + Return a true value if we should attempt to use the C optimizations. + + This takes into account whether we're on PyPy and the value of the + ``PURE_PYTHON`` environment variable, as defined in `_use_c_impl`. + """ + is_pypy = hasattr(sys, 'pypy_version_info') + + if _c_optimizations_required(): + return True + if is_pypy: + return False + return not _c_optimizations_ignored() + + +def _use_c_impl(py_impl, name=None, globs=None): + """ + Decorator. Given an object implemented in Python, with a name like + ``Foo``, import the corresponding C implementation from + ``zope.interface._zope_interface_coptimizations`` with the name + ``Foo`` and use it instead. + + If the ``PURE_PYTHON`` environment variable is set to any value + other than ``"0"``, or we're on PyPy, ignore the C implementation + and return the Python version. If the C implementation cannot be + imported, return the Python version. If ``PURE_PYTHON`` is set to + 0, *require* the C implementation (let the ImportError propagate); + note that PyPy can import the C implementation in this case (and all + tests pass). + + In all cases, the Python version is kept available. in the module + globals with the name ``FooPy`` and the name ``FooFallback`` (both + conventions have been used; the C implementation of some functions + looks for the ``Fallback`` version, as do some of the Sphinx + documents). + + Example:: + + @_use_c_impl + class Foo(object): + ... + """ + name = name or py_impl.__name__ + globs = globs or sys._getframe(1).f_globals + + def find_impl(): + if not _should_attempt_c_optimizations(): + return py_impl + + c_opt = _c_optimizations_available() + if not c_opt: # pragma: no cover (only Jython doesn't build extensions) + return py_impl + + __traceback_info__ = c_opt + return getattr(c_opt, name) + + c_impl = find_impl() + # Always make available by the FooPy name and FooFallback + # name (for testing and documentation) + globs[name + 'Py'] = py_impl + globs[name + 'Fallback'] = py_impl + + return c_impl diff --git a/contrib/python/zope.interface/py3/zope/interface/_flatten.py b/contrib/python/zope.interface/py3/zope/interface/_flatten.py new file mode 100644 index 0000000000..a80c2de49a --- /dev/null +++ b/contrib/python/zope.interface/py3/zope/interface/_flatten.py @@ -0,0 +1,35 @@ +############################################################################## +# +# Copyright (c) 2002 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. +# +############################################################################## +"""Adapter-style interface registry + +See Adapter class. +""" +from zope.interface import Declaration + +def _flatten(implements, include_None=0): + + try: + r = implements.flattened() + except AttributeError: + if implements is None: + r=() + else: + r = Declaration(implements).flattened() + + if not include_None: + return r + + r = list(r) + r.append(None) + return r diff --git a/contrib/python/zope.interface/py3/zope/interface/_zope_interface_coptimizations.c b/contrib/python/zope.interface/py3/zope/interface/_zope_interface_coptimizations.c new file mode 100644 index 0000000000..91899283c0 --- /dev/null +++ b/contrib/python/zope.interface/py3/zope/interface/_zope_interface_coptimizations.c @@ -0,0 +1,2101 @@ +/*########################################################################### + # + # Copyright (c) 2003 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. + # + ############################################################################*/ + +#include "Python.h" +#include "structmember.h" + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-parameter" +#pragma clang diagnostic ignored "-Wmissing-field-initializers" +#endif + +#define TYPE(O) ((PyTypeObject*)(O)) +#define OBJECT(O) ((PyObject*)(O)) +#define CLASSIC(O) ((PyClassObject*)(O)) +#ifndef PyVarObject_HEAD_INIT +#define PyVarObject_HEAD_INIT(a, b) PyObject_HEAD_INIT(a) b, +#endif +#ifndef Py_TYPE +#define Py_TYPE(o) ((o)->ob_type) +#endif + +#define PyNative_FromString PyUnicode_FromString + +static PyObject *str__dict__, *str__implemented__, *strextends; +static PyObject *BuiltinImplementationSpecifications, *str__provides__; +static PyObject *str__class__, *str__providedBy__; +static PyObject *empty, *fallback; +static PyObject *str__conform__, *str_call_conform, *adapter_hooks; +static PyObject *str_uncached_lookup, *str_uncached_lookupAll; +static PyObject *str_uncached_subscriptions; +static PyObject *str_registry, *strro, *str_generation, *strchanged; +static PyObject *str__self__; +static PyObject *str__module__; +static PyObject *str__name__; +static PyObject *str__adapt__; +static PyObject *str_CALL_CUSTOM_ADAPT; + +static PyTypeObject *Implements; + +static int imported_declarations = 0; + +static int +import_declarations(void) +{ + PyObject *declarations, *i; + + declarations = PyImport_ImportModule("zope.interface.declarations"); + if (declarations == NULL) + return -1; + + BuiltinImplementationSpecifications = PyObject_GetAttrString( + declarations, "BuiltinImplementationSpecifications"); + if (BuiltinImplementationSpecifications == NULL) + return -1; + + empty = PyObject_GetAttrString(declarations, "_empty"); + if (empty == NULL) + return -1; + + fallback = PyObject_GetAttrString(declarations, "implementedByFallback"); + if (fallback == NULL) + return -1; + + + + i = PyObject_GetAttrString(declarations, "Implements"); + if (i == NULL) + return -1; + + if (! PyType_Check(i)) + { + PyErr_SetString(PyExc_TypeError, + "zope.interface.declarations.Implements is not a type"); + return -1; + } + + Implements = (PyTypeObject *)i; + + Py_DECREF(declarations); + + imported_declarations = 1; + return 0; +} + + +static PyTypeObject SpecificationBaseType; /* Forward */ + +static PyObject * +implementedByFallback(PyObject *cls) +{ + if (imported_declarations == 0 && import_declarations() < 0) + return NULL; + + return PyObject_CallFunctionObjArgs(fallback, cls, NULL); +} + +static PyObject * +implementedBy(PyObject *ignored, PyObject *cls) +{ + /* Fast retrieval of implements spec, if possible, to optimize + common case. Use fallback code if we get stuck. + */ + + PyObject *dict = NULL, *spec; + + if (PyObject_TypeCheck(cls, &PySuper_Type)) + { + // Let merging be handled by Python. + return implementedByFallback(cls); + } + + if (PyType_Check(cls)) + { + dict = TYPE(cls)->tp_dict; + Py_XINCREF(dict); + } + + if (dict == NULL) + dict = PyObject_GetAttr(cls, str__dict__); + + if (dict == NULL) + { + /* Probably a security proxied class, use more expensive fallback code */ + PyErr_Clear(); + return implementedByFallback(cls); + } + + spec = PyObject_GetItem(dict, str__implemented__); + Py_DECREF(dict); + if (spec) + { + if (imported_declarations == 0 && import_declarations() < 0) + return NULL; + + if (PyObject_TypeCheck(spec, Implements)) + return spec; + + /* Old-style declaration, use more expensive fallback code */ + Py_DECREF(spec); + return implementedByFallback(cls); + } + + PyErr_Clear(); + + /* Maybe we have a builtin */ + if (imported_declarations == 0 && import_declarations() < 0) + return NULL; + + spec = PyDict_GetItem(BuiltinImplementationSpecifications, cls); + if (spec != NULL) + { + Py_INCREF(spec); + return spec; + } + + /* We're stuck, use fallback */ + return implementedByFallback(cls); +} + +static PyObject * +getObjectSpecification(PyObject *ignored, PyObject *ob) +{ + PyObject *cls, *result; + + result = PyObject_GetAttr(ob, str__provides__); + if (!result) + { + if (!PyErr_ExceptionMatches(PyExc_AttributeError)) + { + /* Propagate non AttributeError exceptions. */ + return NULL; + } + PyErr_Clear(); + } + else + { + int is_instance = -1; + is_instance = PyObject_IsInstance(result, (PyObject*)&SpecificationBaseType); + if (is_instance < 0) + { + /* Propagate all errors */ + return NULL; + } + if (is_instance) + { + return result; + } + } + + /* We do a getattr here so as not to be defeated by proxies */ + cls = PyObject_GetAttr(ob, str__class__); + if (cls == NULL) + { + if (!PyErr_ExceptionMatches(PyExc_AttributeError)) + { + /* Propagate non-AttributeErrors */ + return NULL; + } + PyErr_Clear(); + if (imported_declarations == 0 && import_declarations() < 0) + return NULL; + + Py_INCREF(empty); + return empty; + } + result = implementedBy(NULL, cls); + Py_DECREF(cls); + + return result; +} + +static PyObject * +providedBy(PyObject *ignored, PyObject *ob) +{ + PyObject *result, *cls, *cp; + int is_instance = -1; + result = NULL; + + is_instance = PyObject_IsInstance(ob, (PyObject*)&PySuper_Type); + if (is_instance < 0) + { + if (!PyErr_ExceptionMatches(PyExc_AttributeError)) + { + /* Propagate non-AttributeErrors */ + return NULL; + } + PyErr_Clear(); + } + if (is_instance) + { + return implementedBy(NULL, ob); + } + + result = PyObject_GetAttr(ob, str__providedBy__); + + if (result == NULL) + { + if (!PyErr_ExceptionMatches(PyExc_AttributeError)) + { + return NULL; + } + + PyErr_Clear(); + return getObjectSpecification(NULL, ob); + } + + + /* We want to make sure we have a spec. We can't do a type check + because we may have a proxy, so we'll just try to get the + only attribute. + */ + if (PyObject_TypeCheck(result, &SpecificationBaseType) + || + PyObject_HasAttr(result, strextends) + ) + return result; + + /* + The object's class doesn't understand descriptors. + Sigh. We need to get an object descriptor, but we have to be + careful. We want to use the instance's __provides__,l if + there is one, but only if it didn't come from the class. + */ + Py_DECREF(result); + + cls = PyObject_GetAttr(ob, str__class__); + if (cls == NULL) + return NULL; + + result = PyObject_GetAttr(ob, str__provides__); + if (result == NULL) + { + /* No __provides__, so just fall back to implementedBy */ + PyErr_Clear(); + result = implementedBy(NULL, cls); + Py_DECREF(cls); + return result; + } + + cp = PyObject_GetAttr(cls, str__provides__); + if (cp == NULL) + { + /* The the class has no provides, assume we're done: */ + PyErr_Clear(); + Py_DECREF(cls); + return result; + } + + if (cp == result) + { + /* + Oops, we got the provides from the class. This means + the object doesn't have it's own. We should use implementedBy + */ + Py_DECREF(result); + result = implementedBy(NULL, cls); + } + + Py_DECREF(cls); + Py_DECREF(cp); + + return result; +} + +typedef struct { + PyObject_HEAD + PyObject* weakreflist; + /* + In the past, these fields were stored in the __dict__ + and were technically allowed to contain any Python object, though + other type checks would fail or fall back to generic code paths if + they didn't have the expected type. We preserve that behaviour and don't + make any assumptions about contents. + */ + PyObject* _implied; + /* + The remainder aren't used in C code but must be stored here + to prevent instance layout conflicts. + */ + PyObject* _dependents; + PyObject* _bases; + PyObject* _v_attrs; + PyObject* __iro__; + PyObject* __sro__; +} Spec; + +/* + We know what the fields are *supposed* to define, but + they could have anything, so we need to traverse them. +*/ +static int +Spec_traverse(Spec* self, visitproc visit, void* arg) +{ + Py_VISIT(self->_implied); + Py_VISIT(self->_dependents); + Py_VISIT(self->_bases); + Py_VISIT(self->_v_attrs); + Py_VISIT(self->__iro__); + Py_VISIT(self->__sro__); + return 0; +} + +static int +Spec_clear(Spec* self) +{ + Py_CLEAR(self->_implied); + Py_CLEAR(self->_dependents); + Py_CLEAR(self->_bases); + Py_CLEAR(self->_v_attrs); + Py_CLEAR(self->__iro__); + Py_CLEAR(self->__sro__); + return 0; +} + +static void +Spec_dealloc(Spec* self) +{ + /* PyType_GenericAlloc that you get when you don't + specify a tp_alloc always tracks the object. */ + PyObject_GC_UnTrack((PyObject *)self); + if (self->weakreflist != NULL) { + PyObject_ClearWeakRefs(OBJECT(self)); + } + Spec_clear(self); + Py_TYPE(self)->tp_free(OBJECT(self)); +} + +static PyObject * +Spec_extends(Spec *self, PyObject *other) +{ + PyObject *implied; + + implied = self->_implied; + if (implied == NULL) { + return NULL; + } + + if (PyDict_GetItem(implied, other) != NULL) + Py_RETURN_TRUE; + Py_RETURN_FALSE; +} + +static char Spec_extends__doc__[] = +"Test whether a specification is or extends another" +; + +static char Spec_providedBy__doc__[] = +"Test whether an interface is implemented by the specification" +; + +static PyObject * +Spec_call(Spec *self, PyObject *args, PyObject *kw) +{ + PyObject *spec; + + if (! PyArg_ParseTuple(args, "O", &spec)) + return NULL; + return Spec_extends(self, spec); +} + +static PyObject * +Spec_providedBy(PyObject *self, PyObject *ob) +{ + PyObject *decl, *item; + + decl = providedBy(NULL, ob); + if (decl == NULL) + return NULL; + + if (PyObject_TypeCheck(decl, &SpecificationBaseType)) + item = Spec_extends((Spec*)decl, self); + else + /* decl is probably a security proxy. We have to go the long way + around. + */ + item = PyObject_CallFunctionObjArgs(decl, self, NULL); + + Py_DECREF(decl); + return item; +} + + +static char Spec_implementedBy__doc__[] = +"Test whether the specification is implemented by a class or factory.\n" +"Raise TypeError if argument is neither a class nor a callable." +; + +static PyObject * +Spec_implementedBy(PyObject *self, PyObject *cls) +{ + PyObject *decl, *item; + + decl = implementedBy(NULL, cls); + if (decl == NULL) + return NULL; + + if (PyObject_TypeCheck(decl, &SpecificationBaseType)) + item = Spec_extends((Spec*)decl, self); + else + item = PyObject_CallFunctionObjArgs(decl, self, NULL); + + Py_DECREF(decl); + return item; +} + +static struct PyMethodDef Spec_methods[] = { + {"providedBy", + (PyCFunction)Spec_providedBy, METH_O, + Spec_providedBy__doc__}, + {"implementedBy", + (PyCFunction)Spec_implementedBy, METH_O, + Spec_implementedBy__doc__}, + {"isOrExtends", (PyCFunction)Spec_extends, METH_O, + Spec_extends__doc__}, + + {NULL, NULL} /* sentinel */ +}; + +static PyMemberDef Spec_members[] = { + {"_implied", T_OBJECT_EX, offsetof(Spec, _implied), 0, ""}, + {"_dependents", T_OBJECT_EX, offsetof(Spec, _dependents), 0, ""}, + {"_bases", T_OBJECT_EX, offsetof(Spec, _bases), 0, ""}, + {"_v_attrs", T_OBJECT_EX, offsetof(Spec, _v_attrs), 0, ""}, + {"__iro__", T_OBJECT_EX, offsetof(Spec, __iro__), 0, ""}, + {"__sro__", T_OBJECT_EX, offsetof(Spec, __sro__), 0, ""}, + {NULL}, +}; + + +static PyTypeObject SpecificationBaseType = { + PyVarObject_HEAD_INIT(NULL, 0) + /* tp_name */ "_interface_coptimizations." + "SpecificationBase", + /* tp_basicsize */ sizeof(Spec), + /* tp_itemsize */ 0, + /* tp_dealloc */ (destructor)Spec_dealloc, + /* tp_print */ (printfunc)0, + /* tp_getattr */ (getattrfunc)0, + /* tp_setattr */ (setattrfunc)0, + /* tp_compare */ 0, + /* tp_repr */ (reprfunc)0, + /* tp_as_number */ 0, + /* tp_as_sequence */ 0, + /* tp_as_mapping */ 0, + /* tp_hash */ (hashfunc)0, + /* tp_call */ (ternaryfunc)Spec_call, + /* tp_str */ (reprfunc)0, + /* tp_getattro */ (getattrofunc)0, + /* tp_setattro */ (setattrofunc)0, + /* tp_as_buffer */ 0, + /* tp_flags */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, + "Base type for Specification objects", + /* tp_traverse */ (traverseproc)Spec_traverse, + /* tp_clear */ (inquiry)Spec_clear, + /* tp_richcompare */ (richcmpfunc)0, + /* tp_weaklistoffset */ offsetof(Spec, weakreflist), + /* tp_iter */ (getiterfunc)0, + /* tp_iternext */ (iternextfunc)0, + /* tp_methods */ Spec_methods, + /* tp_members */ Spec_members, +}; + +static PyObject * +OSD_descr_get(PyObject *self, PyObject *inst, PyObject *cls) +{ + PyObject *provides; + + if (inst == NULL) + return getObjectSpecification(NULL, cls); + + provides = PyObject_GetAttr(inst, str__provides__); + /* Return __provides__ if we got it, or return NULL and propagate non-AttributeError. */ + if (provides != NULL || !PyErr_ExceptionMatches(PyExc_AttributeError)) + return provides; + + PyErr_Clear(); + return implementedBy(NULL, cls); +} + +static PyTypeObject OSDType = { + PyVarObject_HEAD_INIT(NULL, 0) + /* tp_name */ "_interface_coptimizations." + "ObjectSpecificationDescriptor", + /* tp_basicsize */ 0, + /* tp_itemsize */ 0, + /* tp_dealloc */ (destructor)0, + /* tp_print */ (printfunc)0, + /* tp_getattr */ (getattrfunc)0, + /* tp_setattr */ (setattrfunc)0, + /* tp_compare */ 0, + /* tp_repr */ (reprfunc)0, + /* tp_as_number */ 0, + /* tp_as_sequence */ 0, + /* tp_as_mapping */ 0, + /* tp_hash */ (hashfunc)0, + /* tp_call */ (ternaryfunc)0, + /* tp_str */ (reprfunc)0, + /* tp_getattro */ (getattrofunc)0, + /* tp_setattro */ (setattrofunc)0, + /* tp_as_buffer */ 0, + /* tp_flags */ Py_TPFLAGS_DEFAULT + | Py_TPFLAGS_BASETYPE , + "Object Specification Descriptor", + /* tp_traverse */ (traverseproc)0, + /* tp_clear */ (inquiry)0, + /* tp_richcompare */ (richcmpfunc)0, + /* tp_weaklistoffset */ (long)0, + /* tp_iter */ (getiterfunc)0, + /* tp_iternext */ (iternextfunc)0, + /* tp_methods */ 0, + /* tp_members */ 0, + /* tp_getset */ 0, + /* tp_base */ 0, + /* tp_dict */ 0, /* internal use */ + /* tp_descr_get */ (descrgetfunc)OSD_descr_get, +}; + +typedef struct { + Spec spec; + /* These members are handled generically, as for Spec members. */ + PyObject* _cls; + PyObject* _implements; +} CPB; + +static PyObject * +CPB_descr_get(CPB *self, PyObject *inst, PyObject *cls) +{ + PyObject *implements; + + if (self->_cls == NULL) + return NULL; + + if (cls == self->_cls) + { + if (inst == NULL) + { + Py_INCREF(self); + return OBJECT(self); + } + + implements = self->_implements; + Py_XINCREF(implements); + return implements; + } + + PyErr_SetObject(PyExc_AttributeError, str__provides__); + return NULL; +} + +static int +CPB_traverse(CPB* self, visitproc visit, void* arg) +{ + Py_VISIT(self->_cls); + Py_VISIT(self->_implements); + return Spec_traverse((Spec*)self, visit, arg); +} + +static int +CPB_clear(CPB* self) +{ + Py_CLEAR(self->_cls); + Py_CLEAR(self->_implements); + Spec_clear((Spec*)self); + return 0; +} + +static void +CPB_dealloc(CPB* self) +{ + PyObject_GC_UnTrack((PyObject *)self); + CPB_clear(self); + Spec_dealloc((Spec*)self); +} + +static PyMemberDef CPB_members[] = { + {"_cls", T_OBJECT_EX, offsetof(CPB, _cls), 0, "Defining class."}, + {"_implements", T_OBJECT_EX, offsetof(CPB, _implements), 0, "Result of implementedBy."}, + {NULL} +}; + +static PyTypeObject CPBType = { + PyVarObject_HEAD_INIT(NULL, 0) + /* tp_name */ "_interface_coptimizations." + "ClassProvidesBase", + /* tp_basicsize */ sizeof(CPB), + /* tp_itemsize */ 0, + /* tp_dealloc */ (destructor)CPB_dealloc, + /* tp_print */ (printfunc)0, + /* tp_getattr */ (getattrfunc)0, + /* tp_setattr */ (setattrfunc)0, + /* tp_compare */ 0, + /* tp_repr */ (reprfunc)0, + /* tp_as_number */ 0, + /* tp_as_sequence */ 0, + /* tp_as_mapping */ 0, + /* tp_hash */ (hashfunc)0, + /* tp_call */ (ternaryfunc)0, + /* tp_str */ (reprfunc)0, + /* tp_getattro */ (getattrofunc)0, + /* tp_setattro */ (setattrofunc)0, + /* tp_as_buffer */ 0, + /* tp_flags */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, + "C Base class for ClassProvides", + /* tp_traverse */ (traverseproc)CPB_traverse, + /* tp_clear */ (inquiry)CPB_clear, + /* tp_richcompare */ (richcmpfunc)0, + /* tp_weaklistoffset */ (long)0, + /* tp_iter */ (getiterfunc)0, + /* tp_iternext */ (iternextfunc)0, + /* tp_methods */ 0, + /* tp_members */ CPB_members, + /* tp_getset */ 0, + /* tp_base */ &SpecificationBaseType, + /* tp_dict */ 0, /* internal use */ + /* tp_descr_get */ (descrgetfunc)CPB_descr_get, + /* tp_descr_set */ 0, + /* tp_dictoffset */ 0, + /* tp_init */ 0, + /* tp_alloc */ 0, + /* tp_new */ 0, +}; + +/* ==================================================================== */ +/* ========== Begin: __call__ and __adapt__ =========================== */ + +/* + def __adapt__(self, obj): + """Adapt an object to the receiver + """ + if self.providedBy(obj): + return obj + + for hook in adapter_hooks: + adapter = hook(self, obj) + if adapter is not None: + return adapter + + +*/ +static PyObject * +__adapt__(PyObject *self, PyObject *obj) +{ + PyObject *decl, *args, *adapter; + int implements, i, l; + + decl = providedBy(NULL, obj); + if (decl == NULL) + return NULL; + + if (PyObject_TypeCheck(decl, &SpecificationBaseType)) + { + PyObject *implied; + + implied = ((Spec*)decl)->_implied; + if (implied == NULL) + { + Py_DECREF(decl); + return NULL; + } + + implements = PyDict_GetItem(implied, self) != NULL; + Py_DECREF(decl); + } + else + { + /* decl is probably a security proxy. We have to go the long way + around. + */ + PyObject *r; + r = PyObject_CallFunctionObjArgs(decl, self, NULL); + Py_DECREF(decl); + if (r == NULL) + return NULL; + implements = PyObject_IsTrue(r); + Py_DECREF(r); + } + + if (implements) + { + Py_INCREF(obj); + return obj; + } + + l = PyList_GET_SIZE(adapter_hooks); + args = PyTuple_New(2); + if (args == NULL) + return NULL; + Py_INCREF(self); + PyTuple_SET_ITEM(args, 0, self); + Py_INCREF(obj); + PyTuple_SET_ITEM(args, 1, obj); + for (i = 0; i < l; i++) + { + adapter = PyObject_CallObject(PyList_GET_ITEM(adapter_hooks, i), args); + if (adapter == NULL || adapter != Py_None) + { + Py_DECREF(args); + return adapter; + } + Py_DECREF(adapter); + } + + Py_DECREF(args); + + Py_INCREF(Py_None); + return Py_None; +} + +typedef struct { + Spec spec; + PyObject* __name__; + PyObject* __module__; + Py_hash_t _v_cached_hash; +} IB; + +static struct PyMethodDef ib_methods[] = { + {"__adapt__", (PyCFunction)__adapt__, METH_O, + "Adapt an object to the receiver"}, + {NULL, NULL} /* sentinel */ +}; + +/* + def __call__(self, obj, alternate=_marker): + try: + conform = obj.__conform__ + except AttributeError: # pylint:disable=bare-except + conform = None + + if conform is not None: + adapter = self._call_conform(conform) + if adapter is not None: + return adapter + + adapter = self.__adapt__(obj) + + if adapter is not None: + return adapter + if alternate is not _marker: + return alternate + raise TypeError("Could not adapt", obj, self) + +*/ +static PyObject * +IB_call(PyObject *self, PyObject *args, PyObject *kwargs) +{ + PyObject *conform, *obj, *alternate, *adapter; + static char *kwlist[] = {"obj", "alternate", NULL}; + conform = obj = alternate = adapter = NULL; + + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O", kwlist, + &obj, &alternate)) + return NULL; + + conform = PyObject_GetAttr(obj, str__conform__); + if (conform == NULL) + { + if (!PyErr_ExceptionMatches(PyExc_AttributeError)) + { + /* Propagate non-AttributeErrors */ + return NULL; + } + PyErr_Clear(); + + Py_INCREF(Py_None); + conform = Py_None; + } + + if (conform != Py_None) + { + adapter = PyObject_CallMethodObjArgs(self, str_call_conform, + conform, NULL); + Py_DECREF(conform); + if (adapter == NULL || adapter != Py_None) + return adapter; + Py_DECREF(adapter); + } + else + { + Py_DECREF(conform); + } + + /* We differ from the Python code here. For speed, instead of always calling + self.__adapt__(), we check to see if the type has defined it. Checking in + the dict for __adapt__ isn't sufficient because there's no cheap way to + tell if it's the __adapt__ that InterfaceBase itself defines (our type + will *never* be InterfaceBase, we're always subclassed by + InterfaceClass). Instead, we cooperate with InterfaceClass in Python to + set a flag in a new subclass when this is necessary. */ + if (PyDict_GetItem(self->ob_type->tp_dict, str_CALL_CUSTOM_ADAPT)) + { + /* Doesn't matter what the value is. Simply being present is enough. */ + adapter = PyObject_CallMethodObjArgs(self, str__adapt__, obj, NULL); + } + else + { + adapter = __adapt__(self, obj); + } + + if (adapter == NULL || adapter != Py_None) + { + return adapter; + } + Py_DECREF(adapter); + + if (alternate != NULL) + { + Py_INCREF(alternate); + return alternate; + } + + adapter = Py_BuildValue("sOO", "Could not adapt", obj, self); + if (adapter != NULL) + { + PyErr_SetObject(PyExc_TypeError, adapter); + Py_DECREF(adapter); + } + return NULL; +} + + +static int +IB_traverse(IB* self, visitproc visit, void* arg) +{ + Py_VISIT(self->__name__); + Py_VISIT(self->__module__); + return Spec_traverse((Spec*)self, visit, arg); +} + +static int +IB_clear(IB* self) +{ + Py_CLEAR(self->__name__); + Py_CLEAR(self->__module__); + return Spec_clear((Spec*)self); +} + +static void +IB_dealloc(IB* self) +{ + PyObject_GC_UnTrack((PyObject *)self); + IB_clear(self); + Spec_dealloc((Spec*)self); +} + +static PyMemberDef IB_members[] = { + {"__name__", T_OBJECT_EX, offsetof(IB, __name__), 0, ""}, + // The redundancy between __module__ and __ibmodule__ is because + // __module__ is often shadowed by subclasses. + {"__module__", T_OBJECT_EX, offsetof(IB, __module__), READONLY, ""}, + {"__ibmodule__", T_OBJECT_EX, offsetof(IB, __module__), 0, ""}, + {NULL} +}; + +static Py_hash_t +IB_hash(IB* self) +{ + PyObject* tuple; + if (!self->__module__) { + PyErr_SetString(PyExc_AttributeError, "__module__"); + return -1; + } + if (!self->__name__) { + PyErr_SetString(PyExc_AttributeError, "__name__"); + return -1; + } + + if (self->_v_cached_hash) { + return self->_v_cached_hash; + } + + tuple = PyTuple_Pack(2, self->__name__, self->__module__); + if (!tuple) { + return -1; + } + self->_v_cached_hash = PyObject_Hash(tuple); + Py_CLEAR(tuple); + return self->_v_cached_hash; +} + +static PyTypeObject InterfaceBaseType; + +static PyObject* +IB_richcompare(IB* self, PyObject* other, int op) +{ + PyObject* othername; + PyObject* othermod; + PyObject* oresult; + IB* otherib; + int result; + + otherib = NULL; + oresult = othername = othermod = NULL; + + if (OBJECT(self) == other) { + switch(op) { + case Py_EQ: + case Py_LE: + case Py_GE: + Py_RETURN_TRUE; + break; + case Py_NE: + Py_RETURN_FALSE; + } + } + + if (other == Py_None) { + switch(op) { + case Py_LT: + case Py_LE: + case Py_NE: + Py_RETURN_TRUE; + default: + Py_RETURN_FALSE; + } + } + + if (PyObject_TypeCheck(other, &InterfaceBaseType)) { + // This branch borrows references. No need to clean + // up if otherib is not null. + otherib = (IB*)other; + othername = otherib->__name__; + othermod = otherib->__module__; + } + else { + othername = PyObject_GetAttrString(other, "__name__"); + if (othername) { + othermod = PyObject_GetAttrString(other, "__module__"); + } + if (!othername || !othermod) { + if (PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_AttributeError)) { + PyErr_Clear(); + oresult = Py_NotImplemented; + } + goto cleanup; + } + } +#if 0 +// This is the simple, straightforward version of what Python does. + PyObject* pt1 = PyTuple_Pack(2, self->__name__, self->__module__); + PyObject* pt2 = PyTuple_Pack(2, othername, othermod); + oresult = PyObject_RichCompare(pt1, pt2, op); +#endif + + // tuple comparison is decided by the first non-equal element. + result = PyObject_RichCompareBool(self->__name__, othername, Py_EQ); + if (result == 0) { + result = PyObject_RichCompareBool(self->__name__, othername, op); + } + else if (result == 1) { + result = PyObject_RichCompareBool(self->__module__, othermod, op); + } + // If either comparison failed, we have an error set. + // Leave oresult NULL so we raise it. + if (result == -1) { + goto cleanup; + } + + oresult = result ? Py_True : Py_False; + + +cleanup: + Py_XINCREF(oresult); + + if (!otherib) { + Py_XDECREF(othername); + Py_XDECREF(othermod); + } + return oresult; + +} + +static int +IB_init(IB* self, PyObject* args, PyObject* kwargs) +{ + static char *kwlist[] = {"__name__", "__module__", NULL}; + PyObject* module = NULL; + PyObject* name = NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OO:InterfaceBase.__init__", kwlist, + &name, &module)) { + return -1; + } + IB_clear(self); + self->__module__ = module ? module : Py_None; + Py_INCREF(self->__module__); + self->__name__ = name ? name : Py_None; + Py_INCREF(self->__name__); + return 0; +} + + +static PyTypeObject InterfaceBaseType = { + PyVarObject_HEAD_INIT(NULL, 0) + /* tp_name */ "_zope_interface_coptimizations." + "InterfaceBase", + /* tp_basicsize */ sizeof(IB), + /* tp_itemsize */ 0, + /* tp_dealloc */ (destructor)IB_dealloc, + /* tp_print */ (printfunc)0, + /* tp_getattr */ (getattrfunc)0, + /* tp_setattr */ (setattrfunc)0, + /* tp_compare */ 0, + /* tp_repr */ (reprfunc)0, + /* tp_as_number */ 0, + /* tp_as_sequence */ 0, + /* tp_as_mapping */ 0, + /* tp_hash */ (hashfunc)IB_hash, + /* tp_call */ (ternaryfunc)IB_call, + /* tp_str */ (reprfunc)0, + /* tp_getattro */ (getattrofunc)0, + /* tp_setattro */ (setattrofunc)0, + /* tp_as_buffer */ 0, + /* tp_flags */ Py_TPFLAGS_DEFAULT + | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, + /* tp_doc */ "Interface base type providing __call__ and __adapt__", + /* tp_traverse */ (traverseproc)IB_traverse, + /* tp_clear */ (inquiry)IB_clear, + /* tp_richcompare */ (richcmpfunc)IB_richcompare, + /* tp_weaklistoffset */ (long)0, + /* tp_iter */ (getiterfunc)0, + /* tp_iternext */ (iternextfunc)0, + /* tp_methods */ ib_methods, + /* tp_members */ IB_members, + /* tp_getset */ 0, + /* tp_base */ &SpecificationBaseType, + /* tp_dict */ 0, + /* tp_descr_get */ 0, + /* tp_descr_set */ 0, + /* tp_dictoffset */ 0, + /* tp_init */ (initproc)IB_init, +}; + +/* =================== End: __call__ and __adapt__ ==================== */ +/* ==================================================================== */ + +/* ==================================================================== */ +/* ========================== Begin: Lookup Bases ===================== */ + +typedef struct { + PyObject_HEAD + PyObject *_cache; + PyObject *_mcache; + PyObject *_scache; +} lookup; + +typedef struct { + PyObject_HEAD + PyObject *_cache; + PyObject *_mcache; + PyObject *_scache; + PyObject *_verify_ro; + PyObject *_verify_generations; +} verify; + +static int +lookup_traverse(lookup *self, visitproc visit, void *arg) +{ + int vret; + + if (self->_cache) { + vret = visit(self->_cache, arg); + if (vret != 0) + return vret; + } + + if (self->_mcache) { + vret = visit(self->_mcache, arg); + if (vret != 0) + return vret; + } + + if (self->_scache) { + vret = visit(self->_scache, arg); + if (vret != 0) + return vret; + } + + return 0; +} + +static int +lookup_clear(lookup *self) +{ + Py_CLEAR(self->_cache); + Py_CLEAR(self->_mcache); + Py_CLEAR(self->_scache); + return 0; +} + +static void +lookup_dealloc(lookup *self) +{ + PyObject_GC_UnTrack((PyObject *)self); + lookup_clear(self); + Py_TYPE(self)->tp_free((PyObject*)self); +} + +/* + def changed(self, ignored=None): + self._cache.clear() + self._mcache.clear() + self._scache.clear() +*/ +static PyObject * +lookup_changed(lookup *self, PyObject *ignored) +{ + lookup_clear(self); + Py_INCREF(Py_None); + return Py_None; +} + +#define ASSURE_DICT(N) if (N == NULL) { N = PyDict_New(); \ + if (N == NULL) return NULL; \ + } + +/* + def _getcache(self, provided, name): + cache = self._cache.get(provided) + if cache is None: + cache = {} + self._cache[provided] = cache + if name: + c = cache.get(name) + if c is None: + c = {} + cache[name] = c + cache = c + return cache +*/ +static PyObject * +_subcache(PyObject *cache, PyObject *key) +{ + PyObject *subcache; + + subcache = PyDict_GetItem(cache, key); + if (subcache == NULL) + { + int status; + + subcache = PyDict_New(); + if (subcache == NULL) + return NULL; + status = PyDict_SetItem(cache, key, subcache); + Py_DECREF(subcache); + if (status < 0) + return NULL; + } + + return subcache; +} +static PyObject * +_getcache(lookup *self, PyObject *provided, PyObject *name) +{ + PyObject *cache; + + ASSURE_DICT(self->_cache); + cache = _subcache(self->_cache, provided); + if (cache == NULL) + return NULL; + + if (name != NULL && PyObject_IsTrue(name)) + cache = _subcache(cache, name); + + return cache; +} + + +/* + def lookup(self, required, provided, name=u'', default=None): + cache = self._getcache(provided, name) + if len(required) == 1: + result = cache.get(required[0], _not_in_mapping) + else: + result = cache.get(tuple(required), _not_in_mapping) + + if result is _not_in_mapping: + result = self._uncached_lookup(required, provided, name) + if len(required) == 1: + cache[required[0]] = result + else: + cache[tuple(required)] = result + + if result is None: + return default + + return result +*/ + +static PyObject * +_lookup(lookup *self, + PyObject *required, PyObject *provided, PyObject *name, + PyObject *default_) +{ + PyObject *result, *key, *cache; + result = key = cache = NULL; + if ( name && !PyUnicode_Check(name) ) + { + PyErr_SetString(PyExc_ValueError, + "name is not a string or unicode"); + return NULL; + } + + /* If `required` is a lazy sequence, it could have arbitrary side-effects, + such as clearing our caches. So we must not retrieve the cache until + after resolving it. */ + required = PySequence_Tuple(required); + if (required == NULL) + return NULL; + + + cache = _getcache(self, provided, name); + if (cache == NULL) + return NULL; + + if (PyTuple_GET_SIZE(required) == 1) + key = PyTuple_GET_ITEM(required, 0); + else + key = required; + + result = PyDict_GetItem(cache, key); + if (result == NULL) + { + int status; + + result = PyObject_CallMethodObjArgs(OBJECT(self), str_uncached_lookup, + required, provided, name, NULL); + if (result == NULL) + { + Py_DECREF(required); + return NULL; + } + status = PyDict_SetItem(cache, key, result); + Py_DECREF(required); + if (status < 0) + { + Py_DECREF(result); + return NULL; + } + } + else + { + Py_INCREF(result); + Py_DECREF(required); + } + + if (result == Py_None && default_ != NULL) + { + Py_DECREF(Py_None); + Py_INCREF(default_); + return default_; + } + + return result; +} +static PyObject * +lookup_lookup(lookup *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"required", "provided", "name", "default", NULL}; + PyObject *required, *provided, *name=NULL, *default_=NULL; + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO|OO:LookupBase.lookup", kwlist, + &required, &provided, &name, &default_)) + return NULL; + + return _lookup(self, required, provided, name, default_); +} + + +/* + def lookup1(self, required, provided, name=u'', default=None): + cache = self._getcache(provided, name) + result = cache.get(required, _not_in_mapping) + if result is _not_in_mapping: + return self.lookup((required, ), provided, name, default) + + if result is None: + return default + + return result +*/ +static PyObject * +_lookup1(lookup *self, + PyObject *required, PyObject *provided, PyObject *name, + PyObject *default_) +{ + PyObject *result, *cache; + + if ( name && !PyUnicode_Check(name) ) + { + PyErr_SetString(PyExc_ValueError, + "name is not a string or unicode"); + return NULL; + } + + cache = _getcache(self, provided, name); + if (cache == NULL) + return NULL; + + result = PyDict_GetItem(cache, required); + if (result == NULL) + { + PyObject *tup; + + tup = PyTuple_New(1); + if (tup == NULL) + return NULL; + Py_INCREF(required); + PyTuple_SET_ITEM(tup, 0, required); + result = _lookup(self, tup, provided, name, default_); + Py_DECREF(tup); + } + else + { + if (result == Py_None && default_ != NULL) + { + result = default_; + } + Py_INCREF(result); + } + + return result; +} +static PyObject * +lookup_lookup1(lookup *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"required", "provided", "name", "default", NULL}; + PyObject *required, *provided, *name=NULL, *default_=NULL; + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO|OO:LookupBase.lookup1", kwlist, + &required, &provided, &name, &default_)) + return NULL; + + return _lookup1(self, required, provided, name, default_); +} + +/* + def adapter_hook(self, provided, object, name=u'', default=None): + required = providedBy(object) + cache = self._getcache(provided, name) + factory = cache.get(required, _not_in_mapping) + if factory is _not_in_mapping: + factory = self.lookup((required, ), provided, name) + + if factory is not None: + if isinstance(object, super): + object = object.__self__ + result = factory(object) + if result is not None: + return result + + return default +*/ +static PyObject * +_adapter_hook(lookup *self, + PyObject *provided, PyObject *object, PyObject *name, + PyObject *default_) +{ + PyObject *required, *factory, *result; + + if ( name && !PyUnicode_Check(name) ) + { + PyErr_SetString(PyExc_ValueError, + "name is not a string or unicode"); + return NULL; + } + + required = providedBy(NULL, object); + if (required == NULL) + return NULL; + + factory = _lookup1(self, required, provided, name, Py_None); + Py_DECREF(required); + if (factory == NULL) + return NULL; + + if (factory != Py_None) + { + if (PyObject_TypeCheck(object, &PySuper_Type)) { + PyObject* self = PyObject_GetAttr(object, str__self__); + if (self == NULL) + { + Py_DECREF(factory); + return NULL; + } + // Borrow the reference to self + Py_DECREF(self); + object = self; + } + result = PyObject_CallFunctionObjArgs(factory, object, NULL); + Py_DECREF(factory); + if (result == NULL || result != Py_None) + return result; + } + else + result = factory; /* None */ + + if (default_ == NULL || default_ == result) /* No default specified, */ + return result; /* Return None. result is owned None */ + + Py_DECREF(result); + Py_INCREF(default_); + + return default_; +} +static PyObject * +lookup_adapter_hook(lookup *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"provided", "object", "name", "default", NULL}; + PyObject *object, *provided, *name=NULL, *default_=NULL; + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO|OO:LookupBase.adapter_hook", kwlist, + &provided, &object, &name, &default_)) + return NULL; + + return _adapter_hook(self, provided, object, name, default_); +} + +static PyObject * +lookup_queryAdapter(lookup *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"object", "provided", "name", "default", NULL}; + PyObject *object, *provided, *name=NULL, *default_=NULL; + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO|OO:LookupBase.queryAdapter", kwlist, + &object, &provided, &name, &default_)) + return NULL; + + return _adapter_hook(self, provided, object, name, default_); +} + +/* + def lookupAll(self, required, provided): + cache = self._mcache.get(provided) + if cache is None: + cache = {} + self._mcache[provided] = cache + + required = tuple(required) + result = cache.get(required, _not_in_mapping) + if result is _not_in_mapping: + result = self._uncached_lookupAll(required, provided) + cache[required] = result + + return result +*/ +static PyObject * +_lookupAll(lookup *self, PyObject *required, PyObject *provided) +{ + PyObject *cache, *result; + + /* resolve before getting cache. See note in _lookup. */ + required = PySequence_Tuple(required); + if (required == NULL) + return NULL; + + ASSURE_DICT(self->_mcache); + cache = _subcache(self->_mcache, provided); + if (cache == NULL) + return NULL; + + result = PyDict_GetItem(cache, required); + if (result == NULL) + { + int status; + + result = PyObject_CallMethodObjArgs(OBJECT(self), str_uncached_lookupAll, + required, provided, NULL); + if (result == NULL) + { + Py_DECREF(required); + return NULL; + } + status = PyDict_SetItem(cache, required, result); + Py_DECREF(required); + if (status < 0) + { + Py_DECREF(result); + return NULL; + } + } + else + { + Py_INCREF(result); + Py_DECREF(required); + } + + return result; +} +static PyObject * +lookup_lookupAll(lookup *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"required", "provided", NULL}; + PyObject *required, *provided; + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO:LookupBase.lookupAll", kwlist, + &required, &provided)) + return NULL; + + return _lookupAll(self, required, provided); +} + +/* + def subscriptions(self, required, provided): + cache = self._scache.get(provided) + if cache is None: + cache = {} + self._scache[provided] = cache + + required = tuple(required) + result = cache.get(required, _not_in_mapping) + if result is _not_in_mapping: + result = self._uncached_subscriptions(required, provided) + cache[required] = result + + return result +*/ +static PyObject * +_subscriptions(lookup *self, PyObject *required, PyObject *provided) +{ + PyObject *cache, *result; + + /* resolve before getting cache. See note in _lookup. */ + required = PySequence_Tuple(required); + if (required == NULL) + return NULL; + + ASSURE_DICT(self->_scache); + cache = _subcache(self->_scache, provided); + if (cache == NULL) + return NULL; + + result = PyDict_GetItem(cache, required); + if (result == NULL) + { + int status; + + result = PyObject_CallMethodObjArgs( + OBJECT(self), str_uncached_subscriptions, + required, provided, NULL); + if (result == NULL) + { + Py_DECREF(required); + return NULL; + } + status = PyDict_SetItem(cache, required, result); + Py_DECREF(required); + if (status < 0) + { + Py_DECREF(result); + return NULL; + } + } + else + { + Py_INCREF(result); + Py_DECREF(required); + } + + return result; +} +static PyObject * +lookup_subscriptions(lookup *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"required", "provided", NULL}; + PyObject *required, *provided; + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, + &required, &provided)) + return NULL; + + return _subscriptions(self, required, provided); +} + +static struct PyMethodDef lookup_methods[] = { + {"changed", (PyCFunction)lookup_changed, METH_O, ""}, + {"lookup", (PyCFunction)lookup_lookup, METH_KEYWORDS | METH_VARARGS, ""}, + {"lookup1", (PyCFunction)lookup_lookup1, METH_KEYWORDS | METH_VARARGS, ""}, + {"queryAdapter", (PyCFunction)lookup_queryAdapter, METH_KEYWORDS | METH_VARARGS, ""}, + {"adapter_hook", (PyCFunction)lookup_adapter_hook, METH_KEYWORDS | METH_VARARGS, ""}, + {"lookupAll", (PyCFunction)lookup_lookupAll, METH_KEYWORDS | METH_VARARGS, ""}, + {"subscriptions", (PyCFunction)lookup_subscriptions, METH_KEYWORDS | METH_VARARGS, ""}, + {NULL, NULL} /* sentinel */ +}; + +static PyTypeObject LookupBase = { + PyVarObject_HEAD_INIT(NULL, 0) + /* tp_name */ "_zope_interface_coptimizations." + "LookupBase", + /* tp_basicsize */ sizeof(lookup), + /* tp_itemsize */ 0, + /* tp_dealloc */ (destructor)&lookup_dealloc, + /* tp_print */ (printfunc)0, + /* tp_getattr */ (getattrfunc)0, + /* tp_setattr */ (setattrfunc)0, + /* tp_compare */ 0, + /* tp_repr */ (reprfunc)0, + /* tp_as_number */ 0, + /* tp_as_sequence */ 0, + /* tp_as_mapping */ 0, + /* tp_hash */ (hashfunc)0, + /* tp_call */ (ternaryfunc)0, + /* tp_str */ (reprfunc)0, + /* tp_getattro */ (getattrofunc)0, + /* tp_setattro */ (setattrofunc)0, + /* tp_as_buffer */ 0, + /* tp_flags */ Py_TPFLAGS_DEFAULT + | Py_TPFLAGS_BASETYPE + | Py_TPFLAGS_HAVE_GC, + /* tp_doc */ "", + /* tp_traverse */ (traverseproc)lookup_traverse, + /* tp_clear */ (inquiry)lookup_clear, + /* tp_richcompare */ (richcmpfunc)0, + /* tp_weaklistoffset */ (long)0, + /* tp_iter */ (getiterfunc)0, + /* tp_iternext */ (iternextfunc)0, + /* tp_methods */ lookup_methods, +}; + +static int +verifying_traverse(verify *self, visitproc visit, void *arg) +{ + int vret; + + vret = lookup_traverse((lookup *)self, visit, arg); + if (vret != 0) + return vret; + + if (self->_verify_ro) { + vret = visit(self->_verify_ro, arg); + if (vret != 0) + return vret; + } + if (self->_verify_generations) { + vret = visit(self->_verify_generations, arg); + if (vret != 0) + return vret; + } + + return 0; +} + +static int +verifying_clear(verify *self) +{ + lookup_clear((lookup *)self); + Py_CLEAR(self->_verify_generations); + Py_CLEAR(self->_verify_ro); + return 0; +} + + +static void +verifying_dealloc(verify *self) +{ + PyObject_GC_UnTrack((PyObject *)self); + verifying_clear(self); + Py_TYPE(self)->tp_free((PyObject*)self); +} + +/* + def changed(self, originally_changed): + super(VerifyingBasePy, self).changed(originally_changed) + self._verify_ro = self._registry.ro[1:] + self._verify_generations = [r._generation for r in self._verify_ro] +*/ +static PyObject * +_generations_tuple(PyObject *ro) +{ + int i, l; + PyObject *generations; + + l = PyTuple_GET_SIZE(ro); + generations = PyTuple_New(l); + for (i=0; i < l; i++) + { + PyObject *generation; + + generation = PyObject_GetAttr(PyTuple_GET_ITEM(ro, i), str_generation); + if (generation == NULL) + { + Py_DECREF(generations); + return NULL; + } + PyTuple_SET_ITEM(generations, i, generation); + } + + return generations; +} +static PyObject * +verifying_changed(verify *self, PyObject *ignored) +{ + PyObject *t, *ro; + + verifying_clear(self); + + t = PyObject_GetAttr(OBJECT(self), str_registry); + if (t == NULL) + return NULL; + ro = PyObject_GetAttr(t, strro); + Py_DECREF(t); + if (ro == NULL) + return NULL; + + t = PyObject_CallFunctionObjArgs(OBJECT(&PyTuple_Type), ro, NULL); + Py_DECREF(ro); + if (t == NULL) + return NULL; + + ro = PyTuple_GetSlice(t, 1, PyTuple_GET_SIZE(t)); + Py_DECREF(t); + if (ro == NULL) + return NULL; + + self->_verify_generations = _generations_tuple(ro); + if (self->_verify_generations == NULL) + { + Py_DECREF(ro); + return NULL; + } + + self->_verify_ro = ro; + + Py_INCREF(Py_None); + return Py_None; +} + +/* + def _verify(self): + if ([r._generation for r in self._verify_ro] + != self._verify_generations): + self.changed(None) +*/ +static int +_verify(verify *self) +{ + PyObject *changed_result; + + if (self->_verify_ro != NULL && self->_verify_generations != NULL) + { + PyObject *generations; + int changed; + + generations = _generations_tuple(self->_verify_ro); + if (generations == NULL) + return -1; + + changed = PyObject_RichCompareBool(self->_verify_generations, + generations, Py_NE); + Py_DECREF(generations); + if (changed == -1) + return -1; + + if (changed == 0) + return 0; + } + + changed_result = PyObject_CallMethodObjArgs(OBJECT(self), strchanged, + Py_None, NULL); + if (changed_result == NULL) + return -1; + + Py_DECREF(changed_result); + return 0; +} + +static PyObject * +verifying_lookup(verify *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"required", "provided", "name", "default", NULL}; + PyObject *required, *provided, *name=NULL, *default_=NULL; + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO|OO", kwlist, + &required, &provided, &name, &default_)) + return NULL; + + if (_verify(self) < 0) + return NULL; + + return _lookup((lookup *)self, required, provided, name, default_); +} + +static PyObject * +verifying_lookup1(verify *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"required", "provided", "name", "default", NULL}; + PyObject *required, *provided, *name=NULL, *default_=NULL; + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO|OO", kwlist, + &required, &provided, &name, &default_)) + return NULL; + + if (_verify(self) < 0) + return NULL; + + return _lookup1((lookup *)self, required, provided, name, default_); +} + +static PyObject * +verifying_adapter_hook(verify *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"provided", "object", "name", "default", NULL}; + PyObject *object, *provided, *name=NULL, *default_=NULL; + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO|OO", kwlist, + &provided, &object, &name, &default_)) + return NULL; + + if (_verify(self) < 0) + return NULL; + + return _adapter_hook((lookup *)self, provided, object, name, default_); +} + +static PyObject * +verifying_queryAdapter(verify *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"object", "provided", "name", "default", NULL}; + PyObject *object, *provided, *name=NULL, *default_=NULL; + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO|OO", kwlist, + &object, &provided, &name, &default_)) + return NULL; + + if (_verify(self) < 0) + return NULL; + + return _adapter_hook((lookup *)self, provided, object, name, default_); +} + +static PyObject * +verifying_lookupAll(verify *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"required", "provided", NULL}; + PyObject *required, *provided; + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, + &required, &provided)) + return NULL; + + if (_verify(self) < 0) + return NULL; + + return _lookupAll((lookup *)self, required, provided); +} + +static PyObject * +verifying_subscriptions(verify *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"required", "provided", NULL}; + PyObject *required, *provided; + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, + &required, &provided)) + return NULL; + + if (_verify(self) < 0) + return NULL; + + return _subscriptions((lookup *)self, required, provided); +} + +static struct PyMethodDef verifying_methods[] = { + {"changed", (PyCFunction)verifying_changed, METH_O, ""}, + {"lookup", (PyCFunction)verifying_lookup, METH_KEYWORDS | METH_VARARGS, ""}, + {"lookup1", (PyCFunction)verifying_lookup1, METH_KEYWORDS | METH_VARARGS, ""}, + {"queryAdapter", (PyCFunction)verifying_queryAdapter, METH_KEYWORDS | METH_VARARGS, ""}, + {"adapter_hook", (PyCFunction)verifying_adapter_hook, METH_KEYWORDS | METH_VARARGS, ""}, + {"lookupAll", (PyCFunction)verifying_lookupAll, METH_KEYWORDS | METH_VARARGS, ""}, + {"subscriptions", (PyCFunction)verifying_subscriptions, METH_KEYWORDS | METH_VARARGS, ""}, + {NULL, NULL} /* sentinel */ +}; + +static PyTypeObject VerifyingBase = { + PyVarObject_HEAD_INIT(NULL, 0) + /* tp_name */ "_zope_interface_coptimizations." + "VerifyingBase", + /* tp_basicsize */ sizeof(verify), + /* tp_itemsize */ 0, + /* tp_dealloc */ (destructor)&verifying_dealloc, + /* tp_print */ (printfunc)0, + /* tp_getattr */ (getattrfunc)0, + /* tp_setattr */ (setattrfunc)0, + /* tp_compare */ 0, + /* tp_repr */ (reprfunc)0, + /* tp_as_number */ 0, + /* tp_as_sequence */ 0, + /* tp_as_mapping */ 0, + /* tp_hash */ (hashfunc)0, + /* tp_call */ (ternaryfunc)0, + /* tp_str */ (reprfunc)0, + /* tp_getattro */ (getattrofunc)0, + /* tp_setattro */ (setattrofunc)0, + /* tp_as_buffer */ 0, + /* tp_flags */ Py_TPFLAGS_DEFAULT + | Py_TPFLAGS_BASETYPE + | Py_TPFLAGS_HAVE_GC, + /* tp_doc */ "", + /* tp_traverse */ (traverseproc)verifying_traverse, + /* tp_clear */ (inquiry)verifying_clear, + /* tp_richcompare */ (richcmpfunc)0, + /* tp_weaklistoffset */ (long)0, + /* tp_iter */ (getiterfunc)0, + /* tp_iternext */ (iternextfunc)0, + /* tp_methods */ verifying_methods, + /* tp_members */ 0, + /* tp_getset */ 0, + /* tp_base */ &LookupBase, +}; + +/* ========================== End: Lookup Bases ======================= */ +/* ==================================================================== */ + + + +static struct PyMethodDef m_methods[] = { + {"implementedBy", (PyCFunction)implementedBy, METH_O, + "Interfaces implemented by a class or factory.\n" + "Raises TypeError if argument is neither a class nor a callable."}, + {"getObjectSpecification", (PyCFunction)getObjectSpecification, METH_O, + "Get an object's interfaces (internal api)"}, + {"providedBy", (PyCFunction)providedBy, METH_O, + "Get an object's interfaces"}, + + {NULL, (PyCFunction)NULL, 0, NULL} /* sentinel */ +}; + +#if PY_MAJOR_VERSION >= 3 +static char module_doc[] = "C optimizations for zope.interface\n\n"; + +static struct PyModuleDef _zic_module = { + PyModuleDef_HEAD_INIT, + "_zope_interface_coptimizations", + module_doc, + -1, + m_methods, + NULL, + NULL, + NULL, + NULL +}; +#endif + +static PyObject * +init(void) +{ + PyObject *m; + +#if PY_MAJOR_VERSION < 3 +#define DEFINE_STRING(S) \ + if(! (str ## S = PyString_FromString(# S))) return NULL +#else +#define DEFINE_STRING(S) \ + if(! (str ## S = PyUnicode_FromString(# S))) return NULL +#endif + + DEFINE_STRING(__dict__); + DEFINE_STRING(__implemented__); + DEFINE_STRING(__provides__); + DEFINE_STRING(__class__); + DEFINE_STRING(__providedBy__); + DEFINE_STRING(extends); + DEFINE_STRING(__conform__); + DEFINE_STRING(_call_conform); + DEFINE_STRING(_uncached_lookup); + DEFINE_STRING(_uncached_lookupAll); + DEFINE_STRING(_uncached_subscriptions); + DEFINE_STRING(_registry); + DEFINE_STRING(_generation); + DEFINE_STRING(ro); + DEFINE_STRING(changed); + DEFINE_STRING(__self__); + DEFINE_STRING(__name__); + DEFINE_STRING(__module__); + DEFINE_STRING(__adapt__); + DEFINE_STRING(_CALL_CUSTOM_ADAPT); +#undef DEFINE_STRING + adapter_hooks = PyList_New(0); + if (adapter_hooks == NULL) + return NULL; + + /* Initialize types: */ + SpecificationBaseType.tp_new = PyBaseObject_Type.tp_new; + if (PyType_Ready(&SpecificationBaseType) < 0) + return NULL; + OSDType.tp_new = PyBaseObject_Type.tp_new; + if (PyType_Ready(&OSDType) < 0) + return NULL; + CPBType.tp_new = PyBaseObject_Type.tp_new; + if (PyType_Ready(&CPBType) < 0) + return NULL; + + InterfaceBaseType.tp_new = PyBaseObject_Type.tp_new; + if (PyType_Ready(&InterfaceBaseType) < 0) + return NULL; + + LookupBase.tp_new = PyBaseObject_Type.tp_new; + if (PyType_Ready(&LookupBase) < 0) + return NULL; + + VerifyingBase.tp_new = PyBaseObject_Type.tp_new; + if (PyType_Ready(&VerifyingBase) < 0) + return NULL; + + #if PY_MAJOR_VERSION < 3 + /* Create the module and add the functions */ + m = Py_InitModule3("_zope_interface_coptimizations", m_methods, + "C optimizations for zope.interface\n\n"); + #else + m = PyModule_Create(&_zic_module); + #endif + if (m == NULL) + return NULL; + + /* Add types: */ + if (PyModule_AddObject(m, "SpecificationBase", OBJECT(&SpecificationBaseType)) < 0) + return NULL; + if (PyModule_AddObject(m, "ObjectSpecificationDescriptor", + (PyObject *)&OSDType) < 0) + return NULL; + if (PyModule_AddObject(m, "ClassProvidesBase", OBJECT(&CPBType)) < 0) + return NULL; + if (PyModule_AddObject(m, "InterfaceBase", OBJECT(&InterfaceBaseType)) < 0) + return NULL; + if (PyModule_AddObject(m, "LookupBase", OBJECT(&LookupBase)) < 0) + return NULL; + if (PyModule_AddObject(m, "VerifyingBase", OBJECT(&VerifyingBase)) < 0) + return NULL; + if (PyModule_AddObject(m, "adapter_hooks", adapter_hooks) < 0) + return NULL; + return m; +} + +PyMODINIT_FUNC +#if PY_MAJOR_VERSION < 3 +init_zope_interface_coptimizations(void) +{ + init(); +} +#else +PyInit__zope_interface_coptimizations(void) +{ + return init(); +} +#endif + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif diff --git a/contrib/python/zope.interface/py3/zope/interface/adapter.py b/contrib/python/zope.interface/py3/zope/interface/adapter.py new file mode 100644 index 0000000000..dbff0d19da --- /dev/null +++ b/contrib/python/zope.interface/py3/zope/interface/adapter.py @@ -0,0 +1,1015 @@ +############################################################################## +# +# Copyright (c) 2004 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. +# +############################################################################## +"""Adapter management +""" +import itertools +import weakref + +from zope.interface import implementer +from zope.interface import providedBy +from zope.interface import Interface +from zope.interface import ro +from zope.interface.interfaces import IAdapterRegistry + +from zope.interface._compat import _normalize_name +from zope.interface._compat import _use_c_impl + +__all__ = [ + 'AdapterRegistry', + 'VerifyingAdapterRegistry', +] + +# In the CPython implementation, +# ``tuple`` and ``list`` cooperate so that ``tuple([some list])`` +# directly allocates and iterates at the C level without using a +# Python iterator. That's not the case for +# ``tuple(generator_expression)`` or ``tuple(map(func, it))``. +## +# 3.8 +# ``tuple([t for t in range(10)])`` -> 610ns +# ``tuple(t for t in range(10))`` -> 696ns +# ``tuple(map(lambda t: t, range(10)))`` -> 881ns +## +# 2.7 +# ``tuple([t fon t in range(10)])`` -> 625ns +# ``tuple(t for t in range(10))`` -> 665ns +# ``tuple(map(lambda t: t, range(10)))`` -> 958ns +# +# All three have substantial variance. +## +# On PyPy, this is also the best option. +## +# PyPy 2.7.18-7.3.3 +# ``tuple([t fon t in range(10)])`` -> 128ns +# ``tuple(t for t in range(10))`` -> 175ns +# ``tuple(map(lambda t: t, range(10)))`` -> 153ns +## +# PyPy 3.7.9 7.3.3-beta +# ``tuple([t fon t in range(10)])`` -> 82ns +# ``tuple(t for t in range(10))`` -> 177ns +# ``tuple(map(lambda t: t, range(10)))`` -> 168ns +# + +class BaseAdapterRegistry: + """ + A basic implementation of the data storage and algorithms required + for a :class:`zope.interface.interfaces.IAdapterRegistry`. + + Subclasses can set the following attributes to control how the data + is stored; in particular, these hooks can be helpful for ZODB + persistence. They can be class attributes that are the named (or similar) type, or + they can be methods that act as a constructor for an object that behaves + like the types defined here; this object will not assume that they are type + objects, but subclasses are free to do so: + + _sequenceType = list + This is the type used for our two mutable top-level "byorder" sequences. + Must support mutation operations like ``append()`` and ``del seq[index]``. + These are usually small (< 10). Although at least one of them is + accessed when performing lookups or queries on this object, the other + is untouched. In many common scenarios, both are only required when + mutating registrations and subscriptions (like what + :meth:`zope.interface.interfaces.IComponents.registerUtility` does). + This use pattern makes it an ideal candidate to be a + :class:`~persistent.list.PersistentList`. + _leafSequenceType = tuple + This is the type used for the leaf sequences of subscribers. + It could be set to a ``PersistentList`` to avoid many unnecessary data + loads when subscribers aren't being used. Mutation operations are directed + through :meth:`_addValueToLeaf` and :meth:`_removeValueFromLeaf`; if you use + a mutable type, you'll need to override those. + _mappingType = dict + This is the mutable mapping type used for the keyed mappings. + A :class:`~persistent.mapping.PersistentMapping` + could be used to help reduce the number of data loads when the registry is large + and parts of it are rarely used. Further reductions in data loads can come from + using a :class:`~BTrees.OOBTree.OOBTree`, but care is required + to be sure that all required/provided + values are fully ordered (e.g., no required or provided values that are classes + can be used). + _providedType = dict + This is the mutable mapping type used for the ``_provided`` mapping. + This is separate from the generic mapping type because the values + are always integers, so one might choose to use a more optimized data + structure such as a :class:`~BTrees.OIBTree.OIBTree`. + The same caveats regarding key types + apply as for ``_mappingType``. + + It is possible to also set these on an instance, but because of the need to + potentially also override :meth:`_addValueToLeaf` and :meth:`_removeValueFromLeaf`, + this may be less useful in a persistent scenario; using a subclass is recommended. + + .. versionchanged:: 5.3.0 + Add support for customizing the way internal data + structures are created. + .. versionchanged:: 5.3.0 + Add methods :meth:`rebuild`, :meth:`allRegistrations` + and :meth:`allSubscriptions`. + """ + + # List of methods copied from lookup sub-objects: + _delegated = ('lookup', 'queryMultiAdapter', 'lookup1', 'queryAdapter', + 'adapter_hook', 'lookupAll', 'names', + 'subscriptions', 'subscribers') + + # All registries maintain a generation that can be used by verifying + # registries + _generation = 0 + + def __init__(self, bases=()): + + # The comments here could be improved. Possibly this bit needs + # explaining in a separate document, as the comments here can + # be quite confusing. /regebro + + # {order -> {required -> {provided -> {name -> value}}}} + # Here "order" is actually an index in a list, "required" and + # "provided" are interfaces, and "required" is really a nested + # key. So, for example: + # for order == 0 (that is, self._adapters[0]), we have: + # {provided -> {name -> value}} + # but for order == 2 (that is, self._adapters[2]), we have: + # {r1 -> {r2 -> {provided -> {name -> value}}}} + # + self._adapters = self._sequenceType() + + # {order -> {required -> {provided -> {name -> [value]}}}} + # where the remarks about adapters above apply + self._subscribers = self._sequenceType() + + # Set, with a reference count, keeping track of the interfaces + # for which we have provided components: + self._provided = self._providedType() + + # Create ``_v_lookup`` object to perform lookup. We make this a + # separate object to to make it easier to implement just the + # lookup functionality in C. This object keeps track of cache + # invalidation data in two kinds of registries. + + # Invalidating registries have caches that are invalidated + # when they or their base registies change. An invalidating + # registry can only have invalidating registries as bases. + # See LookupBaseFallback below for the pertinent logic. + + # Verifying registies can't rely on getting invalidation messages, + # so have to check the generations of base registries to determine + # if their cache data are current. See VerifyingBasePy below + # for the pertinent object. + self._createLookup() + + # Setting the bases causes the registries described above + # to be initialized (self._setBases -> self.changed -> + # self._v_lookup.changed). + + self.__bases__ = bases + + def _setBases(self, bases): + """ + If subclasses need to track when ``__bases__`` changes, they + can override this method. + + Subclasses must still call this method. + """ + self.__dict__['__bases__'] = bases + self.ro = ro.ro(self) + self.changed(self) + + __bases__ = property(lambda self: self.__dict__['__bases__'], + lambda self, bases: self._setBases(bases), + ) + + def _createLookup(self): + self._v_lookup = self.LookupClass(self) + for name in self._delegated: + self.__dict__[name] = getattr(self._v_lookup, name) + + # Hooks for subclasses to define the types of objects used in + # our data structures. + # These have to be documented in the docstring, instead of local + # comments, because Sphinx autodoc ignores the comment and just writes + # "alias of list" + _sequenceType = list + _leafSequenceType = tuple + _mappingType = dict + _providedType = dict + + def _addValueToLeaf(self, existing_leaf_sequence, new_item): + """ + Add the value *new_item* to the *existing_leaf_sequence*, which may + be ``None``. + + Subclasses that redefine `_leafSequenceType` should override this method. + + :param existing_leaf_sequence: + If *existing_leaf_sequence* is not *None*, it will be an instance + of `_leafSequenceType`. (Unless the object has been unpickled + from an old pickle and the class definition has changed, in which case + it may be an instance of a previous definition, commonly a `tuple`.) + + :return: + This method returns the new value to be stored. It may mutate the + sequence in place if it was not ``None`` and the type is mutable, but + it must also return it. + + .. versionadded:: 5.3.0 + """ + if existing_leaf_sequence is None: + return (new_item,) + return existing_leaf_sequence + (new_item,) + + def _removeValueFromLeaf(self, existing_leaf_sequence, to_remove): + """ + Remove the item *to_remove* from the (non-``None``, non-empty) + *existing_leaf_sequence* and return the mutated sequence. + + If there is more than one item that is equal to *to_remove* + they must all be removed. + + Subclasses that redefine `_leafSequenceType` should override + this method. Note that they can call this method to help + in their implementation; this implementation will always + return a new tuple constructed by iterating across + the *existing_leaf_sequence* and omitting items equal to *to_remove*. + + :param existing_leaf_sequence: + As for `_addValueToLeaf`, probably an instance of + `_leafSequenceType` but possibly an older type; never `None`. + :return: + A version of *existing_leaf_sequence* with all items equal to + *to_remove* removed. Must not return `None`. However, + returning an empty + object, even of another type such as the empty tuple, ``()`` is + explicitly allowed; such an object will never be stored. + + .. versionadded:: 5.3.0 + """ + return tuple([v for v in existing_leaf_sequence if v != to_remove]) + + def changed(self, originally_changed): + self._generation += 1 + self._v_lookup.changed(originally_changed) + + def register(self, required, provided, name, value): + if not isinstance(name, str): + raise ValueError('name is not a string') + if value is None: + self.unregister(required, provided, name, value) + return + + required = tuple([_convert_None_to_Interface(r) for r in required]) + name = _normalize_name(name) + order = len(required) + byorder = self._adapters + while len(byorder) <= order: + byorder.append(self._mappingType()) + components = byorder[order] + key = required + (provided,) + + for k in key: + d = components.get(k) + if d is None: + d = self._mappingType() + components[k] = d + components = d + + if components.get(name) is value: + return + + components[name] = value + + n = self._provided.get(provided, 0) + 1 + self._provided[provided] = n + if n == 1: + self._v_lookup.add_extendor(provided) + + self.changed(self) + + def _find_leaf(self, byorder, required, provided, name): + # Find the leaf value, if any, in the *byorder* list + # for the interface sequence *required* and the interface + # *provided*, given the already normalized *name*. + # + # If no such leaf value exists, returns ``None`` + required = tuple([_convert_None_to_Interface(r) for r in required]) + order = len(required) + if len(byorder) <= order: + return None + + components = byorder[order] + key = required + (provided,) + + for k in key: + d = components.get(k) + if d is None: + return None + components = d + + return components.get(name) + + def registered(self, required, provided, name=''): + return self._find_leaf( + self._adapters, + required, + provided, + _normalize_name(name) + ) + + @classmethod + def _allKeys(cls, components, i, parent_k=()): + if i == 0: + for k, v in components.items(): + yield parent_k + (k,), v + else: + for k, v in components.items(): + new_parent_k = parent_k + (k,) + yield from cls._allKeys(v, i - 1, new_parent_k) + + def _all_entries(self, byorder): + # Recurse through the mapping levels of the `byorder` sequence, + # reconstructing a flattened sequence of ``(required, provided, name, value)`` + # tuples that can be used to reconstruct the sequence with the appropriate + # registration methods. + # + # Locally reference the `byorder` data; it might be replaced while + # this method is running (see ``rebuild``). + for i, components in enumerate(byorder): + # We will have *i* levels of dictionaries to go before + # we get to the leaf. + for key, value in self._allKeys(components, i + 1): + assert len(key) == i + 2 + required = key[:i] + provided = key[-2] + name = key[-1] + yield (required, provided, name, value) + + def allRegistrations(self): + """ + Yields tuples ``(required, provided, name, value)`` for all + the registrations that this object holds. + + These tuples could be passed as the arguments to the + :meth:`register` method on another adapter registry to + duplicate the registrations this object holds. + + .. versionadded:: 5.3.0 + """ + yield from self._all_entries(self._adapters) + + def unregister(self, required, provided, name, value=None): + required = tuple([_convert_None_to_Interface(r) for r in required]) + order = len(required) + byorder = self._adapters + if order >= len(byorder): + return False + components = byorder[order] + key = required + (provided,) + + # Keep track of how we got to `components`: + lookups = [] + for k in key: + d = components.get(k) + if d is None: + return + lookups.append((components, k)) + components = d + + old = components.get(name) + if old is None: + return + if (value is not None) and (old is not value): + return + + del components[name] + if not components: + # Clean out empty containers, since we don't want our keys + # to reference global objects (interfaces) unnecessarily. + # This is often a problem when an interface is slated for + # removal; a hold-over entry in the registry can make it + # difficult to remove such interfaces. + for comp, k in reversed(lookups): + d = comp[k] + if d: + break + else: + del comp[k] + while byorder and not byorder[-1]: + del byorder[-1] + n = self._provided[provided] - 1 + if n == 0: + del self._provided[provided] + self._v_lookup.remove_extendor(provided) + else: + self._provided[provided] = n + + self.changed(self) + + def subscribe(self, required, provided, value): + required = tuple([_convert_None_to_Interface(r) for r in required]) + name = '' + order = len(required) + byorder = self._subscribers + while len(byorder) <= order: + byorder.append(self._mappingType()) + components = byorder[order] + key = required + (provided,) + + for k in key: + d = components.get(k) + if d is None: + d = self._mappingType() + components[k] = d + components = d + + components[name] = self._addValueToLeaf(components.get(name), value) + + if provided is not None: + n = self._provided.get(provided, 0) + 1 + self._provided[provided] = n + if n == 1: + self._v_lookup.add_extendor(provided) + + self.changed(self) + + def subscribed(self, required, provided, subscriber): + subscribers = self._find_leaf( + self._subscribers, + required, + provided, + '' + ) or () + return subscriber if subscriber in subscribers else None + + def allSubscriptions(self): + """ + Yields tuples ``(required, provided, value)`` for all the + subscribers that this object holds. + + These tuples could be passed as the arguments to the + :meth:`subscribe` method on another adapter registry to + duplicate the registrations this object holds. + + .. versionadded:: 5.3.0 + """ + for required, provided, _name, value in self._all_entries(self._subscribers): + for v in value: + yield (required, provided, v) + + def unsubscribe(self, required, provided, value=None): + required = tuple([_convert_None_to_Interface(r) for r in required]) + order = len(required) + byorder = self._subscribers + if order >= len(byorder): + return + components = byorder[order] + key = required + (provided,) + + # Keep track of how we got to `components`: + lookups = [] + for k in key: + d = components.get(k) + if d is None: + return + lookups.append((components, k)) + components = d + + old = components.get('') + if not old: + # this is belt-and-suspenders against the failure of cleanup below + return # pragma: no cover + len_old = len(old) + if value is None: + # Removing everything; note that the type of ``new`` won't + # necessarily match the ``_leafSequenceType``, but that's + # OK because we're about to delete the entire entry + # anyway. + new = () + else: + new = self._removeValueFromLeaf(old, value) + # ``new`` may be the same object as ``old``, just mutated in place, + # so we cannot compare it to ``old`` to check for changes. Remove + # our reference to it now to avoid trying to do so below. + del old + + if len(new) == len_old: + # No changes, so nothing could have been removed. + return + + if new: + components[''] = new + else: + # Instead of setting components[u''] = new, we clean out + # empty containers, since we don't want our keys to + # reference global objects (interfaces) unnecessarily. This + # is often a problem when an interface is slated for + # removal; a hold-over entry in the registry can make it + # difficult to remove such interfaces. + del components[''] + for comp, k in reversed(lookups): + d = comp[k] + if d: + break + else: + del comp[k] + while byorder and not byorder[-1]: + del byorder[-1] + + if provided is not None: + n = self._provided[provided] + len(new) - len_old + if n == 0: + del self._provided[provided] + self._v_lookup.remove_extendor(provided) + else: + self._provided[provided] = n + + self.changed(self) + + def rebuild(self): + """ + Rebuild (and replace) all the internal data structures of this + object. + + This is useful, especially for persistent implementations, if + you suspect an issue with reference counts keeping interfaces + alive even though they are no longer used. + + It is also useful if you or a subclass change the data types + (``_mappingType`` and friends) that are to be used. + + This method replaces all internal data structures with new objects; + it specifically does not re-use any storage. + + .. versionadded:: 5.3.0 + """ + + # Grab the iterators, we're about to discard their data. + registrations = self.allRegistrations() + subscriptions = self.allSubscriptions() + + def buffer(it): + # The generator doesn't actually start running until we + # ask for its next(), by which time the attributes will change + # unless we do so before calling __init__. + try: + first = next(it) + except StopIteration: + return iter(()) + + return itertools.chain((first,), it) + + registrations = buffer(registrations) + subscriptions = buffer(subscriptions) + + + # Replace the base data structures as well as _v_lookup. + self.__init__(self.__bases__) + # Re-register everything previously registered and subscribed. + # + # XXX: This is going to call ``self.changed()`` a lot, all of + # which is unnecessary (because ``self.__init__`` just + # re-created those dependent objects and also called + # ``self.changed()``). Is this a bottleneck that needs fixed? + # (We could do ``self.changed = lambda _: None`` before + # beginning and remove it after to disable the presumably expensive + # part of passing that notification to the change of objects.) + for args in registrations: + self.register(*args) + for args in subscriptions: + self.subscribe(*args) + + # XXX hack to fake out twisted's use of a private api. We need to get them + # to use the new registered method. + def get(self, _): # pragma: no cover + class XXXTwistedFakeOut: + selfImplied = {} + return XXXTwistedFakeOut + + +_not_in_mapping = object() + +@_use_c_impl +class LookupBase: + + def __init__(self): + self._cache = {} + self._mcache = {} + self._scache = {} + + def changed(self, ignored=None): + self._cache.clear() + self._mcache.clear() + self._scache.clear() + + def _getcache(self, provided, name): + cache = self._cache.get(provided) + if cache is None: + cache = {} + self._cache[provided] = cache + if name: + c = cache.get(name) + if c is None: + c = {} + cache[name] = c + cache = c + return cache + + def lookup(self, required, provided, name='', default=None): + if not isinstance(name, str): + raise ValueError('name is not a string') + cache = self._getcache(provided, name) + required = tuple(required) + if len(required) == 1: + result = cache.get(required[0], _not_in_mapping) + else: + result = cache.get(tuple(required), _not_in_mapping) + + if result is _not_in_mapping: + result = self._uncached_lookup(required, provided, name) + if len(required) == 1: + cache[required[0]] = result + else: + cache[tuple(required)] = result + + if result is None: + return default + + return result + + def lookup1(self, required, provided, name='', default=None): + if not isinstance(name, str): + raise ValueError('name is not a string') + cache = self._getcache(provided, name) + result = cache.get(required, _not_in_mapping) + if result is _not_in_mapping: + return self.lookup((required, ), provided, name, default) + + if result is None: + return default + + return result + + def queryAdapter(self, object, provided, name='', default=None): + return self.adapter_hook(provided, object, name, default) + + def adapter_hook(self, provided, object, name='', default=None): + if not isinstance(name, str): + raise ValueError('name is not a string') + required = providedBy(object) + cache = self._getcache(provided, name) + factory = cache.get(required, _not_in_mapping) + if factory is _not_in_mapping: + factory = self.lookup((required, ), provided, name) + + if factory is not None: + if isinstance(object, super): + object = object.__self__ + result = factory(object) + if result is not None: + return result + + return default + + def lookupAll(self, required, provided): + cache = self._mcache.get(provided) + if cache is None: + cache = {} + self._mcache[provided] = cache + + required = tuple(required) + result = cache.get(required, _not_in_mapping) + if result is _not_in_mapping: + result = self._uncached_lookupAll(required, provided) + cache[required] = result + + return result + + + def subscriptions(self, required, provided): + cache = self._scache.get(provided) + if cache is None: + cache = {} + self._scache[provided] = cache + + required = tuple(required) + result = cache.get(required, _not_in_mapping) + if result is _not_in_mapping: + result = self._uncached_subscriptions(required, provided) + cache[required] = result + + return result + + +@_use_c_impl +class VerifyingBase(LookupBaseFallback): + # Mixin for lookups against registries which "chain" upwards, and + # whose lookups invalidate their own caches whenever a parent registry + # bumps its own '_generation' counter. E.g., used by + # zope.component.persistentregistry + + def changed(self, originally_changed): + LookupBaseFallback.changed(self, originally_changed) + self._verify_ro = self._registry.ro[1:] + self._verify_generations = [r._generation for r in self._verify_ro] + + def _verify(self): + if ([r._generation for r in self._verify_ro] + != self._verify_generations): + self.changed(None) + + def _getcache(self, provided, name): + self._verify() + return LookupBaseFallback._getcache(self, provided, name) + + def lookupAll(self, required, provided): + self._verify() + return LookupBaseFallback.lookupAll(self, required, provided) + + def subscriptions(self, required, provided): + self._verify() + return LookupBaseFallback.subscriptions(self, required, provided) + + +class AdapterLookupBase: + + def __init__(self, registry): + self._registry = registry + self._required = {} + self.init_extendors() + super().__init__() + + def changed(self, ignored=None): + super().changed(None) + for r in self._required.keys(): + r = r() + if r is not None: + r.unsubscribe(self) + self._required.clear() + + + # Extendors + # --------- + + # When given an target interface for an adapter lookup, we need to consider + # adapters for interfaces that extend the target interface. This is + # what the extendors dictionary is about. It tells us all of the + # interfaces that extend an interface for which there are adapters + # registered. + + # We could separate this by order and name, thus reducing the + # number of provided interfaces to search at run time. The tradeoff, + # however, is that we have to store more information. For example, + # if the same interface is provided for multiple names and if the + # interface extends many interfaces, we'll have to keep track of + # a fair bit of information for each name. It's better to + # be space efficient here and be time efficient in the cache + # implementation. + + # TODO: add invalidation when a provided interface changes, in case + # the interface's __iro__ has changed. This is unlikely enough that + # we'll take our chances for now. + + def init_extendors(self): + self._extendors = {} + for p in self._registry._provided: + self.add_extendor(p) + + def add_extendor(self, provided): + _extendors = self._extendors + for i in provided.__iro__: + extendors = _extendors.get(i, ()) + _extendors[i] = ( + [e for e in extendors if provided.isOrExtends(e)] + + + [provided] + + + [e for e in extendors if not provided.isOrExtends(e)] + ) + + def remove_extendor(self, provided): + _extendors = self._extendors + for i in provided.__iro__: + _extendors[i] = [e for e in _extendors.get(i, ()) + if e != provided] + + + def _subscribe(self, *required): + _refs = self._required + for r in required: + ref = r.weakref() + if ref not in _refs: + r.subscribe(self) + _refs[ref] = 1 + + def _uncached_lookup(self, required, provided, name=''): + required = tuple(required) + result = None + order = len(required) + for registry in self._registry.ro: + byorder = registry._adapters + if order >= len(byorder): + continue + + extendors = registry._v_lookup._extendors.get(provided) + if not extendors: + continue + + components = byorder[order] + result = _lookup(components, required, extendors, name, 0, + order) + if result is not None: + break + + self._subscribe(*required) + + return result + + def queryMultiAdapter(self, objects, provided, name='', default=None): + factory = self.lookup([providedBy(o) for o in objects], provided, name) + if factory is None: + return default + + result = factory(*[o.__self__ if isinstance(o, super) else o for o in objects]) + if result is None: + return default + + return result + + def _uncached_lookupAll(self, required, provided): + required = tuple(required) + order = len(required) + result = {} + for registry in reversed(self._registry.ro): + byorder = registry._adapters + if order >= len(byorder): + continue + extendors = registry._v_lookup._extendors.get(provided) + if not extendors: + continue + components = byorder[order] + _lookupAll(components, required, extendors, result, 0, order) + + self._subscribe(*required) + + return tuple(result.items()) + + def names(self, required, provided): + return [c[0] for c in self.lookupAll(required, provided)] + + def _uncached_subscriptions(self, required, provided): + required = tuple(required) + order = len(required) + result = [] + for registry in reversed(self._registry.ro): + byorder = registry._subscribers + if order >= len(byorder): + continue + + if provided is None: + extendors = (provided, ) + else: + extendors = registry._v_lookup._extendors.get(provided) + if extendors is None: + continue + + _subscriptions(byorder[order], required, extendors, '', + result, 0, order) + + self._subscribe(*required) + + return result + + def subscribers(self, objects, provided): + subscriptions = self.subscriptions([providedBy(o) for o in objects], provided) + if provided is None: + result = () + for subscription in subscriptions: + subscription(*objects) + else: + result = [] + for subscription in subscriptions: + subscriber = subscription(*objects) + if subscriber is not None: + result.append(subscriber) + return result + +class AdapterLookup(AdapterLookupBase, LookupBase): + pass + +@implementer(IAdapterRegistry) +class AdapterRegistry(BaseAdapterRegistry): + """ + A full implementation of ``IAdapterRegistry`` that adds support for + sub-registries. + """ + + LookupClass = AdapterLookup + + def __init__(self, bases=()): + # AdapterRegisties are invalidating registries, so + # we need to keep track of our invalidating subregistries. + self._v_subregistries = weakref.WeakKeyDictionary() + + super().__init__(bases) + + def _addSubregistry(self, r): + self._v_subregistries[r] = 1 + + def _removeSubregistry(self, r): + if r in self._v_subregistries: + del self._v_subregistries[r] + + def _setBases(self, bases): + old = self.__dict__.get('__bases__', ()) + for r in old: + if r not in bases: + r._removeSubregistry(self) + for r in bases: + if r not in old: + r._addSubregistry(self) + + super()._setBases(bases) + + def changed(self, originally_changed): + super().changed(originally_changed) + + for sub in self._v_subregistries.keys(): + sub.changed(originally_changed) + + +class VerifyingAdapterLookup(AdapterLookupBase, VerifyingBase): + pass + +@implementer(IAdapterRegistry) +class VerifyingAdapterRegistry(BaseAdapterRegistry): + """ + The most commonly-used adapter registry. + """ + + LookupClass = VerifyingAdapterLookup + +def _convert_None_to_Interface(x): + if x is None: + return Interface + else: + return x + +def _lookup(components, specs, provided, name, i, l): + # this function is called very often. + # The components.get in loops is executed 100 of 1000s times. + # by loading get into a local variable the bytecode + # "LOAD_FAST 0 (components)" in the loop can be eliminated. + components_get = components.get + if i < l: + for spec in specs[i].__sro__: + comps = components_get(spec) + if comps: + r = _lookup(comps, specs, provided, name, i+1, l) + if r is not None: + return r + else: + for iface in provided: + comps = components_get(iface) + if comps: + r = comps.get(name) + if r is not None: + return r + + return None + +def _lookupAll(components, specs, provided, result, i, l): + components_get = components.get # see _lookup above + if i < l: + for spec in reversed(specs[i].__sro__): + comps = components_get(spec) + if comps: + _lookupAll(comps, specs, provided, result, i+1, l) + else: + for iface in reversed(provided): + comps = components_get(iface) + if comps: + result.update(comps) + +def _subscriptions(components, specs, provided, name, result, i, l): + components_get = components.get # see _lookup above + if i < l: + for spec in reversed(specs[i].__sro__): + comps = components_get(spec) + if comps: + _subscriptions(comps, specs, provided, name, result, i+1, l) + else: + for iface in reversed(provided): + comps = components_get(iface) + if comps: + comps = comps.get(name) + if comps: + result.extend(comps) diff --git a/contrib/python/zope.interface/py3/zope/interface/advice.py b/contrib/python/zope.interface/py3/zope/interface/advice.py new file mode 100644 index 0000000000..54e356e672 --- /dev/null +++ b/contrib/python/zope.interface/py3/zope/interface/advice.py @@ -0,0 +1,118 @@ +############################################################################## +# +# Copyright (c) 2003 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. +# +############################################################################## +"""Class advice. + +This module was adapted from 'protocols.advice', part of the Python +Enterprise Application Kit (PEAK). Please notify the PEAK authors +(pje@telecommunity.com and tsarna@sarna.org) if bugs are found or +Zope-specific changes are required, so that the PEAK version of this module +can be kept in sync. + +PEAK is a Python application framework that interoperates with (but does +not require) Zope 3 and Twisted. It provides tools for manipulating UML +models, object-relational persistence, aspect-oriented programming, and more. +Visit the PEAK home page at http://peak.telecommunity.com for more information. +""" + +from types import FunctionType + +__all__ = [ + 'determineMetaclass', + 'getFrameInfo', + 'isClassAdvisor', + 'minimalBases', +] + +import sys + +def getFrameInfo(frame): + """Return (kind,module,locals,globals) for a frame + + 'kind' is one of "exec", "module", "class", "function call", or "unknown". + """ + + f_locals = frame.f_locals + f_globals = frame.f_globals + + sameNamespace = f_locals is f_globals + hasModule = '__module__' in f_locals + hasName = '__name__' in f_globals + + sameName = hasModule and hasName + sameName = sameName and f_globals['__name__']==f_locals['__module__'] + + module = hasName and sys.modules.get(f_globals['__name__']) or None + + namespaceIsModule = module and module.__dict__ is f_globals + + if not namespaceIsModule: + # some kind of funky exec + kind = "exec" + elif sameNamespace and not hasModule: + kind = "module" + elif sameName and not sameNamespace: + kind = "class" + elif not sameNamespace: + kind = "function call" + else: # pragma: no cover + # How can you have f_locals is f_globals, and have '__module__' set? + # This is probably module-level code, but with a '__module__' variable. + kind = "unknown" + return kind, module, f_locals, f_globals + + +def isClassAdvisor(ob): + """True if 'ob' is a class advisor function""" + return isinstance(ob,FunctionType) and hasattr(ob,'previousMetaclass') + + +def determineMetaclass(bases, explicit_mc=None): + """Determine metaclass from 1+ bases and optional explicit __metaclass__""" + + meta = [getattr(b,'__class__',type(b)) for b in bases] + + if explicit_mc is not None: + # The explicit metaclass needs to be verified for compatibility + # as well, and allowed to resolve the incompatible bases, if any + meta.append(explicit_mc) + + if len(meta)==1: + # easy case + return meta[0] + + candidates = minimalBases(meta) # minimal set of metaclasses + + if len(candidates)>1: + # We could auto-combine, but for now we won't... + raise TypeError("Incompatible metatypes", bases) + + # Just one, return it + return candidates[0] + + +def minimalBases(classes): + """Reduce a list of base classes to its ordered minimum equivalent""" + candidates = [] + + for m in classes: + for n in classes: + if issubclass(n,m) and m is not n: + break + else: + # m has no subclasses in 'classes' + if m in candidates: + candidates.remove(m) # ensure that we're later in the list + candidates.append(m) + + return candidates diff --git a/contrib/python/zope.interface/py3/zope/interface/common/__init__.py b/contrib/python/zope.interface/py3/zope/interface/common/__init__.py new file mode 100644 index 0000000000..56f4566a24 --- /dev/null +++ b/contrib/python/zope.interface/py3/zope/interface/common/__init__.py @@ -0,0 +1,272 @@ +############################################################################## +# 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. +############################################################################## + +import itertools +from types import FunctionType + +from zope.interface import classImplements +from zope.interface import Interface +from zope.interface.interface import fromFunction +from zope.interface.interface import InterfaceClass +from zope.interface.interface import _decorator_non_return + +__all__ = [ + # Nothing public here. +] + + +# pylint:disable=inherit-non-class, +# pylint:disable=no-self-argument,no-method-argument +# pylint:disable=unexpected-special-method-signature + +class optional: + # 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. + def __init__(self, method): + self.__doc__ = method.__doc__ + + +class ABCInterfaceClass(InterfaceClass): + """ + An interface that is automatically derived from a + :class:`abc.ABCMeta` type. + + Internal use only. + + The body of the interface definition *must* define + a property ``abc`` that is the ABC to base the interface on. + + If ``abc`` is *not* in the interface definition, a regular + interface will be defined instead (but ``extra_classes`` is still + respected). + + Use the ``@optional`` decorator on method definitions if + the ABC defines methods that are not actually required in all cases + because the Python language has multiple ways to implement a protocol. + For example, the ``iter()`` protocol can be implemented with + ``__iter__`` or the pair ``__len__`` and ``__getitem__``. + + When created, any existing classes that are registered to conform + to the ABC are declared to implement this interface. This is *not* + automatically updated as the ABC registry changes. If the body of the + interface definition defines ``extra_classes``, it should be a + tuple giving additional classes to declare implement the interface. + + Note that this is not fully symmetric. For example, it is usually + the case that a subclass relationship carries the interface + declarations over:: + + >>> from zope.interface import Interface + >>> class I1(Interface): + ... pass + ... + >>> from zope.interface import implementer + >>> @implementer(I1) + ... class Root(object): + ... pass + ... + >>> class Child(Root): + ... pass + ... + >>> child = Child() + >>> isinstance(child, Root) + True + >>> from zope.interface import providedBy + >>> list(providedBy(child)) + [<InterfaceClass __main__.I1>] + + However, that's not the case with ABCs and ABC interfaces. Just + because ``isinstance(A(), AnABC)`` and ``isinstance(B(), AnABC)`` + are both true, that doesn't mean there's any class hierarchy + relationship between ``A`` and ``B``, or between either of them + and ``AnABC``. Thus, if ``AnABC`` implemented ``IAnABC``, it would + not follow that either ``A`` or ``B`` implements ``IAnABC`` (nor + their instances provide it):: + + >>> class SizedClass(object): + ... def __len__(self): return 1 + ... + >>> from collections.abc import Sized + >>> isinstance(SizedClass(), Sized) + True + >>> from zope.interface import classImplements + >>> classImplements(Sized, I1) + None + >>> list(providedBy(SizedClass())) + [] + + Thus, to avoid conflicting assumptions, ABCs should not be + declared to implement their parallel ABC interface. Only concrete + classes specifically registered with the ABC should be declared to + do so. + + .. versionadded:: 5.0.0 + """ + + # If we could figure out invalidation, and used some special + # Specification/Declaration instances, and override the method ``providedBy`` here, + # perhaps we could more closely integrate with ABC virtual inheritance? + + def __init__(self, name, bases, attrs): + # go ahead and give us a name to ease debugging. + self.__name__ = name + extra_classes = attrs.pop('extra_classes', ()) + ignored_classes = attrs.pop('ignored_classes', ()) + + if 'abc' not in attrs: + # Something like ``IList(ISequence)``: We're extending + # abc interfaces but not an ABC interface ourself. + InterfaceClass.__init__(self, name, bases, attrs) + ABCInterfaceClass.__register_classes(self, extra_classes, ignored_classes) + self.__class__ = InterfaceClass + return + + based_on = attrs.pop('abc') + self.__abc = based_on + self.__extra_classes = tuple(extra_classes) + self.__ignored_classes = tuple(ignored_classes) + + assert name[1:] == based_on.__name__, (name, based_on) + methods = { + # Passing the name is important in case of aliases, + # e.g., ``__ror__ = __or__``. + k: self.__method_from_function(v, k) + for k, v in vars(based_on).items() + if isinstance(v, FunctionType) and not self.__is_private_name(k) + 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( + "{}\n{}".format(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__ + if mod == str.__module__: + return "`%s`" % name + if mod == '_io': + mod = 'io' + return "`{}.{}`".format(mod, name) + implementations_doc = "\n - ".join( + ref(c) + for c in sorted(self.getRegisteredConformers(), key=ref) + ) + if implementations_doc: + implementations_doc = "\n\nKnown implementations are:\n\n - " + implementations_doc + + 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 `{}.{}`.\n\n{}{}{}""".format( + based_on.__module__, based_on.__name__, + attrs.get('__doc__', based_on_doc), + self.__optional_methods_to_docs(attrs), + implementations_doc + ) + return doc + + + @staticmethod + def __is_private_name(name): + if name.startswith('__') and name.endswith('__'): + return False + return name.startswith('_') + + @staticmethod + def __is_reverse_protocol_name(name): + # The reverse names, like __rand__, + # aren't really part of the protocol. The interpreter has + # very complex behaviour around invoking those. PyPy + # doesn't always even expose them as attributes. + return name.startswith('__r') and name.endswith('__') + + def __method_from_function(self, function, name): + method = fromFunction(function, self, name=name) + # Eliminate the leading *self*, which is implied in + # an interface, but explicit in an ABC. + method.positional = method.positional[1:] + return method + + def __register_classes(self, conformers=None, ignored_classes=None): + # Make the concrete classes already present in our ABC's registry + # declare that they implement this interface. + conformers = conformers if conformers is not None else self.getRegisteredConformers() + ignored = ignored_classes if ignored_classes is not None else self.__ignored_classes + for cls in conformers: + if cls in ignored: + continue + classImplements(cls, self) + + def getABC(self): + """ + Return the ABC this interface represents. + """ + return self.__abc + + def getRegisteredConformers(self): + """ + Return an iterable of the classes that are known to conform to + the ABC this interface parallels. + """ + based_on = self.__abc + + # The registry only contains things that aren't already + # known to be subclasses of the ABC. But the ABC is in charge + # of checking that, so its quite possible that registrations + # are in fact ignored, winding up just in the _abc_cache. + try: + registered = list(based_on._abc_registry) + list(based_on._abc_cache) + except AttributeError: + # Rewritten in C in CPython 3.7. + # These expose the underlying weakref. + from abc import _get_dump + data = _get_dump(based_on) + registry = data[0] + cache = data[1] + registered = [x() for x in itertools.chain(registry, cache)] + registered = [x for x in registered if x is not None] + + return set(itertools.chain(registered, self.__extra_classes)) + + +def _create_ABCInterface(): + # It's a two-step process to create the root ABCInterface, because + # without specifying a corresponding ABC, using the normal constructor + # gets us a plain InterfaceClass object, and there is no ABC to associate with the + # root. + abc_name_bases_attrs = ('ABCInterface', (Interface,), {}) + instance = ABCInterfaceClass.__new__(ABCInterfaceClass, *abc_name_bases_attrs) + InterfaceClass.__init__(instance, *abc_name_bases_attrs) + return instance + +ABCInterface = _create_ABCInterface() diff --git a/contrib/python/zope.interface/py3/zope/interface/common/builtins.py b/contrib/python/zope.interface/py3/zope/interface/common/builtins.py new file mode 100644 index 0000000000..17090e4a79 --- /dev/null +++ b/contrib/python/zope.interface/py3/zope/interface/common/builtins.py @@ -0,0 +1,119 @@ +############################################################################## +# 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 for builtin types. + +After this module is imported, the standard library types will declare +that they implement the appropriate interface. + +.. versionadded:: 5.0.0 +""" + +from zope.interface import classImplements + +from zope.interface.common import collections +from zope.interface.common import numbers +from zope.interface.common import io + +__all__ = [ + 'IList', + 'ITuple', + 'ITextString', + 'IByteString', + 'INativeString', + 'IBool', + 'IDict', + '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): + """ + Interface for :class:`tuple` + """ + extra_classes = (tuple,) + + +class ITextString(collections.ISequence): + """ + Interface for text ("unicode") strings. + + This is :class:`str` + """ + extra_classes = (str,) + + +class IByteString(collections.IByteString): + """ + Interface for immutable byte strings. + + On all Python versions this is :class:`bytes`. + + Unlike :class:`zope.interface.common.collections.IByteString` + (the parent of this interface) this does *not* include + :class:`bytearray`. + """ + extra_classes = (bytes,) + + +class INativeString(ITextString): + """ + Interface for native strings. + + On all Python versions, this is :class:`str`. Tt extends + :class:`ITextString`. + """ +# We're not extending ABCInterface so extra_classes won't work +classImplements(str, INativeString) + + +class IBool(numbers.IIntegral): + """ + Interface for :class:`bool` + """ + extra_classes = (bool,) + + +class IDict(collections.IMutableMapping): + """ + Interface for :class:`dict` + """ + extra_classes = (dict,) + + +class IFile(io.IIOBase): + """ + Interface for :class:`file`. + + It is recommended to use the interfaces from :mod:`zope.interface.common.io` + instead of this interface. + + On Python 3, there is no single implementation of this interface; + depending on the arguments, the :func:`open` builtin can return + many different classes that implement different interfaces from + :mod:`zope.interface.common.io`. + """ + extra_classes = () diff --git a/contrib/python/zope.interface/py3/zope/interface/common/collections.py b/contrib/python/zope.interface/py3/zope/interface/common/collections.py new file mode 100644 index 0000000000..c549028268 --- /dev/null +++ b/contrib/python/zope.interface/py3/zope/interface/common/collections.py @@ -0,0 +1,253 @@ +############################################################################## +# 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 +""" + +import sys + +from abc import ABCMeta +from collections import abc +from collections import OrderedDict +from collections import UserList +from collections import UserDict +from collections import UserString + +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 + + +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', +] + +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``. + """ + +class IHashable(ABCInterface): + abc = abc.Hashable + +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 + +class IReversible(IIterable): + abc = _new_in_ver('Reversible', True, (IIterable.getABC(),)) + + @optional + def __reversed__(): + """ + Optional method. If this isn't present, the interpreter + will use ``__len__`` and ``__getitem__`` to implement the + `reversed` builtin. + """ + +class IGenerator(IIterator): + # New in Python 3.5 + abc = _new_in_ver('Generator', True, (IIterator.getABC(),)) + + +class ISized(ABCInterface): + abc = abc.Sized + + +# ICallable is not defined because there's no standard signature. + +class ICollection(ISized, + IIterable, + IContainer): + abc = _new_in_ver('Collection', True, + (ISized.getABC(), IIterable.getABC(), IContainer.getABC())) + + +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. + """ + +class IMutableSequence(ISequence): + abc = abc.MutableSequence + extra_classes = (UserList,) + + +class IByteString(ISequence): + """ + This unifies `bytes` and `bytearray`. + """ + abc = _new_in_ver('ByteString', True, + (ISequence.getABC(),), + (bytes, bytearray)) + + +class ISet(ICollection): + abc = abc.Set + + +class IMutableSet(ISet): + abc = abc.MutableSet + + +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,) + + +class IMutableMapping(IMapping): + abc = abc.MutableMapping + extra_classes = (dict, UserDict,) + ignored_classes = (OrderedDict,) + +class IMappingView(ISized): + abc = abc.MappingView + + +class IItemsView(IMappingView, ISet): + abc = abc.ItemsView + + +class IKeysView(IMappingView, ISet): + abc = abc.KeysView + + +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``. + """ + +class IAwaitable(ABCInterface): + abc = _new_in_ver('Awaitable', True) + + +class ICoroutine(IAwaitable): + abc = _new_in_ver('Coroutine', True) + + +class IAsyncIterable(ABCInterface): + abc = _new_in_ver('AsyncIterable', True) + + +class IAsyncIterator(IAsyncIterable): + abc = _new_in_ver('AsyncIterator', True) + + +class IAsyncGenerator(IAsyncIterator): + abc = _new_in_ver('AsyncGenerator', True) diff --git a/contrib/python/zope.interface/py3/zope/interface/common/idatetime.py b/contrib/python/zope.interface/py3/zope/interface/common/idatetime.py new file mode 100644 index 0000000000..82f0059c85 --- /dev/null +++ b/contrib/python/zope.interface/py3/zope/interface/common/idatetime.py @@ -0,0 +1,606 @@ +############################################################################## +# Copyright (c) 2002 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. +############################################################################## +"""Datetime interfaces. + +This module is called idatetime because if it were called datetime the import +of the real datetime would fail. +""" +from datetime import timedelta, date, datetime, time, tzinfo + +from zope.interface import Interface, Attribute +from zope.interface import classImplements + + +class ITimeDeltaClass(Interface): + """This is the timedelta class interface. + + This is symbolic; this module does **not** make + `datetime.timedelta` provide this interface. + """ + + min = Attribute("The most negative timedelta object") + + max = Attribute("The most positive timedelta object") + + resolution = Attribute( + "The smallest difference between non-equal timedelta objects") + + +class ITimeDelta(ITimeDeltaClass): + """Represent the difference between two datetime objects. + + Implemented by `datetime.timedelta`. + + Supported operators: + + - add, subtract timedelta + - unary plus, minus, abs + - compare to timedelta + - multiply, divide by int/long + + In addition, `.datetime` supports subtraction of two `.datetime` objects + returning a `.timedelta`, and addition or subtraction of a `.datetime` + and a `.timedelta` giving a `.datetime`. + + Representation: (days, seconds, microseconds). + """ + + days = Attribute("Days between -999999999 and 999999999 inclusive") + + seconds = Attribute("Seconds between 0 and 86399 inclusive") + + microseconds = Attribute("Microseconds between 0 and 999999 inclusive") + + +class IDateClass(Interface): + """This is the date class interface. + + This is symbolic; this module does **not** make + `datetime.date` provide this interface. + """ + + min = Attribute("The earliest representable date") + + max = Attribute("The latest representable date") + + resolution = Attribute( + "The smallest difference between non-equal date objects") + + def today(): + """Return the current local time. + + This is equivalent to ``date.fromtimestamp(time.time())``""" + + def fromtimestamp(timestamp): + """Return the local date from a POSIX timestamp (like time.time()) + + This may raise `ValueError`, if the timestamp is out of the range of + values supported by the platform C ``localtime()`` function. It's common + for this to be restricted to years from 1970 through 2038. Note that + on non-POSIX systems that include leap seconds in their notion of a + timestamp, leap seconds are ignored by `fromtimestamp`. + """ + + def fromordinal(ordinal): + """Return the date corresponding to the proleptic Gregorian ordinal. + + January 1 of year 1 has ordinal 1. `ValueError` is raised unless + 1 <= ordinal <= date.max.toordinal(). + + For any date *d*, ``date.fromordinal(d.toordinal()) == d``. + """ + + +class IDate(IDateClass): + """Represents a date (year, month and day) in an idealized calendar. + + Implemented by `datetime.date`. + + Operators: + + __repr__, __str__ + __cmp__, __hash__ + __add__, __radd__, __sub__ (add/radd only with timedelta arg) + """ + + year = Attribute("Between MINYEAR and MAXYEAR inclusive.") + + month = Attribute("Between 1 and 12 inclusive") + + day = Attribute( + "Between 1 and the number of days in the given month of the given year.") + + def replace(year, month, day): + """Return a date with the same value. + + Except for those members given new values by whichever keyword + arguments are specified. For example, if ``d == date(2002, 12, 31)``, then + ``d.replace(day=26) == date(2000, 12, 26)``. + """ + + def timetuple(): + """Return a 9-element tuple of the form returned by `time.localtime`. + + The hours, minutes and seconds are 0, and the DST flag is -1. + ``d.timetuple()`` is equivalent to + ``(d.year, d.month, d.day, 0, 0, 0, d.weekday(), d.toordinal() - + date(d.year, 1, 1).toordinal() + 1, -1)`` + """ + + def toordinal(): + """Return the proleptic Gregorian ordinal of the date + + January 1 of year 1 has ordinal 1. For any date object *d*, + ``date.fromordinal(d.toordinal()) == d``. + """ + + def weekday(): + """Return the day of the week as an integer. + + Monday is 0 and Sunday is 6. For example, + ``date(2002, 12, 4).weekday() == 2``, a Wednesday. + + .. seealso:: `isoweekday`. + """ + + def isoweekday(): + """Return the day of the week as an integer. + + Monday is 1 and Sunday is 7. For example, + date(2002, 12, 4).isoweekday() == 3, a Wednesday. + + .. seealso:: `weekday`, `isocalendar`. + """ + + def isocalendar(): + """Return a 3-tuple, (ISO year, ISO week number, ISO weekday). + + The ISO calendar is a widely used variant of the Gregorian calendar. + See http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm for a good + explanation. + + The ISO year consists of 52 or 53 full weeks, and where a week starts + on a Monday and ends on a Sunday. The first week of an ISO year is the + first (Gregorian) calendar week of a year containing a Thursday. This + is called week number 1, and the ISO year of that Thursday is the same + as its Gregorian year. + + For example, 2004 begins on a Thursday, so the first week of ISO year + 2004 begins on Monday, 29 Dec 2003 and ends on Sunday, 4 Jan 2004, so + that ``date(2003, 12, 29).isocalendar() == (2004, 1, 1)`` and + ``date(2004, 1, 4).isocalendar() == (2004, 1, 7)``. + """ + + def isoformat(): + """Return a string representing the date in ISO 8601 format. + + This is 'YYYY-MM-DD'. + For example, ``date(2002, 12, 4).isoformat() == '2002-12-04'``. + """ + + def __str__(): + """For a date *d*, ``str(d)`` is equivalent to ``d.isoformat()``.""" + + def ctime(): + """Return a string representing the date. + + For example date(2002, 12, 4).ctime() == 'Wed Dec 4 00:00:00 2002'. + d.ctime() is equivalent to time.ctime(time.mktime(d.timetuple())) + on platforms where the native C ctime() function + (which `time.ctime` invokes, but which date.ctime() does not invoke) + conforms to the C standard. + """ + + def strftime(format): + """Return a string representing the date. + + Controlled by an explicit format string. Format codes referring to + hours, minutes or seconds will see 0 values. + """ + + +class IDateTimeClass(Interface): + """This is the datetime class interface. + + This is symbolic; this module does **not** make + `datetime.datetime` provide this interface. + """ + + min = Attribute("The earliest representable datetime") + + max = Attribute("The latest representable datetime") + + resolution = Attribute( + "The smallest possible difference between non-equal datetime objects") + + def today(): + """Return the current local datetime, with tzinfo None. + + This is equivalent to ``datetime.fromtimestamp(time.time())``. + + .. seealso:: `now`, `fromtimestamp`. + """ + + def now(tz=None): + """Return the current local date and time. + + If optional argument *tz* is None or not specified, this is like `today`, + but, if possible, supplies more precision than can be gotten from going + through a `time.time` timestamp (for example, this may be possible on + platforms supplying the C ``gettimeofday()`` function). + + Else tz must be an instance of a class tzinfo subclass, and the current + date and time are converted to tz's time zone. In this case the result + is equivalent to tz.fromutc(datetime.utcnow().replace(tzinfo=tz)). + + .. seealso:: `today`, `utcnow`. + """ + + def utcnow(): + """Return the current UTC date and time, with tzinfo None. + + This is like `now`, but returns the current UTC date and time, as a + naive datetime object. + + .. seealso:: `now`. + """ + + def fromtimestamp(timestamp, tz=None): + """Return the local date and time corresponding to the POSIX timestamp. + + Same as is returned by time.time(). If optional argument tz is None or + not specified, the timestamp is converted to the platform's local date + and time, and the returned datetime object is naive. + + Else tz must be an instance of a class tzinfo subclass, and the + timestamp is converted to tz's time zone. In this case the result is + equivalent to + ``tz.fromutc(datetime.utcfromtimestamp(timestamp).replace(tzinfo=tz))``. + + fromtimestamp() may raise `ValueError`, if the timestamp is out of the + range of values supported by the platform C localtime() or gmtime() + functions. It's common for this to be restricted to years in 1970 + through 2038. Note that on non-POSIX systems that include leap seconds + in their notion of a timestamp, leap seconds are ignored by + fromtimestamp(), and then it's possible to have two timestamps + differing by a second that yield identical datetime objects. + + .. seealso:: `utcfromtimestamp`. + """ + + def utcfromtimestamp(timestamp): + """Return the UTC datetime from the POSIX timestamp with tzinfo None. + + This may raise `ValueError`, if the timestamp is out of the range of + values supported by the platform C ``gmtime()`` function. It's common for + this to be restricted to years in 1970 through 2038. + + .. seealso:: `fromtimestamp`. + """ + + def fromordinal(ordinal): + """Return the datetime from the proleptic Gregorian ordinal. + + January 1 of year 1 has ordinal 1. `ValueError` is raised unless + 1 <= ordinal <= datetime.max.toordinal(). + The hour, minute, second and microsecond of the result are all 0, and + tzinfo is None. + """ + + def combine(date, time): + """Return a new datetime object. + + Its date members are equal to the given date object's, and whose time + and tzinfo members are equal to the given time object's. For any + datetime object *d*, ``d == datetime.combine(d.date(), d.timetz())``. + If date is a datetime object, its time and tzinfo members are ignored. + """ + + +class IDateTime(IDate, IDateTimeClass): + """Object contains all the information from a date object and a time object. + + Implemented by `datetime.datetime`. + """ + + year = Attribute("Year between MINYEAR and MAXYEAR inclusive") + + month = Attribute("Month between 1 and 12 inclusive") + + day = Attribute( + "Day between 1 and the number of days in the given month of the year") + + hour = Attribute("Hour in range(24)") + + minute = Attribute("Minute in range(60)") + + second = Attribute("Second in range(60)") + + microsecond = Attribute("Microsecond in range(1000000)") + + tzinfo = Attribute( + """The object passed as the tzinfo argument to the datetime constructor + or None if none was passed""") + + def date(): + """Return date object with same year, month and day.""" + + def time(): + """Return time object with same hour, minute, second, microsecond. + + tzinfo is None. + + .. seealso:: Method :meth:`timetz`. + """ + + def timetz(): + """Return time object with same hour, minute, second, microsecond, + and tzinfo. + + .. seealso:: Method :meth:`time`. + """ + + def replace(year, month, day, hour, minute, second, microsecond, tzinfo): + """Return a datetime with the same members, except for those members + given new values by whichever keyword arguments are specified. + + Note that ``tzinfo=None`` can be specified to create a naive datetime from + an aware datetime with no conversion of date and time members. + """ + + def astimezone(tz): + """Return a datetime object with new tzinfo member tz, adjusting the + date and time members so the result is the same UTC time as self, but + in tz's local time. + + tz must be an instance of a tzinfo subclass, and its utcoffset() and + dst() methods must not return None. self must be aware (self.tzinfo + must not be None, and self.utcoffset() must not return None). + + If self.tzinfo is tz, self.astimezone(tz) is equal to self: no + adjustment of date or time members is performed. Else the result is + local time in time zone tz, representing the same UTC time as self: + + after astz = dt.astimezone(tz), astz - astz.utcoffset() + + will usually have the same date and time members as dt - dt.utcoffset(). + The discussion of class `datetime.tzinfo` explains the cases at Daylight Saving + Time transition boundaries where this cannot be achieved (an issue only + if tz models both standard and daylight time). + + If you merely want to attach a time zone object *tz* to a datetime *dt* + without adjustment of date and time members, use ``dt.replace(tzinfo=tz)``. + If you merely want to remove the time zone object from an aware + datetime dt without conversion of date and time members, use + ``dt.replace(tzinfo=None)``. + + Note that the default `tzinfo.fromutc` method can be overridden in a + tzinfo subclass to effect the result returned by `astimezone`. + """ + + def utcoffset(): + """Return the timezone offset in minutes east of UTC (negative west of + UTC).""" + + def dst(): + """Return 0 if DST is not in effect, or the DST offset (in minutes + eastward) if DST is in effect. + """ + + def tzname(): + """Return the timezone name.""" + + def timetuple(): + """Return a 9-element tuple of the form returned by `time.localtime`.""" + + def utctimetuple(): + """Return UTC time tuple compatilble with `time.gmtime`.""" + + def toordinal(): + """Return the proleptic Gregorian ordinal of the date. + + The same as self.date().toordinal(). + """ + + def weekday(): + """Return the day of the week as an integer. + + Monday is 0 and Sunday is 6. The same as self.date().weekday(). + See also isoweekday(). + """ + + def isoweekday(): + """Return the day of the week as an integer. + + Monday is 1 and Sunday is 7. The same as self.date().isoweekday. + + .. seealso:: `weekday`, `isocalendar`. + """ + + def isocalendar(): + """Return a 3-tuple, (ISO year, ISO week number, ISO weekday). + + The same as self.date().isocalendar(). + """ + + def isoformat(sep='T'): + """Return a string representing the date and time in ISO 8601 format. + + YYYY-MM-DDTHH:MM:SS.mmmmmm or YYYY-MM-DDTHH:MM:SS if microsecond is 0 + + If `utcoffset` does not return None, a 6-character string is appended, + giving the UTC offset in (signed) hours and minutes: + + YYYY-MM-DDTHH:MM:SS.mmmmmm+HH:MM or YYYY-MM-DDTHH:MM:SS+HH:MM + if microsecond is 0. + + The optional argument sep (default 'T') is a one-character separator, + placed between the date and time portions of the result. + """ + + def __str__(): + """For a datetime instance *d*, ``str(d)`` is equivalent to ``d.isoformat(' ')``. + """ + + def ctime(): + """Return a string representing the date and time. + + ``datetime(2002, 12, 4, 20, 30, 40).ctime() == 'Wed Dec 4 20:30:40 2002'``. + ``d.ctime()`` is equivalent to ``time.ctime(time.mktime(d.timetuple()))`` on + platforms where the native C ``ctime()`` function (which `time.ctime` + invokes, but which `datetime.ctime` does not invoke) conforms to the + C standard. + """ + + def strftime(format): + """Return a string representing the date and time. + + This is controlled by an explicit format string. + """ + + +class ITimeClass(Interface): + """This is the time class interface. + + This is symbolic; this module does **not** make + `datetime.time` provide this interface. + + """ + + min = Attribute("The earliest representable time") + + max = Attribute("The latest representable time") + + resolution = Attribute( + "The smallest possible difference between non-equal time objects") + + +class ITime(ITimeClass): + """Represent time with time zone. + + Implemented by `datetime.time`. + + Operators: + + __repr__, __str__ + __cmp__, __hash__ + """ + + hour = Attribute("Hour in range(24)") + + minute = Attribute("Minute in range(60)") + + second = Attribute("Second in range(60)") + + microsecond = Attribute("Microsecond in range(1000000)") + + tzinfo = Attribute( + """The object passed as the tzinfo argument to the time constructor + or None if none was passed.""") + + def replace(hour, minute, second, microsecond, tzinfo): + """Return a time with the same value. + + Except for those members given new values by whichever keyword + arguments are specified. Note that tzinfo=None can be specified + to create a naive time from an aware time, without conversion of the + time members. + """ + + def isoformat(): + """Return a string representing the time in ISO 8601 format. + + That is HH:MM:SS.mmmmmm or, if self.microsecond is 0, HH:MM:SS + If utcoffset() does not return None, a 6-character string is appended, + giving the UTC offset in (signed) hours and minutes: + HH:MM:SS.mmmmmm+HH:MM or, if self.microsecond is 0, HH:MM:SS+HH:MM + """ + + def __str__(): + """For a time t, str(t) is equivalent to t.isoformat().""" + + def strftime(format): + """Return a string representing the time. + + This is controlled by an explicit format string. + """ + + def utcoffset(): + """Return the timezone offset in minutes east of UTC (negative west of + UTC). + + If tzinfo is None, returns None, else returns + self.tzinfo.utcoffset(None), and raises an exception if the latter + doesn't return None or a timedelta object representing a whole number + of minutes with magnitude less than one day. + """ + + def dst(): + """Return 0 if DST is not in effect, or the DST offset (in minutes + eastward) if DST is in effect. + + If tzinfo is None, returns None, else returns self.tzinfo.dst(None), + and raises an exception if the latter doesn't return None, or a + timedelta object representing a whole number of minutes with + magnitude less than one day. + """ + + def tzname(): + """Return the timezone name. + + If tzinfo is None, returns None, else returns self.tzinfo.tzname(None), + or raises an exception if the latter doesn't return None or a string + object. + """ + + +class ITZInfo(Interface): + """Time zone info class. + """ + + def utcoffset(dt): + """Return offset of local time from UTC, in minutes east of UTC. + + If local time is west of UTC, this should be negative. + Note that this is intended to be the total offset from UTC; + for example, if a tzinfo object represents both time zone and DST + adjustments, utcoffset() should return their sum. If the UTC offset + isn't known, return None. Else the value returned must be a timedelta + object specifying a whole number of minutes in the range -1439 to 1439 + inclusive (1440 = 24*60; the magnitude of the offset must be less + than one day). + """ + + def dst(dt): + """Return the daylight saving time (DST) adjustment, in minutes east + of UTC, or None if DST information isn't known. + """ + + def tzname(dt): + """Return the time zone name corresponding to the datetime object as + a string. + """ + + def fromutc(dt): + """Return an equivalent datetime in self's local time.""" + + +classImplements(timedelta, ITimeDelta) +classImplements(date, IDate) +classImplements(datetime, IDateTime) +classImplements(time, ITime) +classImplements(tzinfo, ITZInfo) + +## directlyProvides(timedelta, ITimeDeltaClass) +## directlyProvides(date, IDateClass) +## directlyProvides(datetime, IDateTimeClass) +## directlyProvides(time, ITimeClass) diff --git a/contrib/python/zope.interface/py3/zope/interface/common/interfaces.py b/contrib/python/zope.interface/py3/zope/interface/common/interfaces.py new file mode 100644 index 0000000000..70bd294f35 --- /dev/null +++ b/contrib/python/zope.interface/py3/zope/interface/common/interfaces.py @@ -0,0 +1,208 @@ +############################################################################## +# +# Copyright (c) 2003 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. +# +############################################################################## +"""Interfaces for standard python exceptions +""" +from zope.interface import Interface +from zope.interface import classImplements + +class IException(Interface): + "Interface for `Exception`" +classImplements(Exception, IException) + + +class IStandardError(IException): + "Interface for `StandardError` (no longer existing.)" + + +class IWarning(IException): + "Interface for `Warning`" +classImplements(Warning, IWarning) + + +class ISyntaxError(IStandardError): + "Interface for `SyntaxError`" +classImplements(SyntaxError, ISyntaxError) + + +class ILookupError(IStandardError): + "Interface for `LookupError`" +classImplements(LookupError, ILookupError) + + +class IValueError(IStandardError): + "Interface for `ValueError`" +classImplements(ValueError, IValueError) + + +class IRuntimeError(IStandardError): + "Interface for `RuntimeError`" +classImplements(RuntimeError, IRuntimeError) + + +class IArithmeticError(IStandardError): + "Interface for `ArithmeticError`" +classImplements(ArithmeticError, IArithmeticError) + + +class IAssertionError(IStandardError): + "Interface for `AssertionError`" +classImplements(AssertionError, IAssertionError) + + +class IAttributeError(IStandardError): + "Interface for `AttributeError`" +classImplements(AttributeError, IAttributeError) + + +class IDeprecationWarning(IWarning): + "Interface for `DeprecationWarning`" +classImplements(DeprecationWarning, IDeprecationWarning) + + +class IEOFError(IStandardError): + "Interface for `EOFError`" +classImplements(EOFError, IEOFError) + + +class IEnvironmentError(IStandardError): + "Interface for `EnvironmentError`" +classImplements(EnvironmentError, IEnvironmentError) + + +class IFloatingPointError(IArithmeticError): + "Interface for `FloatingPointError`" +classImplements(FloatingPointError, IFloatingPointError) + + +class IIOError(IEnvironmentError): + "Interface for `IOError`" +classImplements(IOError, IIOError) + + +class IImportError(IStandardError): + "Interface for `ImportError`" +classImplements(ImportError, IImportError) + + +class IIndentationError(ISyntaxError): + "Interface for `IndentationError`" +classImplements(IndentationError, IIndentationError) + + +class IIndexError(ILookupError): + "Interface for `IndexError`" +classImplements(IndexError, IIndexError) + + +class IKeyError(ILookupError): + "Interface for `KeyError`" +classImplements(KeyError, IKeyError) + + +class IKeyboardInterrupt(IStandardError): + "Interface for `KeyboardInterrupt`" +classImplements(KeyboardInterrupt, IKeyboardInterrupt) + + +class IMemoryError(IStandardError): + "Interface for `MemoryError`" +classImplements(MemoryError, IMemoryError) + + +class INameError(IStandardError): + "Interface for `NameError`" +classImplements(NameError, INameError) + + +class INotImplementedError(IRuntimeError): + "Interface for `NotImplementedError`" +classImplements(NotImplementedError, INotImplementedError) + + +class IOSError(IEnvironmentError): + "Interface for `OSError`" +classImplements(OSError, IOSError) + + +class IOverflowError(IArithmeticError): + "Interface for `ArithmeticError`" +classImplements(OverflowError, IOverflowError) + + +class IOverflowWarning(IWarning): + """Deprecated, no standard class implements this. + + This was the interface for ``OverflowWarning`` prior to Python 2.5, + but that class was removed for all versions after that. + """ + + +class IReferenceError(IStandardError): + "Interface for `ReferenceError`" +classImplements(ReferenceError, IReferenceError) + + +class IRuntimeWarning(IWarning): + "Interface for `RuntimeWarning`" +classImplements(RuntimeWarning, IRuntimeWarning) + + +class IStopIteration(IException): + "Interface for `StopIteration`" +classImplements(StopIteration, IStopIteration) + + +class ISyntaxWarning(IWarning): + "Interface for `SyntaxWarning`" +classImplements(SyntaxWarning, ISyntaxWarning) + + +class ISystemError(IStandardError): + "Interface for `SystemError`" +classImplements(SystemError, ISystemError) + + +class ISystemExit(IException): + "Interface for `SystemExit`" +classImplements(SystemExit, ISystemExit) + + +class ITabError(IIndentationError): + "Interface for `TabError`" +classImplements(TabError, ITabError) + + +class ITypeError(IStandardError): + "Interface for `TypeError`" +classImplements(TypeError, ITypeError) + + +class IUnboundLocalError(INameError): + "Interface for `UnboundLocalError`" +classImplements(UnboundLocalError, IUnboundLocalError) + + +class IUnicodeError(IValueError): + "Interface for `UnicodeError`" +classImplements(UnicodeError, IUnicodeError) + + +class IUserWarning(IWarning): + "Interface for `UserWarning`" +classImplements(UserWarning, IUserWarning) + + +class IZeroDivisionError(IArithmeticError): + "Interface for `ZeroDivisionError`" +classImplements(ZeroDivisionError, IZeroDivisionError) diff --git a/contrib/python/zope.interface/py3/zope/interface/common/io.py b/contrib/python/zope.interface/py3/zope/interface/common/io.py new file mode 100644 index 0000000000..0d6f3badfc --- /dev/null +++ b/contrib/python/zope.interface/py3/zope/interface/common/io.py @@ -0,0 +1,43 @@ +############################################################################## +# 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:`io`. + +After this module is imported, the standard library types will declare +that they implement the appropriate interface. + +.. versionadded:: 5.0.0 +""" + +import io as abc + +from zope.interface.common import ABCInterface + +# pylint:disable=inherit-non-class, +# pylint:disable=no-member + +class IIOBase(ABCInterface): + abc = abc.IOBase + + +class IRawIOBase(IIOBase): + abc = abc.RawIOBase + + +class IBufferedIOBase(IIOBase): + abc = abc.BufferedIOBase + extra_classes = () + + +class ITextIOBase(IIOBase): + abc = abc.TextIOBase diff --git a/contrib/python/zope.interface/py3/zope/interface/common/mapping.py b/contrib/python/zope.interface/py3/zope/interface/common/mapping.py new file mode 100644 index 0000000000..d04333357f --- /dev/null +++ b/contrib/python/zope.interface/py3/zope/interface/common/mapping.py @@ -0,0 +1,168 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 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. +# +############################################################################## +""" +Mapping 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.common import collections + +class IItemMapping(Interface): + """Simplest readable mapping object + """ + + def __getitem__(key): + """Get a value for a key + + A `KeyError` is raised if there is no value for the key. + """ + + +class IReadMapping(collections.IContainer, IItemMapping): + """ + Basic mapping interface. + + .. versionchanged:: 5.0.0 + Extend ``IContainer`` + """ + + def get(key, default=None): + """Get a value for a key + + The default is returned if there is no value for the key. + """ + + def __contains__(key): + """Tell if a key exists in the mapping.""" + # Optional in IContainer, required by this interface. + + +class IWriteMapping(Interface): + """Mapping methods for changing data""" + + def __delitem__(key): + """Delete a value from the mapping using the key.""" + + def __setitem__(key, value): + """Set a new item in the mapping.""" + + +class IEnumerableMapping(collections.ISized, IReadMapping): + """ + Mapping objects whose items can be enumerated. + + .. versionchanged:: 5.0.0 + Extend ``ISized`` + """ + + def keys(): + """Return the keys of the mapping object. + """ + + def __iter__(): + """Return an iterator for the keys of the mapping object. + """ + + def values(): + """Return the values of the mapping object. + """ + + def items(): + """Return the items of the mapping object. + """ + +class IMapping(IWriteMapping, IEnumerableMapping): + ''' Simple mapping interface ''' + +class IIterableMapping(IEnumerableMapping): + """A mapping that has distinct methods for iterating + without copying. + + """ + + +class IClonableMapping(Interface): + """Something that can produce a copy of itself. + + This is available in `dict`. + """ + + def copy(): + "return copy of dict" + +class IExtendedReadMapping(IIterableMapping): + """ + Something with a particular method equivalent to ``__contains__``. + + On Python 2, `dict` provided the ``has_key`` method, but it was removed + in Python 3. + """ + + +class IExtendedWriteMapping(IWriteMapping): + """Additional mutation methods. + + These are all provided by `dict`. + """ + + def clear(): + "delete all items" + + def update(d): + " Update D from E: for k in E.keys(): D[k] = E[k]" + + def setdefault(key, default=None): + "D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D" + + def pop(k, default=None): + """ + pop(k[,default]) -> value + + Remove specified key and return the corresponding value. + + If key is not found, *default* is returned if given, otherwise + `KeyError` is raised. Note that *default* must not be passed by + name. + """ + + def popitem(): + """remove and return some (key, value) pair as a + 2-tuple; but raise KeyError if mapping is empty""" + +class 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`` + """ diff --git a/contrib/python/zope.interface/py3/zope/interface/common/numbers.py b/contrib/python/zope.interface/py3/zope/interface/common/numbers.py new file mode 100644 index 0000000000..6b20e09d32 --- /dev/null +++ b/contrib/python/zope.interface/py3/zope/interface/common/numbers.py @@ -0,0 +1,65 @@ +############################################################################## +# 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:`numbers`. + +After this module is imported, the standard library types will declare +that they implement the appropriate interface. + +.. versionadded:: 5.0.0 +""" + +import numbers as abc + +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 + + +class INumber(ABCInterface): + abc = abc.Number + + +class IComplex(INumber): + abc = abc.Complex + + @optional + def __complex__(): + """ + Rarely implemented, even in builtin types. + """ + + +class IReal(IComplex): + abc = abc.Real + + @optional + def __complex__(): + """ + Rarely implemented, even in builtin types. + """ + + __floor__ = __ceil__ = __complex__ + + +class IRational(IReal): + abc = abc.Rational + + +class IIntegral(IRational): + abc = abc.Integral diff --git a/contrib/python/zope.interface/py3/zope/interface/common/sequence.py b/contrib/python/zope.interface/py3/zope/interface/common/sequence.py new file mode 100644 index 0000000000..5edc73dc6a --- /dev/null +++ b/contrib/python/zope.interface/py3/zope/interface/common/sequence.py @@ -0,0 +1,189 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 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. +# +############################################################################## +""" +Sequence 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 + +class IMinimalSequence(collections.IIterable): + """Most basic sequence interface. + + All sequences are iterable. This requires at least one of the + following: + + - a `__getitem__()` method that takes a single argument; integer + values starting at 0 must be supported, and `IndexError` should + be raised for the first index for which there is no value, or + + - an `__iter__()` method that returns an iterator as defined in + the Python documentation (http://docs.python.org/lib/typeiter.html). + + """ + + def __getitem__(index): + """``x.__getitem__(index) <==> x[index]`` + + Declaring this interface does not specify whether `__getitem__` + supports slice objects.""" + +class IFiniteSequence(collections.ISized, IMinimalSequence): + """ + A sequence of bound size. + + .. versionchanged:: 5.0.0 + Extend ``ISized`` + """ + +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``""" + + def __le__(other): + """``x.__le__(other) <==> x <= other``""" + + def __eq__(other): + """``x.__eq__(other) <==> x == other``""" + + def __ne__(other): + """``x.__ne__(other) <==> x != other``""" + + def __gt__(other): + """``x.__gt__(other) <==> x > other``""" + + def __ge__(other): + """``x.__ge__(other) <==> x >= other``""" + + def __add__(other): + """``x.__add__(other) <==> x + other``""" + + def __mul__(n): + """``x.__mul__(n) <==> x * n``""" + + def __rmul__(n): + """``x.__rmul__(n) <==> n * x``""" + + +class IExtendedReadSequence(IReadSequence): + """Full read interface for lists""" + + def count(item): + """Return number of occurrences of value""" + + def index(item, *args): + """index(value, [start, [stop]]) -> int + + Return first index of *value* + """ + +class IUniqueMemberWriteSequence(Interface): + """The write contract for a sequence that may enforce unique members""" + + def __setitem__(index, item): + """``x.__setitem__(index, item) <==> x[index] = item`` + + Declaring this interface does not specify whether `__setitem__` + supports slice objects. + """ + + def __delitem__(index): + """``x.__delitem__(index) <==> del x[index]`` + + Declaring this interface does not specify whether `__delitem__` + supports slice objects. + """ + + def __iadd__(y): + """``x.__iadd__(y) <==> x += y``""" + + def append(item): + """Append item to end""" + + def insert(index, item): + """Insert item before index""" + + def pop(index=-1): + """Remove and return item at index (default last)""" + + def remove(item): + """Remove first occurrence of value""" + + def reverse(): + """Reverse *IN PLACE*""" + + def sort(cmpfunc=None): + """Stable sort *IN PLACE*; `cmpfunc(x, y)` -> -1, 0, 1""" + + def extend(iterable): + """Extend list by appending elements from the iterable""" + +class IWriteSequence(IUniqueMemberWriteSequence): + """Full write contract for sequences""" + + def __imul__(n): + """``x.__imul__(n) <==> x *= n``""" + +class ISequence(IReadSequence, IWriteSequence): + """ + 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 + """ diff --git a/contrib/python/zope.interface/py3/zope/interface/declarations.py b/contrib/python/zope.interface/py3/zope/interface/declarations.py new file mode 100644 index 0000000000..61e2543929 --- /dev/null +++ b/contrib/python/zope.interface/py3/zope/interface/declarations.py @@ -0,0 +1,1188 @@ +############################################################################## +# Copyright (c) 2003 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. +############################################################################## +"""Implementation of interface declarations + +There are three flavors of declarations: + + - Declarations are used to simply name declared interfaces. + + - ImplementsDeclarations are used to express the interfaces that a + class implements (that instances of the class provides). + + Implements specifications support inheriting interfaces. + + - ProvidesDeclarations are used to express interfaces directly + provided by objects. + +""" +__docformat__ = 'restructuredtext' + +import sys +from types import FunctionType +from types import MethodType +from types import ModuleType +import weakref + +from zope.interface.interface import Interface +from zope.interface.interface import InterfaceClass +from zope.interface.interface import SpecificationBase +from zope.interface.interface import Specification +from zope.interface.interface import NameAndModuleComparisonMixin +from zope.interface._compat import _use_c_impl + +__all__ = [ + # None. The public APIs of this module are + # re-exported from zope.interface directly. +] + +# pylint:disable=too-many-lines + +# Registry of class-implementation specifications +BuiltinImplementationSpecifications = {} + + +def _next_super_class(ob): + # When ``ob`` is an instance of ``super``, return + # the next class in the MRO that we should actually be + # looking at. Watch out for diamond inheritance! + self_class = ob.__self_class__ + class_that_invoked_super = ob.__thisclass__ + complete_mro = self_class.__mro__ + next_class = complete_mro[complete_mro.index(class_that_invoked_super) + 1] + return next_class + +class named: + + def __init__(self, name): + self.name = name + + def __call__(self, ob): + ob.__component_name__ = self.name + return ob + + +class Declaration(Specification): + """Interface declarations""" + + __slots__ = () + + def __init__(self, *bases): + Specification.__init__(self, _normalizeargs(bases)) + + def __contains__(self, interface): + """Test whether an interface is in the specification + """ + + return self.extends(interface) and interface in self.interfaces() + + def __iter__(self): + """Return an iterator for the interfaces in the specification + """ + return self.interfaces() + + def flattened(self): + """Return an iterator of all included and extended interfaces + """ + return iter(self.__iro__) + + def __sub__(self, other): + """Remove interfaces from a specification + """ + return Declaration(*[ + i for i in self.interfaces() + if not [ + j + for j in other.interfaces() + if i.extends(j, 0) # non-strict extends + ] + ]) + + def __add__(self, other): + """ + Add two specifications or a specification and an interface + and produce a new declaration. + + .. versionchanged:: 5.4.0 + Now tries to preserve a consistent resolution order. Interfaces + being added to this object are added to the front of the resulting resolution + order if they already extend an interface in this object. Previously, + they were always added to the end of the order, which easily resulted in + invalid orders. + """ + before = [] + result = list(self.interfaces()) + seen = set(result) + for i in other.interfaces(): + if i in seen: + continue + seen.add(i) + if any(i.extends(x) for x in result): + # It already extends us, e.g., is a subclass, + # so it needs to go at the front of the RO. + before.append(i) + else: + result.append(i) + return Declaration(*(before + result)) + + # XXX: Is __radd__ needed? No tests break if it's removed. + # If it is needed, does it need to handle the C3 ordering differently? + # I (JAM) don't *think* it does. + __radd__ = __add__ + + @staticmethod + def _add_interfaces_to_cls(interfaces, cls): + # Strip redundant interfaces already provided + # by the cls so we don't produce invalid + # resolution orders. + implemented_by_cls = implementedBy(cls) + interfaces = tuple([ + iface + for iface in interfaces + if not implemented_by_cls.isOrExtends(iface) + ]) + return interfaces + (implemented_by_cls,) + + @staticmethod + def _argument_names_for_repr(interfaces): + # These don't actually have to be interfaces, they could be other + # Specification objects like Implements. Also, the first + # one is typically/nominally the cls. + ordered_names = [] + names = set() + for iface in interfaces: + duplicate_transform = repr + if isinstance(iface, InterfaceClass): + # Special case to get 'foo.bar.IFace' + # instead of '<InterfaceClass foo.bar.IFace>' + this_name = iface.__name__ + duplicate_transform = str + elif isinstance(iface, type): + # Likewise for types. (Ignoring legacy old-style + # classes.) + this_name = iface.__name__ + duplicate_transform = _implements_name + elif (isinstance(iface, Implements) + and not iface.declared + and iface.inherit in interfaces): + # If nothing is declared, there's no need to even print this; + # it would just show as ``classImplements(Class)``, and the + # ``Class`` has typically already. + continue + else: + this_name = repr(iface) + + already_seen = this_name in names + names.add(this_name) + if already_seen: + this_name = duplicate_transform(iface) + + ordered_names.append(this_name) + return ', '.join(ordered_names) + + +class _ImmutableDeclaration(Declaration): + # A Declaration that is immutable. Used as a singleton to + # return empty answers for things like ``implementedBy``. + # We have to define the actual singleton after normalizeargs + # is defined, and that in turn is defined after InterfaceClass and + # Implements. + + __slots__ = () + + __instance = None + + def __new__(cls): + if _ImmutableDeclaration.__instance is None: + _ImmutableDeclaration.__instance = object.__new__(cls) + return _ImmutableDeclaration.__instance + + def __reduce__(self): + return "_empty" + + @property + def __bases__(self): + return () + + @__bases__.setter + def __bases__(self, new_bases): + # We expect the superclass constructor to set ``self.__bases__ = ()``. + # Rather than attempt to special case that in the constructor and allow + # setting __bases__ only at that time, it's easier to just allow setting + # the empty tuple at any time. That makes ``x.__bases__ = x.__bases__`` a nice + # no-op too. (Skipping the superclass constructor altogether is a recipe + # for maintenance headaches.) + if new_bases != (): + raise TypeError("Cannot set non-empty bases on shared empty Declaration.") + + # As the immutable empty declaration, we cannot be changed. + # This means there's no logical reason for us to have dependents + # or subscriptions: we'll never notify them. So there's no need for + # us to keep track of any of that. + @property + def dependents(self): + return {} + + changed = subscribe = unsubscribe = lambda self, _ignored: None + + def interfaces(self): + # An empty iterator + return iter(()) + + def extends(self, interface, strict=True): + return interface is self._ROOT + + def get(self, name, default=None): + return default + + def weakref(self, callback=None): + # We're a singleton, we never go away. So there's no need to return + # distinct weakref objects here; their callbacks will never + # be called. Instead, we only need to return a callable that + # returns ourself. The easiest one is to return _ImmutableDeclaration + # itself; testing on Python 3.8 shows that's faster than a function that + # returns _empty. (Remember, one goal is to avoid allocating any + # object, and that includes a method.) + return _ImmutableDeclaration + + @property + def _v_attrs(self): + # _v_attrs is not a public, documented property, but some client code + # uses it anyway as a convenient place to cache things. To keep the + # empty declaration truly immutable, we must ignore that. That includes + # ignoring assignments as well. + return {} + + @_v_attrs.setter + def _v_attrs(self, new_attrs): + pass + + +############################################################################## +# +# Implementation specifications +# +# These specify interfaces implemented by instances of classes + +class Implements(NameAndModuleComparisonMixin, + Declaration): + # Inherit from NameAndModuleComparisonMixin to be + # mutually comparable with InterfaceClass objects. + # (The two must be mutually comparable to be able to work in e.g., BTrees.) + # Instances of this class generally don't have a __module__ other than + # `zope.interface.declarations`, whereas they *do* have a __name__ that is the + # fully qualified name of the object they are representing. + + # Note, though, that equality and hashing are still identity based. This + # accounts for things like nested objects that have the same name (typically + # only in tests) and is consistent with pickling. As far as comparisons to InterfaceClass + # goes, we'll never have equal name and module to those, so we're still consistent there. + # Instances of this class are essentially intended to be unique and are + # heavily cached (note how our __reduce__ handles this) so having identity + # based hash and eq should also work. + + # We want equality and hashing to be based on identity. However, we can't actually + # implement __eq__/__ne__ to do this because sometimes we get wrapped in a proxy. + # We need to let the proxy types implement these methods so they can handle unwrapping + # and then rely on: (1) the interpreter automatically changing `implements == proxy` into + # `proxy == implements` (which will call proxy.__eq__ to do the unwrapping) and then + # (2) the default equality and hashing semantics being identity based. + + # class whose specification should be used as additional base + inherit = None + + # interfaces actually declared for a class + declared = () + + # Weak cache of {class: <implements>} for super objects. + # Created on demand. These are rare, as of 5.0 anyway. Using a class + # level default doesn't take space in instances. Using _v_attrs would be + # another place to store this without taking space unless needed. + _super_cache = None + + __name__ = '?' + + @classmethod + def named(cls, name, *bases): + # Implementation method: Produce an Implements interface with + # a fully fleshed out __name__ before calling the constructor, which + # sets bases to the given interfaces and which may pass this object to + # other objects (e.g., to adjust dependents). If they're sorting or comparing + # by name, this needs to be set. + inst = cls.__new__(cls) + inst.__name__ = name + inst.__init__(*bases) + return inst + + def changed(self, originally_changed): + try: + del self._super_cache + except AttributeError: + pass + return super().changed(originally_changed) + + def __repr__(self): + if self.inherit: + name = getattr(self.inherit, '__name__', None) or _implements_name(self.inherit) + else: + name = self.__name__ + declared_names = self._argument_names_for_repr(self.declared) + if declared_names: + declared_names = ', ' + declared_names + return 'classImplements({}{})'.format(name, declared_names) + + def __reduce__(self): + return implementedBy, (self.inherit, ) + + +def _implements_name(ob): + # Return the __name__ attribute to be used by its __implemented__ + # property. + # This must be stable for the "same" object across processes + # because it is used for sorting. It needn't be unique, though, in cases + # like nested classes named Foo created by different functions, because + # equality and hashing is still based on identity. + # It might be nice to use __qualname__ on Python 3, but that would produce + # different values between Py2 and Py3. + return (getattr(ob, '__module__', '?') or '?') + \ + '.' + (getattr(ob, '__name__', '?') or '?') + + +def _implementedBy_super(sup): + # TODO: This is now simple enough we could probably implement + # in C if needed. + + # If the class MRO is strictly linear, we could just + # follow the normal algorithm for the next class in the + # search order (e.g., just return + # ``implemented_by_next``). But when diamond inheritance + # or mixins + interface declarations are present, we have + # to consider the whole MRO and compute a new Implements + # that excludes the classes being skipped over but + # includes everything else. + implemented_by_self = implementedBy(sup.__self_class__) + cache = implemented_by_self._super_cache # pylint:disable=protected-access + if cache is None: + cache = implemented_by_self._super_cache = weakref.WeakKeyDictionary() + + key = sup.__thisclass__ + try: + return cache[key] + except KeyError: + pass + + next_cls = _next_super_class(sup) + # For ``implementedBy(cls)``: + # .__bases__ is .declared + [implementedBy(b) for b in cls.__bases__] + # .inherit is cls + + implemented_by_next = implementedBy(next_cls) + mro = sup.__self_class__.__mro__ + ix_next_cls = mro.index(next_cls) + classes_to_keep = mro[ix_next_cls:] + new_bases = [implementedBy(c) for c in classes_to_keep] + + new = Implements.named( + implemented_by_self.__name__ + ':' + implemented_by_next.__name__, + *new_bases + ) + new.inherit = implemented_by_next.inherit + new.declared = implemented_by_next.declared + # I don't *think* that new needs to subscribe to ``implemented_by_self``; + # it auto-subscribed to its bases, and that should be good enough. + cache[key] = new + + return new + + +@_use_c_impl +def implementedBy(cls): # pylint:disable=too-many-return-statements,too-many-branches + """Return the interfaces implemented for a class' instances + + The value returned is an `~zope.interface.interfaces.IDeclaration`. + """ + try: + if isinstance(cls, super): + # Yes, this needs to be inside the try: block. Some objects + # like security proxies even break isinstance. + return _implementedBy_super(cls) + + spec = cls.__dict__.get('__implemented__') + except AttributeError: + + # we can't get the class dict. This is probably due to a + # security proxy. If this is the case, then probably no + # descriptor was installed for the class. + + # We don't want to depend directly on zope.security in + # zope.interface, but we'll try to make reasonable + # accommodations in an indirect way. + + # We'll check to see if there's an implements: + + spec = getattr(cls, '__implemented__', None) + if spec is None: + # There's no spec stred in the class. Maybe its a builtin: + spec = BuiltinImplementationSpecifications.get(cls) + if spec is not None: + return spec + return _empty + + if spec.__class__ == Implements: + # we defaulted to _empty or there was a spec. Good enough. + # Return it. + return spec + + # TODO: need old style __implements__ compatibility? + # Hm, there's an __implemented__, but it's not a spec. Must be + # an old-style declaration. Just compute a spec for it + return Declaration(*_normalizeargs((spec, ))) + + if isinstance(spec, Implements): + return spec + + if spec is None: + spec = BuiltinImplementationSpecifications.get(cls) + if spec is not None: + return spec + + # TODO: need old style __implements__ compatibility? + spec_name = _implements_name(cls) + if spec is not None: + # old-style __implemented__ = foo declaration + spec = (spec, ) # tuplefy, as it might be just an int + spec = Implements.named(spec_name, *_normalizeargs(spec)) + spec.inherit = None # old-style implies no inherit + del cls.__implemented__ # get rid of the old-style declaration + else: + try: + bases = cls.__bases__ + except AttributeError: + if not callable(cls): + raise TypeError("ImplementedBy called for non-factory", cls) + bases = () + + spec = Implements.named(spec_name, *[implementedBy(c) for c in bases]) + spec.inherit = cls + + try: + cls.__implemented__ = spec + if not hasattr(cls, '__providedBy__'): + cls.__providedBy__ = objectSpecificationDescriptor + + if isinstance(cls, type) and '__provides__' not in cls.__dict__: + # Make sure we get a __provides__ descriptor + cls.__provides__ = ClassProvides( + cls, + getattr(cls, '__class__', type(cls)), + ) + + except TypeError: + if not isinstance(cls, type): + raise TypeError("ImplementedBy called for non-type", cls) + BuiltinImplementationSpecifications[cls] = spec + + return spec + + +def classImplementsOnly(cls, *interfaces): + """ + Declare the only interfaces implemented by instances of a class + + The arguments after the class are one or more interfaces or interface + specifications (`~zope.interface.interfaces.IDeclaration` objects). + + The interfaces given (including the interfaces in the specifications) + replace any previous declarations, *including* inherited definitions. If you + wish to preserve inherited declarations, you can pass ``implementedBy(cls)`` + in *interfaces*. This can be used to alter the interface resolution order. + """ + spec = implementedBy(cls) + # Clear out everything inherited. It's important to + # also clear the bases right now so that we don't improperly discard + # interfaces that are already implemented by *old* bases that we're + # about to get rid of. + spec.declared = () + spec.inherit = None + spec.__bases__ = () + _classImplements_ordered(spec, interfaces, ()) + + +def classImplements(cls, *interfaces): + """ + Declare additional interfaces implemented for instances of a class + + The arguments after the class are one or more interfaces or + interface specifications (`~zope.interface.interfaces.IDeclaration` objects). + + The interfaces given (including the interfaces in the specifications) + are added to any interfaces previously declared. An effort is made to + keep a consistent C3 resolution order, but this cannot be guaranteed. + + .. versionchanged:: 5.0.0 + Each individual interface in *interfaces* may be added to either the + beginning or end of the list of interfaces declared for *cls*, + based on inheritance, in order to try to maintain a consistent + resolution order. Previously, all interfaces were added to the end. + .. versionchanged:: 5.1.0 + If *cls* is already declared to implement an interface (or derived interface) + in *interfaces* through inheritance, the interface is ignored. Previously, it + would redundantly be made direct base of *cls*, which often produced inconsistent + interface resolution orders. Now, the order will be consistent, but may change. + Also, if the ``__bases__`` of the *cls* are later changed, the *cls* will no + longer be considered to implement such an interface (changing the ``__bases__`` of *cls* + has never been supported). + """ + spec = implementedBy(cls) + interfaces = tuple(_normalizeargs(interfaces)) + + before = [] + after = [] + + # Take steps to try to avoid producing an invalid resolution + # order, while still allowing for BWC (in the past, we always + # appended) + for iface in interfaces: + for b in spec.declared: + if iface.extends(b): + before.append(iface) + break + else: + after.append(iface) + _classImplements_ordered(spec, tuple(before), tuple(after)) + + +def classImplementsFirst(cls, iface): + """ + Declare that instances of *cls* additionally provide *iface*. + + The second argument is an interface or interface specification. + It is added as the highest priority (first in the IRO) interface; + no attempt is made to keep a consistent resolution order. + + .. versionadded:: 5.0.0 + """ + spec = implementedBy(cls) + _classImplements_ordered(spec, (iface,), ()) + + +def _classImplements_ordered(spec, before=(), after=()): + # Elide everything already inherited. + # Except, if it is the root, and we don't already declare anything else + # that would imply it, allow the root through. (TODO: When we disallow non-strict + # IRO, this part of the check can be removed because it's not possible to re-declare + # like that.) + before = [ + x + for x in before + if not spec.isOrExtends(x) or (x is Interface and not spec.declared) + ] + after = [ + x + for x in after + if not spec.isOrExtends(x) or (x is Interface and not spec.declared) + ] + + # eliminate duplicates + new_declared = [] + seen = set() + for l in before, spec.declared, after: + for b in l: + if b not in seen: + new_declared.append(b) + seen.add(b) + + spec.declared = tuple(new_declared) + + # compute the bases + bases = new_declared # guaranteed no dupes + + if spec.inherit is not None: + for c in spec.inherit.__bases__: + b = implementedBy(c) + if b not in seen: + seen.add(b) + bases.append(b) + + spec.__bases__ = tuple(bases) + + +def _implements_advice(cls): + interfaces, do_classImplements = cls.__dict__['__implements_advice_data__'] + del cls.__implements_advice_data__ + do_classImplements(cls, *interfaces) + return cls + + +class implementer: + """ + Declare the interfaces implemented by instances of a class. + + This function is called as a class decorator. + + The arguments are one or more interfaces or interface + specifications (`~zope.interface.interfaces.IDeclaration` + objects). + + The interfaces given (including the interfaces in the + specifications) are added to any interfaces previously declared, + unless the interface is already implemented. + + Previous declarations include declarations for base classes unless + implementsOnly was used. + + This function is provided for convenience. It provides a more + convenient way to call `classImplements`. For example:: + + @implementer(I1) + class C(object): + pass + + is equivalent to calling:: + + classImplements(C, I1) + + after the class has been created. + + .. seealso:: `classImplements` + The change history provided there applies to this function too. + """ + __slots__ = ('interfaces',) + + def __init__(self, *interfaces): + self.interfaces = interfaces + + def __call__(self, ob): + if isinstance(ob, type): + # This is the common branch for classes. + classImplements(ob, *self.interfaces) + return ob + + spec_name = _implements_name(ob) + spec = Implements.named(spec_name, *self.interfaces) + try: + ob.__implemented__ = spec + except AttributeError: + raise TypeError("Can't declare implements", ob) + return ob + +class implementer_only: + """Declare the only interfaces implemented by instances of a class + + This function is called as a class decorator. + + The arguments are one or more interfaces or interface + specifications (`~zope.interface.interfaces.IDeclaration` objects). + + Previous declarations including declarations for base classes + are overridden. + + This function is provided for convenience. It provides a more + convenient way to call `classImplementsOnly`. For example:: + + @implementer_only(I1) + class C(object): pass + + is equivalent to calling:: + + classImplementsOnly(I1) + + after the class has been created. + """ + + def __init__(self, *interfaces): + self.interfaces = interfaces + + def __call__(self, ob): + if isinstance(ob, (FunctionType, MethodType)): + # XXX Does this decorator make sense for anything but classes? + # I don't think so. There can be no inheritance of interfaces + # on a method or function.... + raise ValueError('The implementer_only decorator is not ' + 'supported for methods or functions.') + + # Assume it's a class: + classImplementsOnly(ob, *self.interfaces) + return ob + + +############################################################################## +# +# Instance declarations + +class Provides(Declaration): # Really named ProvidesClass + """Implement ``__provides__``, the instance-specific specification + + When an object is pickled, we pickle the interfaces that it implements. + """ + + def __init__(self, cls, *interfaces): + self.__args = (cls, ) + interfaces + self._cls = cls + Declaration.__init__(self, *self._add_interfaces_to_cls(interfaces, cls)) + + # Added to by ``moduleProvides``, et al + _v_module_names = () + + def __repr__(self): + # The typical way to create instances of this + # object is via calling ``directlyProvides(...)`` or ``alsoProvides()``, + # but that's not the only way. Proxies, for example, + # directly use the ``Provides(...)`` function (which is the + # more generic method, and what we pickle as). We're after the most + # readable, useful repr in the common case, so we use the most + # common name. + # + # We also cooperate with ``moduleProvides`` to attempt to do the + # right thing for that API. See it for details. + function_name = 'directlyProvides' + if self._cls is ModuleType and self._v_module_names: + # See notes in ``moduleProvides``/``directlyProvides`` + providing_on_module = True + interfaces = self.__args[1:] + else: + providing_on_module = False + interfaces = (self._cls,) + self.__bases__ + ordered_names = self._argument_names_for_repr(interfaces) + if providing_on_module: + mod_names = self._v_module_names + if len(mod_names) == 1: + mod_names = "sys.modules[%r]" % mod_names[0] + ordered_names = ( + '{}, '.format(mod_names) + ) + ordered_names + return "{}({})".format( + function_name, + ordered_names, + ) + + def __reduce__(self): + # This reduces to the Provides *function*, not + # this class. + return Provides, self.__args + + __module__ = 'zope.interface' + + def __get__(self, inst, cls): + """Make sure that a class __provides__ doesn't leak to an instance + """ + if inst is None and cls is self._cls: + # We were accessed through a class, so we are the class' + # provides spec. Just return this object, but only if we are + # being called on the same class that we were defined for: + return self + + raise AttributeError('__provides__') + +ProvidesClass = Provides + +# Registry of instance declarations +# This is a memory optimization to allow objects to share specifications. +InstanceDeclarations = weakref.WeakValueDictionary() + +def Provides(*interfaces): # pylint:disable=function-redefined + """Declaration for an instance of *cls*. + + The correct signature is ``cls, *interfaces``. + The *cls* is necessary to avoid the + construction of inconsistent resolution orders. + + Instance declarations are shared among instances that have the same + declaration. The declarations are cached in a weak value dictionary. + """ + spec = InstanceDeclarations.get(interfaces) + if spec is None: + spec = ProvidesClass(*interfaces) + InstanceDeclarations[interfaces] = spec + + return spec + +Provides.__safe_for_unpickling__ = True + + +def directlyProvides(object, *interfaces): # pylint:disable=redefined-builtin + """Declare interfaces declared directly for an object + + The arguments after the object are one or more interfaces or interface + specifications (`~zope.interface.interfaces.IDeclaration` objects). + + The interfaces given (including the interfaces in the specifications) + replace interfaces previously declared for the object. + """ + cls = getattr(object, '__class__', None) + if cls is not None and getattr(cls, '__class__', None) is cls: + # It's a meta class (well, at least it it could be an extension class) + # Note that we can't get here from the tests: there is no normal + # class which isn't descriptor aware. + if not isinstance(object, type): + raise TypeError("Attempt to make an interface declaration on a " + "non-descriptor-aware class") + + interfaces = _normalizeargs(interfaces) + if cls is None: + cls = type(object) + + if issubclass(cls, type): + # we have a class or type. We'll use a special descriptor + # that provides some extra caching + object.__provides__ = ClassProvides(object, cls, *interfaces) + else: + provides = object.__provides__ = Provides(cls, *interfaces) + # See notes in ``moduleProvides``. + if issubclass(cls, ModuleType) and hasattr(object, '__name__'): + provides._v_module_names += (object.__name__,) + + + +def alsoProvides(object, *interfaces): # pylint:disable=redefined-builtin + """Declare interfaces declared directly for an object + + The arguments after the object are one or more interfaces or interface + specifications (`~zope.interface.interfaces.IDeclaration` objects). + + The interfaces given (including the interfaces in the specifications) are + added to the interfaces previously declared for the object. + """ + directlyProvides(object, directlyProvidedBy(object), *interfaces) + + +def noLongerProvides(object, interface): # pylint:disable=redefined-builtin + """ Removes a directly provided interface from an object. + """ + directlyProvides(object, directlyProvidedBy(object) - interface) + if interface.providedBy(object): + raise ValueError("Can only remove directly provided interfaces.") + + +@_use_c_impl +class ClassProvidesBase(SpecificationBase): + + __slots__ = ( + '_cls', + '_implements', + ) + + def __get__(self, inst, cls): + # member slots are set by subclass + # pylint:disable=no-member + if cls is self._cls: + # We only work if called on the class we were defined for + + if inst is None: + # We were accessed through a class, so we are the class' + # provides spec. Just return this object as is: + return self + + return self._implements + + raise AttributeError('__provides__') + + +class ClassProvides(Declaration, ClassProvidesBase): + """Special descriptor for class ``__provides__`` + + The descriptor caches the implementedBy info, so that + we can get declarations for objects without instance-specific + interfaces a bit quicker. + """ + + __slots__ = ( + '__args', + ) + + def __init__(self, cls, metacls, *interfaces): + self._cls = cls + self._implements = implementedBy(cls) + self.__args = (cls, metacls, ) + interfaces + Declaration.__init__(self, *self._add_interfaces_to_cls(interfaces, metacls)) + + def __repr__(self): + # There are two common ways to get instances of this object: + # The most interesting way is calling ``@provider(..)`` as a decorator + # of a class; this is the same as calling ``directlyProvides(cls, ...)``. + # + # The other way is by default: anything that invokes ``implementedBy(x)`` + # will wind up putting an instance in ``type(x).__provides__``; this includes + # the ``@implementer(...)`` decorator. Those instances won't have any + # interfaces. + # + # Thus, as our repr, we go with the ``directlyProvides()`` syntax. + interfaces = (self._cls, ) + self.__args[2:] + ordered_names = self._argument_names_for_repr(interfaces) + return "directlyProvides({})".format(ordered_names) + + def __reduce__(self): + return self.__class__, self.__args + + # Copy base-class method for speed + __get__ = ClassProvidesBase.__get__ + + +def directlyProvidedBy(object): # pylint:disable=redefined-builtin + """Return the interfaces directly provided by the given object + + The value returned is an `~zope.interface.interfaces.IDeclaration`. + """ + provides = getattr(object, "__provides__", None) + if ( + provides is None # no spec + # We might have gotten the implements spec, as an + # optimization. If so, it's like having only one base, that we + # lop off to exclude class-supplied declarations: + or isinstance(provides, Implements) + ): + return _empty + + # Strip off the class part of the spec: + return Declaration(provides.__bases__[:-1]) + + +class provider: + """Declare interfaces provided directly by a class + + This function is called in a class definition. + + The arguments are one or more interfaces or interface specifications + (`~zope.interface.interfaces.IDeclaration` objects). + + The given interfaces (including the interfaces in the specifications) + are used to create the class's direct-object interface specification. + An error will be raised if the module class has an direct interface + specification. In other words, it is an error to call this function more + than once in a class definition. + + Note that the given interfaces have nothing to do with the interfaces + implemented by instances of the class. + + This function is provided for convenience. It provides a more convenient + way to call `directlyProvides` for a class. For example:: + + @provider(I1) + class C: + pass + + is equivalent to calling:: + + directlyProvides(C, I1) + + after the class has been created. + """ + + def __init__(self, *interfaces): + self.interfaces = interfaces + + def __call__(self, ob): + directlyProvides(ob, *self.interfaces) + return ob + + +def moduleProvides(*interfaces): + """Declare interfaces provided by a module + + This function is used in a module definition. + + The arguments are one or more interfaces or interface specifications + (`~zope.interface.interfaces.IDeclaration` objects). + + The given interfaces (including the interfaces in the specifications) are + used to create the module's direct-object interface specification. An + error will be raised if the module already has an interface specification. + In other words, it is an error to call this function more than once in a + module definition. + + This function is provided for convenience. It provides a more convenient + way to call directlyProvides. For example:: + + moduleProvides(I1) + + is equivalent to:: + + directlyProvides(sys.modules[__name__], I1) + """ + frame = sys._getframe(1) # pylint:disable=protected-access + locals = frame.f_locals # pylint:disable=redefined-builtin + + # Try to make sure we were called from a module body + if (locals is not frame.f_globals) or ('__name__' not in locals): + raise TypeError( + "moduleProvides can only be used from a module definition.") + + if '__provides__' in locals: + raise TypeError( + "moduleProvides can only be used once in a module definition.") + + # Note: This is cached based on the key ``(ModuleType, *interfaces)``; + # One consequence is that any module that provides the same interfaces + # gets the same ``__repr__``, meaning that you can't tell what module + # such a declaration came from. Adding the module name to ``_v_module_names`` + # attempts to correct for this; it works in some common situations, but fails + # (1) after pickling (the data is lost) and (2) if declarations are + # actually shared and (3) if the alternate spelling of ``directlyProvides()`` + # is used. Problem (3) is fixed by cooperating with ``directlyProvides`` + # to maintain this information, and problem (2) is worked around by + # printing all the names, but (1) is unsolvable without introducing + # new classes or changing the stored data...but it doesn't actually matter, + # because ``ModuleType`` can't be pickled! + p = locals["__provides__"] = Provides(ModuleType, + *_normalizeargs(interfaces)) + p._v_module_names += (locals['__name__'],) + + +############################################################################## +# +# Declaration querying support + +# XXX: is this a fossil? Nobody calls it, no unit tests exercise it, no +# doctests import it, and the package __init__ doesn't import it. +# (Answer: Versions of zope.container prior to 4.4.0 called this, +# and zope.proxy.decorator up through at least 4.3.5 called this.) +def ObjectSpecification(direct, cls): + """Provide object specifications + + These combine information for the object and for it's classes. + """ + return Provides(cls, direct) # pragma: no cover fossil + +@_use_c_impl +def getObjectSpecification(ob): + try: + provides = ob.__provides__ + except AttributeError: + provides = None + + if provides is not None: + if isinstance(provides, SpecificationBase): + return provides + + try: + cls = ob.__class__ + except AttributeError: + # We can't get the class, so just consider provides + return _empty + return implementedBy(cls) + + +@_use_c_impl +def providedBy(ob): + """ + Return the interfaces provided by *ob*. + + If *ob* is a :class:`super` object, then only interfaces implemented + by the remainder of the classes in the method resolution order are + considered. Interfaces directly provided by the object underlying *ob* + are not. + """ + # Here we have either a special object, an old-style declaration + # or a descriptor + + # Try to get __providedBy__ + try: + if isinstance(ob, super): # Some objects raise errors on isinstance() + return implementedBy(ob) + + r = ob.__providedBy__ + except AttributeError: + # Not set yet. Fall back to lower-level thing that computes it + return getObjectSpecification(ob) + + try: + # We might have gotten a descriptor from an instance of a + # class (like an ExtensionClass) that doesn't support + # descriptors. We'll make sure we got one by trying to get + # the only attribute, which all specs have. + r.extends + except AttributeError: + + # The object's class doesn't understand descriptors. + # Sigh. We need to get an object descriptor, but we have to be + # careful. We want to use the instance's __provides__, if + # there is one, but only if it didn't come from the class. + + try: + r = ob.__provides__ + except AttributeError: + # No __provides__, so just fall back to implementedBy + return implementedBy(ob.__class__) + + # We need to make sure we got the __provides__ from the + # instance. We'll do this by making sure we don't get the same + # thing from the class: + + try: + cp = ob.__class__.__provides__ + except AttributeError: + # The ob doesn't have a class or the class has no + # provides, assume we're done: + return r + + if r is cp: + # Oops, we got the provides from the class. This means + # the object doesn't have it's own. We should use implementedBy + return implementedBy(ob.__class__) + + return r + + +@_use_c_impl +class ObjectSpecificationDescriptor: + """Implement the ``__providedBy__`` attribute + + The ``__providedBy__`` attribute computes the interfaces provided by + an object. If an object has an ``__provides__`` attribute, that is returned. + Otherwise, `implementedBy` the *cls* is returned. + + .. versionchanged:: 5.4.0 + Both the default (C) implementation and the Python implementation + now let exceptions raised by accessing ``__provides__`` propagate. + Previously, the C version ignored all exceptions. + .. versionchanged:: 5.4.0 + The Python implementation now matches the C implementation and lets + a ``__provides__`` of ``None`` override what the class is declared to + implement. + """ + + def __get__(self, inst, cls): + """Get an object specification for an object + """ + if inst is None: + return getObjectSpecification(cls) + + try: + return inst.__provides__ + except AttributeError: + return implementedBy(cls) + + +############################################################################## + +def _normalizeargs(sequence, output=None): + """Normalize declaration arguments + + Normalization arguments might contain Declarions, tuples, or single + interfaces. + + Anything but individual interfaces or implements specs will be expanded. + """ + if output is None: + output = [] + + cls = sequence.__class__ + if InterfaceClass in cls.__mro__ or Implements in cls.__mro__: + output.append(sequence) + else: + for v in sequence: + _normalizeargs(v, output) + + return output + +_empty = _ImmutableDeclaration() + +objectSpecificationDescriptor = ObjectSpecificationDescriptor() diff --git a/contrib/python/zope.interface/py3/zope/interface/document.py b/contrib/python/zope.interface/py3/zope/interface/document.py new file mode 100644 index 0000000000..84cfaa0b71 --- /dev/null +++ b/contrib/python/zope.interface/py3/zope/interface/document.py @@ -0,0 +1,124 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 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. +# +############################################################################## +""" Pretty-Print an Interface object as structured text (Yum) + +This module provides a function, asStructuredText, for rendering an +interface as structured text. +""" +import zope.interface + +__all__ = [ + 'asReStructuredText', + 'asStructuredText', +] + +def asStructuredText(I, munge=0, rst=False): + """ Output structured text format. Note, this will whack any existing + 'structured' format of the text. + + If `rst=True`, then the output will quote all code as inline literals in + accordance with 'reStructuredText' markup principles. + """ + + if rst: + inline_literal = lambda s: "``{}``".format(s) + else: + inline_literal = lambda s: s + + r = [inline_literal(I.getName())] + outp = r.append + level = 1 + + if I.getDoc(): + outp(_justify_and_indent(_trim_doc_string(I.getDoc()), level)) + + bases = [base + for base in I.__bases__ + if base is not zope.interface.Interface + ] + if bases: + outp(_justify_and_indent("This interface extends:", level, munge)) + level += 1 + for b in bases: + item = "o %s" % inline_literal(b.getName()) + outp(_justify_and_indent(_trim_doc_string(item), level, munge)) + level -= 1 + + namesAndDescriptions = sorted(I.namesAndDescriptions()) + + outp(_justify_and_indent("Attributes:", level, munge)) + level += 1 + for name, desc in namesAndDescriptions: + if not hasattr(desc, 'getSignatureString'): # ugh... + item = "{} -- {}".format(inline_literal(desc.getName()), + desc.getDoc() or 'no documentation') + outp(_justify_and_indent(_trim_doc_string(item), level, munge)) + level -= 1 + + outp(_justify_and_indent("Methods:", level, munge)) + level += 1 + for name, desc in namesAndDescriptions: + if hasattr(desc, 'getSignatureString'): # ugh... + _call = "{}{}".format(desc.getName(), desc.getSignatureString()) + item = "{} -- {}".format(inline_literal(_call), + desc.getDoc() or 'no documentation') + outp(_justify_and_indent(_trim_doc_string(item), level, munge)) + + return "\n\n".join(r) + "\n\n" + + +def asReStructuredText(I, munge=0): + """ Output reStructuredText format. Note, this will whack any existing + 'structured' format of the text.""" + return asStructuredText(I, munge=munge, rst=True) + + +def _trim_doc_string(text): + """ Trims a doc string to make it format + correctly with structured text. """ + + lines = text.replace('\r\n', '\n').split('\n') + nlines = [lines.pop(0)] + if lines: + min_indent = min([len(line) - len(line.lstrip()) + for line in lines]) + for line in lines: + nlines.append(line[min_indent:]) + + return '\n'.join(nlines) + + +def _justify_and_indent(text, level, munge=0, width=72): + """ indent and justify text, rejustify (munge) if specified """ + + indent = " " * level + + if munge: + lines = [] + line = indent + text = text.split() + + for word in text: + line = ' '.join([line, word]) + if len(line) > width: + lines.append(line) + line = indent + else: + lines.append(line) + + return '\n'.join(lines) + + else: + return indent + \ + text.strip().replace("\r\n", "\n") .replace("\n", "\n" + indent) diff --git a/contrib/python/zope.interface/py3/zope/interface/exceptions.py b/contrib/python/zope.interface/py3/zope/interface/exceptions.py new file mode 100644 index 0000000000..d5c234a6da --- /dev/null +++ b/contrib/python/zope.interface/py3/zope/interface/exceptions.py @@ -0,0 +1,275 @@ +############################################################################## +# +# Copyright (c) 2002 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-specific exceptions +""" + +__all__ = [ + # Invalid tree + 'Invalid', + 'DoesNotImplement', + 'BrokenImplementation', + 'BrokenMethodImplementation', + 'MultipleInvalid', + # Other + 'BadImplements', + 'InvalidInterface', +] + +class Invalid(Exception): + """A specification is violated + """ + + +class _TargetInvalid(Invalid): + # Internal use. Subclass this when you're describing + # a particular target object that's invalid according + # to a specific interface. + # + # For backwards compatibility, the *target* and *interface* are + # optional, and the signatures are inconsistent in their ordering. + # + # We deal with the inconsistency in ordering by defining the index + # of the two values in ``self.args``. *target* uses a marker object to + # distinguish "not given" from "given, but None", because the latter + # can be a value that gets passed to validation. For this reason, it must + # always be the last argument (we detect absence by the ``IndexError``). + + _IX_INTERFACE = 0 + _IX_TARGET = 1 + # The exception to catch when indexing self.args indicating that + # an argument was not given. If all arguments are expected, + # a subclass should set this to (). + _NOT_GIVEN_CATCH = IndexError + _NOT_GIVEN = '<Not Given>' + + def _get_arg_or_default(self, ix, default=None): + try: + return self.args[ix] # pylint:disable=unsubscriptable-object + except self._NOT_GIVEN_CATCH: + return default + + @property + def interface(self): + return self._get_arg_or_default(self._IX_INTERFACE) + + @property + def target(self): + return self._get_arg_or_default(self._IX_TARGET, self._NOT_GIVEN) + + ### + # str + # + # The ``__str__`` of self is implemented by concatenating (%s), in order, + # these properties (none of which should have leading or trailing + # whitespace): + # + # - self._str_subject + # Begin the message, including a description of the target. + # - self._str_description + # Provide a general description of the type of error, including + # the interface name if possible and relevant. + # - self._str_conjunction + # Join the description to the details. Defaults to ": ". + # - self._str_details + # Provide details about how this particular instance of the error. + # - self._str_trailer + # End the message. Usually just a period. + ### + + @property + def _str_subject(self): + target = self.target + if target is self._NOT_GIVEN: + return "An object" + return "The object {!r}".format(target) + + @property + def _str_description(self): + return "has failed to implement interface %s" % ( + self.interface or '<Unknown>' + ) + + _str_conjunction = ": " + _str_details = "<unknown>" + _str_trailer = '.' + + def __str__(self): + return "{} {}{}{}{}".format( + self._str_subject, + self._str_description, + self._str_conjunction, + self._str_details, + self._str_trailer + ) + + +class DoesNotImplement(_TargetInvalid): + """ + DoesNotImplement(interface[, target]) + + The *target* (optional) does not implement the *interface*. + + .. versionchanged:: 5.0.0 + Add the *target* argument and attribute, and change the resulting + string value of this object accordingly. + """ + + _str_details = "Does not declaratively implement the interface" + + +class BrokenImplementation(_TargetInvalid): + """ + BrokenImplementation(interface, name[, target]) + + The *target* (optional) is missing the attribute *name*. + + .. versionchanged:: 5.0.0 + Add the *target* argument and attribute, and change the resulting + string value of this object accordingly. + + The *name* can either be a simple string or a ``Attribute`` object. + """ + + _IX_NAME = _TargetInvalid._IX_INTERFACE + 1 + _IX_TARGET = _IX_NAME + 1 + + @property + def name(self): + return self.args[1] # pylint:disable=unsubscriptable-object + + @property + def _str_details(self): + return "The %s attribute was not provided" % ( + repr(self.name) if isinstance(self.name, str) else self.name + ) + + +class BrokenMethodImplementation(_TargetInvalid): + """ + BrokenMethodImplementation(method, message[, implementation, interface, target]) + + The *target* (optional) has a *method* in *implementation* that violates + its contract in a way described by *mess*. + + .. versionchanged:: 5.0.0 + Add the *interface* and *target* argument and attribute, + and change the resulting string value of this object accordingly. + + The *method* can either be a simple string or a ``Method`` object. + + .. versionchanged:: 5.0.0 + If *implementation* is given, then the *message* will have the + string "implementation" replaced with an short but informative + representation of *implementation*. + + """ + + _IX_IMPL = 2 + _IX_INTERFACE = _IX_IMPL + 1 + _IX_TARGET = _IX_INTERFACE + 1 + + @property + def method(self): + return self.args[0] # pylint:disable=unsubscriptable-object + + @property + def mess(self): + return self.args[1] # pylint:disable=unsubscriptable-object + + @staticmethod + def __implementation_str(impl): + # It could be a callable or some arbitrary object, we don't + # know yet. + import inspect # Inspect is a heavy-weight dependency, lots of imports + try: + sig = inspect.signature + formatsig = str + except AttributeError: + sig = inspect.getargspec + f = inspect.formatargspec + formatsig = lambda sig: f(*sig) # pylint:disable=deprecated-method + + try: + sig = sig(impl) + except (ValueError, TypeError): + # Unable to introspect. Darn. + # This could be a non-callable, or a particular builtin, + # or a bound method that doesn't even accept 'self', e.g., + # ``Class.method = lambda: None; Class().method`` + return repr(impl) + + try: + name = impl.__qualname__ + except AttributeError: + name = impl.__name__ + + return name + formatsig(sig) + + @property + def _str_details(self): + impl = self._get_arg_or_default(self._IX_IMPL, self._NOT_GIVEN) + message = self.mess + if impl is not self._NOT_GIVEN and 'implementation' in message: + message = message.replace("implementation", '%r') + message = message % (self.__implementation_str(impl),) + + return 'The contract of {} is violated because {}'.format( + repr(self.method) if isinstance(self.method, str) else self.method, + message, + ) + + +class MultipleInvalid(_TargetInvalid): + """ + The *target* has failed to implement the *interface* in + multiple ways. + + The failures are described by *exceptions*, a collection of + other `Invalid` instances. + + .. versionadded:: 5.0 + """ + + _NOT_GIVEN_CATCH = () + + def __init__(self, interface, target, exceptions): + super().__init__(interface, target, tuple(exceptions)) + + @property + def exceptions(self): + return self.args[2] # pylint:disable=unsubscriptable-object + + @property + def _str_details(self): + # It would be nice to use tabs here, but that + # is hard to represent in doctests. + return '\n ' + '\n '.join( + x._str_details.strip() if isinstance(x, _TargetInvalid) else str(x) + for x in self.exceptions + ) + + _str_conjunction = ':' # We don't want a trailing space, messes up doctests + _str_trailer = '' + + +class InvalidInterface(Exception): + """The interface has invalid contents + """ + +class BadImplements(TypeError): + """An implementation assertion is invalid + + because it doesn't contain an interface or a sequence of valid + implementation assertions. + """ diff --git a/contrib/python/zope.interface/py3/zope/interface/interface.py b/contrib/python/zope.interface/py3/zope/interface/interface.py new file mode 100644 index 0000000000..1bd6f9e818 --- /dev/null +++ b/contrib/python/zope.interface/py3/zope/interface/interface.py @@ -0,0 +1,1131 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 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 object implementation +""" +# pylint:disable=protected-access +import sys +from types import MethodType +from types import FunctionType +import weakref + +from zope.interface._compat import _use_c_impl +from zope.interface.exceptions import Invalid +from zope.interface.ro import ro as calculate_ro +from zope.interface import ro + +__all__ = [ + # Most of the public API from this module is directly exported + # from zope.interface. The only remaining public API intended to + # be imported from here should be those few things documented as + # such. + 'InterfaceClass', + 'Specification', + 'adapter_hooks', +] + +CO_VARARGS = 4 +CO_VARKEYWORDS = 8 +# Put in the attrs dict of an interface by ``taggedValue`` and ``invariants`` +TAGGED_DATA = '__interface_tagged_values__' +# Put in the attrs dict of an interface by ``interfacemethod`` +INTERFACE_METHODS = '__interface_methods__' + +_decorator_non_return = object() +_marker = object() + + + +def invariant(call): + f_locals = sys._getframe(1).f_locals + tags = f_locals.setdefault(TAGGED_DATA, {}) + invariants = tags.setdefault('invariants', []) + invariants.append(call) + return _decorator_non_return + + +def taggedValue(key, value): + """Attaches a tagged value to an interface at definition time.""" + f_locals = sys._getframe(1).f_locals + tagged_values = f_locals.setdefault(TAGGED_DATA, {}) + tagged_values[key] = value + return _decorator_non_return + + +class Element: + """ + Default implementation of `zope.interface.interfaces.IElement`. + """ + + # We can't say this yet because we don't have enough + # infrastructure in place. + # + #implements(IElement) + + def __init__(self, __name__, __doc__=''): # pylint:disable=redefined-builtin + if not __doc__ and __name__.find(' ') >= 0: + __doc__ = __name__ + __name__ = None + + self.__name__ = __name__ + self.__doc__ = __doc__ + # Tagged values are rare, especially on methods or attributes. + # Deferring the allocation can save substantial memory. + self.__tagged_values = None + + def getName(self): + """ Returns the name of the object. """ + return self.__name__ + + def getDoc(self): + """ Returns the documentation for the object. """ + return self.__doc__ + + ### + # Tagged values. + # + # Direct tagged values are set only in this instance. Others + # may be inherited (for those subclasses that have that concept). + ### + + def getTaggedValue(self, tag): + """ Returns the value associated with 'tag'. """ + if not self.__tagged_values: + raise KeyError(tag) + return self.__tagged_values[tag] + + def queryTaggedValue(self, tag, default=None): + """ Returns the value associated with 'tag'. """ + return self.__tagged_values.get(tag, default) if self.__tagged_values else default + + def getTaggedValueTags(self): + """ Returns a collection of all tags. """ + return self.__tagged_values.keys() if self.__tagged_values else () + + def setTaggedValue(self, tag, value): + """ Associates 'value' with 'key'. """ + if self.__tagged_values is None: + self.__tagged_values = {} + self.__tagged_values[tag] = value + + queryDirectTaggedValue = queryTaggedValue + getDirectTaggedValue = getTaggedValue + getDirectTaggedValueTags = getTaggedValueTags + + +SpecificationBasePy = object # filled by _use_c_impl. + + +@_use_c_impl +class SpecificationBase: + # This object is the base of the inheritance hierarchy for ClassProvides: + # + # ClassProvides < ClassProvidesBase, Declaration + # Declaration < Specification < SpecificationBase + # ClassProvidesBase < SpecificationBase + # + # In order to have compatible instance layouts, we need to declare + # the storage used by Specification and Declaration here (and + # those classes must have ``__slots__ = ()``); fortunately this is + # not a waste of space because those are the only two inheritance + # trees. These all translate into tp_members in C. + __slots__ = ( + # Things used here. + '_implied', + # Things used in Specification. + '_dependents', + '_bases', + '_v_attrs', + '__iro__', + '__sro__', + '__weakref__', + ) + + def providedBy(self, ob): + """Is the interface implemented by an object + """ + spec = providedBy(ob) + return self in spec._implied + + def implementedBy(self, cls): + """Test whether the specification is implemented by a class or factory. + + Raise TypeError if argument is neither a class nor a callable. + """ + spec = implementedBy(cls) + return self in spec._implied + + def isOrExtends(self, interface): + """Is the interface the same as or extend the given interface + """ + return interface in self._implied # pylint:disable=no-member + + __call__ = isOrExtends + + +class NameAndModuleComparisonMixin: + # Internal use. Implement the basic sorting operators (but not (in)equality + # or hashing). Subclasses must provide ``__name__`` and ``__module__`` + # attributes. Subclasses will be mutually comparable; but because equality + # and hashing semantics are missing from this class, take care in how + # you define those two attributes: If you stick with the default equality + # and hashing (identity based) you should make sure that all possible ``__name__`` + # and ``__module__`` pairs are unique ACROSS ALL SUBCLASSES. (Actually, pretty + # much the same thing goes if you define equality and hashing to be based on + # those two attributes: they must still be consistent ACROSS ALL SUBCLASSES.) + + # pylint:disable=assigning-non-slot + __slots__ = () + + def _compare(self, other): + """ + Compare *self* to *other* based on ``__name__`` and ``__module__``. + + Return 0 if they are equal, return 1 if *self* is + greater than *other*, and return -1 if *self* is less than + *other*. + + If *other* does not have ``__name__`` or ``__module__``, then + return ``NotImplemented``. + + .. caution:: + This allows comparison to things well outside the type hierarchy, + perhaps not symmetrically. + + For example, ``class Foo(object)`` and ``class Foo(Interface)`` + in the same file would compare equal, depending on the order of + operands. Writing code like this by hand would be unusual, but it could + happen with dynamic creation of types and interfaces. + + None is treated as a pseudo interface that implies the loosest + contact possible, no contract. For that reason, all interfaces + sort before None. + """ + if other is self: + return 0 + + if other is None: + return -1 + + n1 = (self.__name__, self.__module__) + try: + n2 = (other.__name__, other.__module__) + except AttributeError: + return NotImplemented + + # This spelling works under Python3, which doesn't have cmp(). + return (n1 > n2) - (n1 < n2) + + def __lt__(self, other): + c = self._compare(other) + if c is NotImplemented: + return c + return c < 0 + + def __le__(self, other): + c = self._compare(other) + if c is NotImplemented: + return c + return c <= 0 + + def __gt__(self, other): + c = self._compare(other) + if c is NotImplemented: + return c + return c > 0 + + def __ge__(self, other): + c = self._compare(other) + if c is NotImplemented: + return c + return c >= 0 + + +@_use_c_impl +class InterfaceBase(NameAndModuleComparisonMixin, SpecificationBasePy): + """Base class that wants to be replaced with a C base :) + """ + + __slots__ = ( + '__name__', + '__ibmodule__', + '_v_cached_hash', + ) + + def __init__(self, name=None, module=None): + self.__name__ = name + self.__ibmodule__ = module + + def _call_conform(self, conform): + raise NotImplementedError + + @property + def __module_property__(self): + # This is for _InterfaceMetaClass + return self.__ibmodule__ + + def __call__(self, obj, alternate=_marker): + """Adapt an object to the interface + """ + try: + conform = obj.__conform__ + except AttributeError: + conform = None + + if conform is not None: + adapter = self._call_conform(conform) + if adapter is not None: + return adapter + + adapter = self.__adapt__(obj) + + if adapter is not None: + return adapter + if alternate is not _marker: + return alternate + raise TypeError("Could not adapt", obj, self) + + def __adapt__(self, obj): + """Adapt an object to the receiver + """ + if self.providedBy(obj): + return obj + + for hook in adapter_hooks: + adapter = hook(self, obj) + if adapter is not None: + return adapter + + return None + + def __hash__(self): + # pylint:disable=assigning-non-slot,attribute-defined-outside-init + try: + return self._v_cached_hash + except AttributeError: + self._v_cached_hash = hash((self.__name__, self.__module__)) + return self._v_cached_hash + + def __eq__(self, other): + c = self._compare(other) + if c is NotImplemented: + return c + return c == 0 + + def __ne__(self, other): + if other is self: + return False + + c = self._compare(other) + if c is NotImplemented: + return c + return c != 0 + +adapter_hooks = _use_c_impl([], 'adapter_hooks') + + +class Specification(SpecificationBase): + """Specifications + + An interface specification is used to track interface declarations + and component registrations. + + This class is a base class for both interfaces themselves and for + interface specifications (declarations). + + Specifications are mutable. If you reassign their bases, their + relations with other specifications are adjusted accordingly. + """ + __slots__ = () + + # The root of all Specifications. This will be assigned `Interface`, + # once it is defined. + _ROOT = None + + # Copy some base class methods for speed + isOrExtends = SpecificationBase.isOrExtends + providedBy = SpecificationBase.providedBy + + def __init__(self, bases=()): + # There are many leaf interfaces with no dependents, + # and a few with very many. It's a heavily left-skewed + # distribution. In a survey of Plone and Zope related packages + # that loaded 2245 InterfaceClass objects and 2235 ClassProvides + # instances, there were a total of 7000 Specification objects created. + # 4700 had 0 dependents, 1400 had 1, 382 had 2 and so on. Only one + # for <type> had 1664. So there's savings to be had deferring + # the creation of dependents. + self._dependents = None # type: weakref.WeakKeyDictionary + self._bases = () + self._implied = {} + self._v_attrs = None + self.__iro__ = () + self.__sro__ = () + + self.__bases__ = tuple(bases) + + @property + def dependents(self): + if self._dependents is None: + self._dependents = weakref.WeakKeyDictionary() + return self._dependents + + def subscribe(self, dependent): + self._dependents[dependent] = self.dependents.get(dependent, 0) + 1 + + def unsubscribe(self, dependent): + try: + n = self._dependents[dependent] + except TypeError: + raise KeyError(dependent) + n -= 1 + if not n: + del self.dependents[dependent] + else: + assert n > 0 + self.dependents[dependent] = n + + def __setBases(self, bases): + # Remove ourselves as a dependent of our old bases + for b in self.__bases__: + b.unsubscribe(self) + + # Register ourselves as a dependent of our new bases + self._bases = bases + for b in bases: + b.subscribe(self) + + self.changed(self) + + __bases__ = property( + lambda self: self._bases, + __setBases, + ) + + # This method exists for tests to override the way we call + # ro.calculate_ro(), usually by adding extra kwargs. We don't + # want to have a mutable dictionary as a class member that we pass + # ourself because mutability is bad, and passing **kw is slower than + # calling the bound function. + _do_calculate_ro = calculate_ro + + def _calculate_sro(self): + """ + Calculate and return the resolution order for this object, using its ``__bases__``. + + Ensures that ``Interface`` is always the last (lowest priority) element. + """ + # We'd like to make Interface the lowest priority as a + # property of the resolution order algorithm. That almost + # works out naturally, but it fails when class inheritance has + # some bases that DO implement an interface, and some that DO + # NOT. In such a mixed scenario, you wind up with a set of + # bases to consider that look like this: [[..., Interface], + # [..., object], ...]. Depending on the order of inheritance, + # Interface can wind up before or after object, and that can + # happen at any point in the tree, meaning Interface can wind + # up somewhere in the middle of the order. Since Interface is + # treated as something that everything winds up implementing + # anyway (a catch-all for things like adapters), having it high up + # the order is bad. It's also bad to have it at the end, just before + # some concrete class: concrete classes should be HIGHER priority than + # interfaces (because there's only one class, but many implementations). + # + # One technically nice way to fix this would be to have + # ``implementedBy(object).__bases__ = (Interface,)`` + # + # But: (1) That fails for old-style classes and (2) that causes + # everything to appear to *explicitly* implement Interface, when up + # to this point it's been an implicit virtual sort of relationship. + # + # So we force the issue by mutating the resolution order. + + # Note that we let C3 use pre-computed __sro__ for our bases. + # This requires that by the time this method is invoked, our bases + # have settled their SROs. Thus, ``changed()`` must first + # update itself before telling its descendents of changes. + sro = self._do_calculate_ro(base_mros={ + b: b.__sro__ + for b in self.__bases__ + }) + root = self._ROOT + if root is not None and sro and sro[-1] is not root: + # In one dataset of 1823 Interface objects, 1117 ClassProvides objects, + # sro[-1] was root 4496 times, and only not root 118 times. So it's + # probably worth checking. + + # Once we don't have to deal with old-style classes, + # we can add a check and only do this if base_count > 1, + # if we tweak the bootstrapping for ``<implementedBy object>`` + sro = [ + x + for x in sro + if x is not root + ] + sro.append(root) + + return sro + + def changed(self, originally_changed): + """ + We, or something we depend on, have changed. + + By the time this is called, the things we depend on, + such as our bases, should themselves be stable. + """ + self._v_attrs = None + + implied = self._implied + implied.clear() + + ancestors = self._calculate_sro() + self.__sro__ = tuple(ancestors) + self.__iro__ = tuple([ancestor for ancestor in ancestors + if isinstance(ancestor, InterfaceClass) + ]) + + for ancestor in ancestors: + # We directly imply our ancestors: + implied[ancestor] = () + + # Now, advise our dependents of change + # (being careful not to create the WeakKeyDictionary if not needed): + for dependent in tuple(self._dependents.keys() if self._dependents else ()): + dependent.changed(originally_changed) + + # Just in case something called get() at some point + # during that process and we have a cycle of some sort + # make sure we didn't cache incomplete results. + self._v_attrs = None + + def interfaces(self): + """Return an iterator for the interfaces in the specification. + """ + seen = {} + for base in self.__bases__: + for interface in base.interfaces(): + if interface not in seen: + seen[interface] = 1 + yield interface + + def extends(self, interface, strict=True): + """Does the specification extend the given interface? + + Test whether an interface in the specification extends the + given interface + """ + return ((interface in self._implied) + and + ((not strict) or (self != interface)) + ) + + def weakref(self, callback=None): + return weakref.ref(self, callback) + + def get(self, name, default=None): + """Query for an attribute description + """ + attrs = self._v_attrs + if attrs is None: + attrs = self._v_attrs = {} + attr = attrs.get(name) + if attr is None: + for iface in self.__iro__: + attr = iface.direct(name) + if attr is not None: + attrs[name] = attr + break + + return default if attr is None else attr + + +class _InterfaceMetaClass(type): + # Handling ``__module__`` on ``InterfaceClass`` is tricky. We need + # to be able to read it on a type and get the expected string. We + # also need to be able to set it on an instance and get the value + # we set. So far so good. But what gets tricky is that we'd like + # to store the value in the C structure (``InterfaceBase.__ibmodule__``) for + # direct access during equality, sorting, and hashing. "No + # problem, you think, I'll just use a property" (well, the C + # equivalents, ``PyMemberDef`` or ``PyGetSetDef``). + # + # Except there is a problem. When a subclass is created, the + # metaclass (``type``) always automatically puts the expected + # string in the class's dictionary under ``__module__``, thus + # overriding the property inherited from the superclass. Writing + # ``Subclass.__module__`` still works, but + # ``Subclass().__module__`` fails. + # + # There are multiple ways to work around this: + # + # (1) Define ``InterfaceBase.__getattribute__`` to watch for + # ``__module__`` and return the C storage. + # + # This works, but slows down *all* attribute access (except, + # ironically, to ``__module__``) by about 25% (40ns becomes 50ns) + # (when implemented in C). Since that includes methods like + # ``providedBy``, that's probably not acceptable. + # + # All the other methods involve modifying subclasses. This can be + # done either on the fly in some cases, as instances are + # constructed, or by using a metaclass. These next few can be done on the fly. + # + # (2) Make ``__module__`` a descriptor in each subclass dictionary. + # It can't be a straight up ``@property`` descriptor, though, because accessing + # it on the class returns a ``property`` object, not the desired string. + # + # (3) Implement a data descriptor (``__get__`` and ``__set__``) + # that is both a subclass of string, and also does the redirect of + # ``__module__`` to ``__ibmodule__`` and does the correct thing + # with the ``instance`` argument to ``__get__`` is None (returns + # the class's value.) (Why must it be a subclass of string? Because + # when it' s in the class's dict, it's defined on an *instance* of the + # metaclass; descriptors in an instance's dict aren't honored --- their + # ``__get__`` is never invoked --- so it must also *be* the value we want + # returned.) + # + # This works, preserves the ability to read and write + # ``__module__``, and eliminates any penalty accessing other + # attributes. But it slows down accessing ``__module__`` of + # instances by 200% (40ns to 124ns), requires editing class dicts on the fly + # (in InterfaceClass.__init__), thus slightly slowing down all interface creation, + # and is ugly. + # + # (4) As in the last step, but make it a non-data descriptor (no ``__set__``). + # + # If you then *also* store a copy of ``__ibmodule__`` in + # ``__module__`` in the instance's dict, reading works for both + # class and instance and is full speed for instances. But the cost + # is storage space, and you can't write to it anymore, not without + # things getting out of sync. + # + # (Actually, ``__module__`` was never meant to be writable. Doing + # so would break BTrees and normal dictionaries, as well as the + # repr, maybe more.) + # + # That leaves us with a metaclass. (Recall that a class is an + # instance of its metaclass, so properties/descriptors defined in + # the metaclass are used when accessing attributes on the + # instance/class. We'll use that to define ``__module__``.) Here + # we can have our cake and eat it too: no extra storage, and + # C-speed access to the underlying storage. The only substantial + # cost is that metaclasses tend to make people's heads hurt. (But + # still less than the descriptor-is-string, hopefully.) + + __slots__ = () + + def __new__(cls, name, bases, attrs): + # Figure out what module defined the interface. + # This is copied from ``InterfaceClass.__init__``; + # reviewers aren't sure how AttributeError or KeyError + # could be raised. + __module__ = sys._getframe(1).f_globals['__name__'] + # Get the C optimized __module__ accessor and give it + # to the new class. + moduledescr = InterfaceBase.__dict__['__module__'] + if isinstance(moduledescr, str): + # We're working with the Python implementation, + # not the C version + moduledescr = InterfaceBase.__dict__['__module_property__'] + attrs['__module__'] = moduledescr + kind = type.__new__(cls, name, bases, attrs) + kind.__module = __module__ + return kind + + @property + def __module__(cls): + return cls.__module + + def __repr__(cls): + return "<class '{}.{}'>".format( + cls.__module, + cls.__name__, + ) + + +_InterfaceClassBase = _InterfaceMetaClass( + 'InterfaceClass', + # From least specific to most specific. + (InterfaceBase, Specification, Element), + {'__slots__': ()} +) + + +def interfacemethod(func): + """ + Convert a method specification to an actual method of the interface. + + This is a decorator that functions like `staticmethod` et al. + + The primary use of this decorator is to allow interface definitions to + define the ``__adapt__`` method, but other interface methods can be + overridden this way too. + + .. seealso:: `zope.interface.interfaces.IInterfaceDeclaration.interfacemethod` + """ + f_locals = sys._getframe(1).f_locals + methods = f_locals.setdefault(INTERFACE_METHODS, {}) + methods[func.__name__] = func + return _decorator_non_return + + +class InterfaceClass(_InterfaceClassBase): + """ + Prototype (scarecrow) Interfaces Implementation. + + Note that it is not possible to change the ``__name__`` or ``__module__`` + after an instance of this object has been constructed. + """ + + # We can't say this yet because we don't have enough + # infrastructure in place. + # + #implements(IInterface) + + def __new__(cls, name=None, bases=(), attrs=None, __doc__=None, # pylint:disable=redefined-builtin + __module__=None): + assert isinstance(bases, tuple) + attrs = attrs or {} + needs_custom_class = attrs.pop(INTERFACE_METHODS, None) + if needs_custom_class: + needs_custom_class.update( + {'__classcell__': attrs.pop('__classcell__')} + if '__classcell__' in attrs + else {} + ) + if '__adapt__' in needs_custom_class: + # We need to tell the C code to call this. + needs_custom_class['_CALL_CUSTOM_ADAPT'] = 1 + + if issubclass(cls, _InterfaceClassWithCustomMethods): + cls_bases = (cls,) + elif cls is InterfaceClass: + cls_bases = (_InterfaceClassWithCustomMethods,) + else: + cls_bases = (cls, _InterfaceClassWithCustomMethods) + + cls = type(cls)( # pylint:disable=self-cls-assignment + name + "<WithCustomMethods>", + cls_bases, + needs_custom_class + ) + + return _InterfaceClassBase.__new__(cls) + + def __init__(self, name, bases=(), attrs=None, __doc__=None, # pylint:disable=redefined-builtin + __module__=None): + # We don't call our metaclass parent directly + # pylint:disable=non-parent-init-called + # pylint:disable=super-init-not-called + if not all(isinstance(base, InterfaceClass) for base in bases): + raise TypeError('Expected base interfaces') + + if attrs is None: + attrs = {} + + if __module__ is None: + __module__ = attrs.get('__module__') + if isinstance(__module__, str): + del attrs['__module__'] + else: + try: + # Figure out what module defined the interface. + # This is how cPython figures out the module of + # a class, but of course it does it in C. :-/ + __module__ = sys._getframe(1).f_globals['__name__'] + except (AttributeError, KeyError): # pragma: no cover + pass + + InterfaceBase.__init__(self, name, __module__) + # These asserts assisted debugging the metaclass + # assert '__module__' not in self.__dict__ + # assert self.__ibmodule__ is self.__module__ is __module__ + + d = attrs.get('__doc__') + if d is not None: + if not isinstance(d, Attribute): + if __doc__ is None: + __doc__ = d + del attrs['__doc__'] + + if __doc__ is None: + __doc__ = '' + + Element.__init__(self, name, __doc__) + + tagged_data = attrs.pop(TAGGED_DATA, None) + if tagged_data is not None: + for key, val in tagged_data.items(): + self.setTaggedValue(key, val) + + Specification.__init__(self, bases) + self.__attrs = self.__compute_attrs(attrs) + + self.__identifier__ = "{}.{}".format(__module__, name) + + def __compute_attrs(self, attrs): + # Make sure that all recorded attributes (and methods) are of type + # `Attribute` and `Method` + def update_value(aname, aval): + if isinstance(aval, Attribute): + aval.interface = self + if not aval.__name__: + aval.__name__ = aname + elif isinstance(aval, FunctionType): + aval = fromFunction(aval, self, name=aname) + else: + raise InvalidInterface("Concrete attribute, " + aname) + return aval + + return { + aname: update_value(aname, aval) + for aname, aval in attrs.items() + if aname not in ( + # __locals__: Python 3 sometimes adds this. + '__locals__', + # __qualname__: PEP 3155 (Python 3.3+) + '__qualname__', + # __annotations__: PEP 3107 (Python 3.0+) + '__annotations__', + ) + and aval is not _decorator_non_return + } + + def interfaces(self): + """Return an iterator for the interfaces in the specification. + """ + yield self + + def getBases(self): + return self.__bases__ + + def isEqualOrExtendedBy(self, other): + """Same interface or extends?""" + return self == other or other.extends(self) + + def names(self, all=False): # pylint:disable=redefined-builtin + """Return the attribute names defined by the interface.""" + if not all: + return self.__attrs.keys() + + r = self.__attrs.copy() + + for base in self.__bases__: + r.update(dict.fromkeys(base.names(all))) + + return r.keys() + + def __iter__(self): + return iter(self.names(all=True)) + + def namesAndDescriptions(self, all=False): # pylint:disable=redefined-builtin + """Return attribute names and descriptions defined by interface.""" + if not all: + return self.__attrs.items() + + r = {} + for base in self.__bases__[::-1]: + r.update(dict(base.namesAndDescriptions(all))) + + r.update(self.__attrs) + + return r.items() + + def getDescriptionFor(self, name): + """Return the attribute description for the given name.""" + r = self.get(name) + if r is not None: + return r + + raise KeyError(name) + + __getitem__ = getDescriptionFor + + def __contains__(self, name): + return self.get(name) is not None + + def direct(self, name): + return self.__attrs.get(name) + + def queryDescriptionFor(self, name, default=None): + return self.get(name, default) + + def validateInvariants(self, obj, errors=None): + """validate object to defined invariants.""" + + for iface in self.__iro__: + for invariant in iface.queryDirectTaggedValue('invariants', ()): + try: + invariant(obj) + except Invalid as error: + if errors is not None: + errors.append(error) + else: + raise + + if errors: + raise Invalid(errors) + + def queryTaggedValue(self, tag, default=None): + """ + Queries for the value associated with *tag*, returning it from the nearest + interface in the ``__iro__``. + + If not found, returns *default*. + """ + for iface in self.__iro__: + value = iface.queryDirectTaggedValue(tag, _marker) + if value is not _marker: + return value + return default + + def getTaggedValue(self, tag): + """ Returns the value associated with 'tag'. """ + value = self.queryTaggedValue(tag, default=_marker) + if value is _marker: + raise KeyError(tag) + return value + + def getTaggedValueTags(self): + """ Returns a list of all tags. """ + keys = set() + for base in self.__iro__: + keys.update(base.getDirectTaggedValueTags()) + return keys + + def __repr__(self): + try: + return self._v_repr + except AttributeError: + name = str(self) + r = "<{} {}>".format(self.__class__.__name__, name) + self._v_repr = r # pylint:disable=attribute-defined-outside-init + return r + + def __str__(self): + name = self.__name__ + m = self.__ibmodule__ + if m: + name = '{}.{}'.format(m, name) + return name + + def _call_conform(self, conform): + try: + return conform(self) + except TypeError: # pragma: no cover + # We got a TypeError. It might be an error raised by + # the __conform__ implementation, or *we* may have + # made the TypeError by calling an unbound method + # (object is a class). In the later case, we behave + # as though there is no __conform__ method. We can + # detect this case by checking whether there is more + # than one traceback object in the traceback chain: + if sys.exc_info()[2].tb_next is not None: + # There is more than one entry in the chain, so + # reraise the error: + raise + # This clever trick is from Phillip Eby + + return None # pragma: no cover + + def __reduce__(self): + return self.__name__ + +Interface = InterfaceClass("Interface", __module__='zope.interface') +# Interface is the only member of its own SRO. +Interface._calculate_sro = lambda: (Interface,) +Interface.changed(Interface) +assert Interface.__sro__ == (Interface,) +Specification._ROOT = Interface +ro._ROOT = Interface + +class _InterfaceClassWithCustomMethods(InterfaceClass): + """ + Marker class for interfaces with custom methods that override InterfaceClass methods. + """ + + +class Attribute(Element): + """Attribute descriptions + """ + + # We can't say this yet because we don't have enough + # infrastructure in place. + # + # implements(IAttribute) + + interface = None + + def _get_str_info(self): + """Return extra data to put at the end of __str__.""" + return "" + + def __str__(self): + of = '' + if self.interface is not None: + of = self.interface.__module__ + '.' + self.interface.__name__ + '.' + # self.__name__ may be None during construction (e.g., debugging) + return of + (self.__name__ or '<unknown>') + self._get_str_info() + + def __repr__(self): + return "<{}.{} object at 0x{:x} {}>".format( + type(self).__module__, + type(self).__name__, + id(self), + self + ) + + +class Method(Attribute): + """Method interfaces + + The idea here is that you have objects that describe methods. + This provides an opportunity for rich meta-data. + """ + + # We can't say this yet because we don't have enough + # infrastructure in place. + # + # implements(IMethod) + + positional = required = () + _optional = varargs = kwargs = None + def _get_optional(self): + if self._optional is None: + return {} + return self._optional + def _set_optional(self, opt): + self._optional = opt + def _del_optional(self): + self._optional = None + optional = property(_get_optional, _set_optional, _del_optional) + + def __call__(self, *args, **kw): + raise BrokenImplementation(self.interface, self.__name__) + + def getSignatureInfo(self): + return {'positional': self.positional, + 'required': self.required, + 'optional': self.optional, + 'varargs': self.varargs, + 'kwargs': self.kwargs, + } + + def getSignatureString(self): + sig = [] + for v in self.positional: + sig.append(v) + if v in self.optional.keys(): + sig[-1] += "=" + repr(self.optional[v]) + if self.varargs: + sig.append("*" + self.varargs) + if self.kwargs: + sig.append("**" + self.kwargs) + + return "(%s)" % ", ".join(sig) + + _get_str_info = getSignatureString + + +def fromFunction(func, interface=None, imlevel=0, name=None): + name = name or func.__name__ + method = Method(name, func.__doc__) + defaults = getattr(func, '__defaults__', None) or () + code = func.__code__ + # Number of positional arguments + na = code.co_argcount - imlevel + names = code.co_varnames[imlevel:] + opt = {} + # Number of required arguments + defaults_count = len(defaults) + if not defaults_count: + # PyPy3 uses ``__defaults_count__`` for builtin methods + # like ``dict.pop``. Surprisingly, these don't have recorded + # ``__defaults__`` + defaults_count = getattr(func, '__defaults_count__', 0) + + nr = na - defaults_count + if nr < 0: + defaults = defaults[-nr:] + nr = 0 + + # Determine the optional arguments. + opt.update(dict(zip(names[nr:], defaults))) + + method.positional = names[:na] + method.required = names[:nr] + method.optional = opt + + argno = na + + # Determine the function's variable argument's name (i.e. *args) + if code.co_flags & CO_VARARGS: + method.varargs = names[argno] + argno = argno + 1 + else: + method.varargs = None + + # Determine the function's keyword argument's name (i.e. **kw) + if code.co_flags & CO_VARKEYWORDS: + method.kwargs = names[argno] + else: + method.kwargs = None + + method.interface = interface + + for key, value in func.__dict__.items(): + method.setTaggedValue(key, value) + + return method + + +def fromMethod(meth, interface=None, name=None): + if isinstance(meth, MethodType): + func = meth.__func__ + else: + func = meth + return fromFunction(func, interface, imlevel=1, name=name) + + +# Now we can create the interesting interfaces and wire them up: +def _wire(): + from zope.interface.declarations import classImplements + # From lest specific to most specific. + from zope.interface.interfaces import IElement + classImplements(Element, IElement) + + from zope.interface.interfaces import IAttribute + classImplements(Attribute, IAttribute) + + from zope.interface.interfaces import IMethod + classImplements(Method, IMethod) + + from zope.interface.interfaces import ISpecification + classImplements(Specification, ISpecification) + + from zope.interface.interfaces import IInterface + classImplements(InterfaceClass, IInterface) + + +# We import this here to deal with module dependencies. +# pylint:disable=wrong-import-position +from zope.interface.declarations import implementedBy +from zope.interface.declarations import providedBy +from zope.interface.exceptions import InvalidInterface +from zope.interface.exceptions import BrokenImplementation + +# This ensures that ``Interface`` winds up in the flattened() +# list of the immutable declaration. It correctly overrides changed() +# as a no-op, so we bypass that. +from zope.interface.declarations import _empty +Specification.changed(_empty, _empty) diff --git a/contrib/python/zope.interface/py3/zope/interface/interfaces.py b/contrib/python/zope.interface/py3/zope/interface/interfaces.py new file mode 100644 index 0000000000..2b67ce1a9e --- /dev/null +++ b/contrib/python/zope.interface/py3/zope/interface/interfaces.py @@ -0,0 +1,1480 @@ +############################################################################## +# +# Copyright (c) 2002 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 Package Interfaces +""" +__docformat__ = 'restructuredtext' + +from zope.interface.interface import Attribute +from zope.interface.interface import Interface +from zope.interface.declarations import implementer + +__all__ = [ + 'ComponentLookupError', + 'IAdapterRegistration', + 'IAdapterRegistry', + 'IAttribute', + 'IComponentLookup', + 'IComponentRegistry', + 'IComponents', + 'IDeclaration', + 'IElement', + 'IHandlerRegistration', + 'IInterface', + 'IInterfaceDeclaration', + 'IMethod', + 'Invalid', + 'IObjectEvent', + 'IRegistered', + 'IRegistration', + 'IRegistrationEvent', + 'ISpecification', + 'ISubscriptionAdapterRegistration', + 'IUnregistered', + 'IUtilityRegistration', + 'ObjectEvent', + 'Registered', + 'Unregistered', +] + +# pylint:disable=inherit-non-class,no-method-argument,no-self-argument +# pylint:disable=unexpected-special-method-signature +# pylint:disable=too-many-lines + +class IElement(Interface): + """ + Objects that have basic documentation and tagged values. + + Known derivatives include :class:`IAttribute` and its derivative + :class:`IMethod`; these have no notion of inheritance. + :class:`IInterface` is also a derivative, and it does have a + notion of inheritance, expressed through its ``__bases__`` and + ordered in its ``__iro__`` (both defined by + :class:`ISpecification`). + """ + + # pylint:disable=arguments-differ + + # Note that defining __doc__ as an Attribute hides the docstring + # from introspection. When changing it, also change it in the Sphinx + # ReST files. + + __name__ = Attribute('__name__', 'The object name') + __doc__ = Attribute('__doc__', 'The object doc string') + + ### + # Tagged values. + # + # Direct values are established in this instance. Others may be + # inherited. Although ``IElement`` itself doesn't have a notion of + # inheritance, ``IInterface`` *does*. It might have been better to + # make ``IInterface`` define new methods + # ``getIndirectTaggedValue``, etc, to include inheritance instead + # of overriding ``getTaggedValue`` to do that, but that ship has sailed. + # So to keep things nice and symmetric, we define the ``Direct`` methods here. + ### + + def getTaggedValue(tag): + """Returns the value associated with *tag*. + + Raise a `KeyError` if the tag isn't set. + + If the object has a notion of inheritance, this searches + through the inheritance hierarchy and returns the nearest result. + If there is no such notion, this looks only at this object. + + .. versionchanged:: 4.7.0 + This method should respect inheritance if present. + """ + + def queryTaggedValue(tag, default=None): + """ + As for `getTaggedValue`, but instead of raising a `KeyError`, returns *default*. + + + .. versionchanged:: 4.7.0 + This method should respect inheritance if present. + """ + + def getTaggedValueTags(): + """ + Returns a collection of all tags in no particular order. + + If the object has a notion of inheritance, this + includes all the inherited tagged values. If there is + no such notion, this looks only at this object. + + .. versionchanged:: 4.7.0 + This method should respect inheritance if present. + """ + + def setTaggedValue(tag, value): + """ + Associates *value* with *key* directly in this object. + """ + + def getDirectTaggedValue(tag): + """ + As for `getTaggedValue`, but never includes inheritance. + + .. versionadded:: 5.0.0 + """ + + def queryDirectTaggedValue(tag, default=None): + """ + As for `queryTaggedValue`, but never includes inheritance. + + .. versionadded:: 5.0.0 + """ + + def getDirectTaggedValueTags(): + """ + As for `getTaggedValueTags`, but includes only tags directly + set on this object. + + .. versionadded:: 5.0.0 + """ + + +class IAttribute(IElement): + """Attribute descriptors""" + + interface = Attribute('interface', + 'Stores the interface instance in which the ' + 'attribute is located.') + + +class IMethod(IAttribute): + """Method attributes""" + + def getSignatureInfo(): + """Returns the signature information. + + This method returns a dictionary with the following string keys: + + - positional + A sequence of the names of positional arguments. + - required + A sequence of the names of required arguments. + - optional + A dictionary mapping argument names to their default values. + - varargs + The name of the varargs argument (or None). + - kwargs + The name of the kwargs argument (or None). + """ + + def getSignatureString(): + """Return a signature string suitable for inclusion in documentation. + + This method returns the function signature string. For example, if you + have ``def func(a, b, c=1, d='f')``, then the signature string is ``"(a, b, + c=1, d='f')"``. + """ + +class ISpecification(Interface): + """Object Behavioral specifications""" + # pylint:disable=arguments-differ + def providedBy(object): # pylint:disable=redefined-builtin + """Test whether the interface is implemented by the object + + Return true of the object asserts that it implements the + interface, including asserting that it implements an extended + interface. + """ + + def implementedBy(class_): + """Test whether the interface is implemented by instances of the class + + Return true of the class asserts that its instances implement the + interface, including asserting that they implement an extended + interface. + """ + + def isOrExtends(other): + """Test whether the specification is or extends another + """ + + def extends(other, strict=True): + """Test whether a specification extends another + + The specification extends other if it has other as a base + interface or if one of it's bases extends other. + + If strict is false, then the specification extends itself. + """ + + def weakref(callback=None): + """Return a weakref to the specification + + This method is, regrettably, needed to allow weakrefs to be + computed to security-proxied specifications. While the + zope.interface package does not require zope.security or + zope.proxy, it has to be able to coexist with it. + + """ + + __bases__ = Attribute("""Base specifications + + A tuple of specifications from which this specification is + directly derived. + + """) + + __sro__ = Attribute("""Specification-resolution order + + A tuple of the specification and all of it's ancestor + specifications from most specific to least specific. The specification + itself is the first element. + + (This is similar to the method-resolution order for new-style classes.) + """) + + __iro__ = Attribute("""Interface-resolution order + + A tuple of the specification's ancestor interfaces from + most specific to least specific. The specification itself is + included if it is an interface. + + (This is similar to the method-resolution order for new-style classes.) + """) + + def get(name, default=None): + """Look up the description for a name + + If the named attribute is not defined, the default is + returned. + """ + + +class IInterface(ISpecification, IElement): + """Interface objects + + Interface objects describe the behavior of an object by containing + useful information about the object. This information includes: + + - Prose documentation about the object. In Python terms, this + is called the "doc string" of the interface. In this element, + you describe how the object works in prose language and any + other useful information about the object. + + - Descriptions of attributes. Attribute descriptions include + the name of the attribute and prose documentation describing + the attributes usage. + + - Descriptions of methods. Method descriptions can include: + + - Prose "doc string" documentation about the method and its + usage. + + - A description of the methods arguments; how many arguments + are expected, optional arguments and their default values, + the position or arguments in the signature, whether the + method accepts arbitrary arguments and whether the method + accepts arbitrary keyword arguments. + + - Optional tagged data. Interface objects (and their attributes and + methods) can have optional, application specific tagged data + associated with them. Examples uses for this are examples, + security assertions, pre/post conditions, and other possible + information you may want to associate with an Interface or its + attributes. + + Not all of this information is mandatory. For example, you may + only want the methods of your interface to have prose + documentation and not describe the arguments of the method in + exact detail. Interface objects are flexible and let you give or + take any of these components. + + Interfaces are created with the Python class statement using + either `zope.interface.Interface` or another interface, as in:: + + from zope.interface import Interface + + class IMyInterface(Interface): + '''Interface documentation''' + + def meth(arg1, arg2): + '''Documentation for meth''' + + # Note that there is no self argument + + class IMySubInterface(IMyInterface): + '''Interface documentation''' + + def meth2(): + '''Documentation for meth2''' + + You use interfaces in two ways: + + - You assert that your object implement the interfaces. + + There are several ways that you can declare that an object + provides an interface: + + 1. Call `zope.interface.implementer` on your class definition. + + 2. Call `zope.interface.directlyProvides` on your object. + + 3. Call `zope.interface.classImplements` to declare that instances + of a class implement an interface. + + For example:: + + from zope.interface import classImplements + + classImplements(some_class, some_interface) + + This approach is useful when it is not an option to modify + the class source. Note that this doesn't affect what the + class itself implements, but only what its instances + implement. + + - You query interface meta-data. See the IInterface methods and + attributes for details. + + """ + # pylint:disable=arguments-differ + def names(all=False): # pylint:disable=redefined-builtin + """Get the interface attribute names + + Return a collection of the names of the attributes, including + methods, included in the interface definition. + + Normally, only directly defined attributes are included. If + a true positional or keyword argument is given, then + attributes defined by base classes will be included. + """ + + def namesAndDescriptions(all=False): # pylint:disable=redefined-builtin + """Get the interface attribute names and descriptions + + Return a collection of the names and descriptions of the + attributes, including methods, as name-value pairs, included + in the interface definition. + + Normally, only directly defined attributes are included. If + a true positional or keyword argument is given, then + attributes defined by base classes will be included. + """ + + def __getitem__(name): + """Get the description for a name + + If the named attribute is not defined, a `KeyError` is raised. + """ + + def direct(name): + """Get the description for the name if it was defined by the interface + + If the interface doesn't define the name, returns None. + """ + + def validateInvariants(obj, errors=None): + """Validate invariants + + Validate object to defined invariants. If errors is None, + raises first Invalid error; if errors is a list, appends all errors + to list, then raises Invalid with the errors as the first element + of the "args" tuple.""" + + def __contains__(name): + """Test whether the name is defined by the interface""" + + def __iter__(): + """Return an iterator over the names defined by the interface + + The names iterated include all of the names defined by the + interface directly and indirectly by base interfaces. + """ + + __module__ = Attribute("""The name of the module defining the interface""") + + +class IDeclaration(ISpecification): + """Interface declaration + + Declarations are used to express the interfaces implemented by + classes or provided by objects. + """ + + def __contains__(interface): + """Test whether an interface is in the specification + + Return true if the given interface is one of the interfaces in + the specification and false otherwise. + """ + + def __iter__(): + """Return an iterator for the interfaces in the specification + """ + + def flattened(): + """Return an iterator of all included and extended interfaces + + An iterator is returned for all interfaces either included in + or extended by interfaces included in the specifications + without duplicates. The interfaces are in "interface + resolution order". The interface resolution order is such that + base interfaces are listed after interfaces that extend them + and, otherwise, interfaces are included in the order that they + were defined in the specification. + """ + + def __sub__(interfaces): + """Create an interface specification with some interfaces excluded + + The argument can be an interface or an interface + specifications. The interface or interfaces given in a + specification are subtracted from the interface specification. + + Removing an interface that is not in the specification does + not raise an error. Doing so has no effect. + + Removing an interface also removes sub-interfaces of the interface. + + """ + + def __add__(interfaces): + """Create an interface specification with some interfaces added + + The argument can be an interface or an interface + specifications. The interface or interfaces given in a + specification are added to the interface specification. + + Adding an interface that is already in the specification does + not raise an error. Doing so has no effect. + """ + + def __nonzero__(): + """Return a true value of the interface specification is non-empty + """ + +class IInterfaceDeclaration(Interface): + """ + Declare and check the interfaces of objects. + + The functions defined in this interface are used to declare the + interfaces that objects provide and to query the interfaces that + have been declared. + + Interfaces can be declared for objects in two ways: + + - Interfaces are declared for instances of the object's class + + - Interfaces are declared for the object directly. + + The interfaces declared for an object are, therefore, the union of + interfaces declared for the object directly and the interfaces + declared for instances of the object's class. + + Note that we say that a class implements the interfaces provided + by it's instances. An instance can also provide interfaces + directly. The interfaces provided by an object are the union of + the interfaces provided directly and the interfaces implemented by + the class. + + This interface is implemented by :mod:`zope.interface`. + """ + # pylint:disable=arguments-differ + ### + # Defining interfaces + ### + + Interface = Attribute("The base class used to create new interfaces") + + def taggedValue(key, value): + """ + Attach a tagged value to an interface while defining the interface. + + This is a way of executing :meth:`IElement.setTaggedValue` from + the definition of the interface. For example:: + + class IFoo(Interface): + taggedValue('key', 'value') + + .. seealso:: `zope.interface.taggedValue` + """ + + def invariant(checker_function): + """ + Attach an invariant checker function to an interface while defining it. + + Invariants can later be validated against particular implementations by + calling :meth:`IInterface.validateInvariants`. + + For example:: + + def check_range(ob): + if ob.max < ob.min: + raise ValueError("max value is less than min value") + + class IRange(Interface): + min = Attribute("The min value") + max = Attribute("The max value") + + invariant(check_range) + + .. seealso:: `zope.interface.invariant` + """ + + def interfacemethod(method): + """ + A decorator that transforms a method specification into an + implementation method. + + This is used to override methods of ``Interface`` or provide new methods. + Definitions using this decorator will not appear in :meth:`IInterface.names()`. + It is possible to have an implementation method and a method specification + of the same name. + + For example:: + + class IRange(Interface): + @interfacemethod + def __adapt__(self, obj): + if isinstance(obj, range): + # Return the builtin ``range`` as-is + return obj + return super(type(IRange), self).__adapt__(obj) + + You can use ``super`` to call the parent class functionality. Note that + the zero-argument version (``super().__adapt__``) works on Python 3.6 and above, but + prior to that the two-argument version must be used, and the class must be explicitly + passed as the first argument. + + .. versionadded:: 5.1.0 + .. seealso:: `zope.interface.interfacemethod` + """ + + ### + # Querying interfaces + ### + + def providedBy(ob): + """ + Return the interfaces provided by an object. + + This is the union of the interfaces directly provided by an + object and interfaces implemented by it's class. + + The value returned is an `IDeclaration`. + + .. seealso:: `zope.interface.providedBy` + """ + + def implementedBy(class_): + """ + Return the interfaces implemented for a class's instances. + + The value returned is an `IDeclaration`. + + .. seealso:: `zope.interface.implementedBy` + """ + + ### + # Declaring interfaces + ### + + def classImplements(class_, *interfaces): + """ + Declare additional interfaces implemented for instances of a class. + + The arguments after the class are one or more interfaces or + interface specifications (`IDeclaration` objects). + + The interfaces given (including the interfaces in the + specifications) are added to any interfaces previously + declared. + + Consider the following example:: + + class C(A, B): + ... + + classImplements(C, I1, I2) + + + Instances of ``C`` provide ``I1``, ``I2``, and whatever interfaces + instances of ``A`` and ``B`` provide. This is equivalent to:: + + @implementer(I1, I2) + class C(A, B): + pass + + .. seealso:: `zope.interface.classImplements` + .. seealso:: `zope.interface.implementer` + """ + + def classImplementsFirst(cls, interface): + """ + See :func:`zope.interface.classImplementsFirst`. + """ + + def implementer(*interfaces): + """ + Create a decorator for declaring interfaces implemented by a + factory. + + A callable is returned that makes an implements declaration on + objects passed to it. + + .. seealso:: :meth:`classImplements` + """ + + def classImplementsOnly(class_, *interfaces): + """ + Declare the only interfaces implemented by instances of a class. + + The arguments after the class are one or more interfaces or + interface specifications (`IDeclaration` objects). + + The interfaces given (including the interfaces in the + specifications) replace any previous declarations. + + Consider the following example:: + + class C(A, B): + ... + + classImplements(C, IA, IB. IC) + classImplementsOnly(C. I1, I2) + + Instances of ``C`` provide only ``I1``, ``I2``, and regardless of + whatever interfaces instances of ``A`` and ``B`` implement. + + .. seealso:: `zope.interface.classImplementsOnly` + """ + + def implementer_only(*interfaces): + """ + Create a decorator for declaring the only interfaces implemented. + + A callable is returned that makes an implements declaration on + objects passed to it. + + .. seealso:: `zope.interface.implementer_only` + """ + + def directlyProvidedBy(object): # pylint:disable=redefined-builtin + """ + Return the interfaces directly provided by the given object. + + The value returned is an `IDeclaration`. + + .. seealso:: `zope.interface.directlyProvidedBy` + """ + + def directlyProvides(object, *interfaces): # pylint:disable=redefined-builtin + """ + Declare interfaces declared directly for an object. + + The arguments after the object are one or more interfaces or + interface specifications (`IDeclaration` objects). + + .. caution:: + The interfaces given (including the interfaces in the + specifications) *replace* interfaces previously + declared for the object. See :meth:`alsoProvides` to add + additional interfaces. + + Consider the following example:: + + class C(A, B): + ... + + ob = C() + directlyProvides(ob, I1, I2) + + The object, ``ob`` provides ``I1``, ``I2``, and whatever interfaces + instances have been declared for instances of ``C``. + + To remove directly provided interfaces, use `directlyProvidedBy` and + subtract the unwanted interfaces. For example:: + + directlyProvides(ob, directlyProvidedBy(ob)-I2) + + removes I2 from the interfaces directly provided by + ``ob``. The object, ``ob`` no longer directly provides ``I2``, + although it might still provide ``I2`` if it's class + implements ``I2``. + + To add directly provided interfaces, use `directlyProvidedBy` and + include additional interfaces. For example:: + + directlyProvides(ob, directlyProvidedBy(ob), I2) + + adds I2 to the interfaces directly provided by ob. + + .. seealso:: `zope.interface.directlyProvides` + """ + + def alsoProvides(object, *interfaces): # pylint:disable=redefined-builtin + """ + Declare additional interfaces directly for an object. + + For example:: + + alsoProvides(ob, I1) + + is equivalent to:: + + directlyProvides(ob, directlyProvidedBy(ob), I1) + + .. seealso:: `zope.interface.alsoProvides` + """ + + def noLongerProvides(object, interface): # pylint:disable=redefined-builtin + """ + Remove an interface from the list of an object's directly provided + interfaces. + + For example:: + + noLongerProvides(ob, I1) + + is equivalent to:: + + directlyProvides(ob, directlyProvidedBy(ob) - I1) + + with the exception that if ``I1`` is an interface that is + provided by ``ob`` through the class's implementation, + `ValueError` is raised. + + .. seealso:: `zope.interface.noLongerProvides` + """ + + def provider(*interfaces): + """ + Declare interfaces provided directly by a class. + + .. seealso:: `zope.interface.provider` + """ + + def moduleProvides(*interfaces): + """ + Declare interfaces provided by a module. + + This function is used in a module definition. + + The arguments are one or more interfaces or interface + specifications (`IDeclaration` objects). + + The given interfaces (including the interfaces in the + specifications) are used to create the module's direct-object + interface specification. An error will be raised if the module + already has an interface specification. In other words, it is + an error to call this function more than once in a module + definition. + + This function is provided for convenience. It provides a more + convenient way to call `directlyProvides` for a module. For example:: + + moduleImplements(I1) + + is equivalent to:: + + directlyProvides(sys.modules[__name__], I1) + + .. seealso:: `zope.interface.moduleProvides` + """ + + def Declaration(*interfaces): + """ + Create an interface specification. + + The arguments are one or more interfaces or interface + specifications (`IDeclaration` objects). + + A new interface specification (`IDeclaration`) with the given + interfaces is returned. + + .. seealso:: `zope.interface.Declaration` + """ + +class IAdapterRegistry(Interface): + """Provide an interface-based registry for adapters + + This registry registers objects that are in some sense "from" a + sequence of specification to an interface and a name. + + No specific semantics are assumed for the registered objects, + however, the most common application will be to register factories + that adapt objects providing required specifications to a provided + interface. + """ + + def register(required, provided, name, value): + """Register a value + + A value is registered for a *sequence* of required specifications, a + provided interface, and a name, which must be text. + """ + + def registered(required, provided, name=''): + """Return the component registered for the given interfaces and name + + name must be text. + + Unlike the lookup method, this methods won't retrieve + components registered for more specific required interfaces or + less specific provided interfaces. + + If no component was registered exactly for the given + interfaces and name, then None is returned. + + """ + + def lookup(required, provided, name='', default=None): + """Lookup a value + + A value is looked up based on a *sequence* of required + specifications, a provided interface, and a name, which must be + text. + """ + + def queryMultiAdapter(objects, provided, name='', default=None): + """Adapt a sequence of objects to a named, provided, interface + """ + + def lookup1(required, provided, name='', default=None): + """Lookup a value using a single required interface + + A value is looked up based on a single required + specifications, a provided interface, and a name, which must be + text. + """ + + def queryAdapter(object, provided, name='', default=None): # pylint:disable=redefined-builtin + """Adapt an object using a registered adapter factory. + """ + + def adapter_hook(provided, object, name='', default=None): # pylint:disable=redefined-builtin + """Adapt an object using a registered adapter factory. + + name must be text. + """ + + def lookupAll(required, provided): + """Find all adapters from the required to the provided interfaces + + An iterable object is returned that provides name-value two-tuples. + """ + + def names(required, provided): # pylint:disable=arguments-differ + """Return the names for which there are registered objects + """ + + def subscribe(required, provided, subscriber): # pylint:disable=arguments-differ + """Register a subscriber + + A subscriber is registered for a *sequence* of required + specifications, a provided interface, and a name. + + Multiple subscribers may be registered for the same (or + equivalent) interfaces. + + .. versionchanged:: 5.1.1 + Correct the method signature to remove the ``name`` parameter. + Subscribers have no names. + """ + + def subscribed(required, provided, subscriber): + """ + Check whether the object *subscriber* is registered directly + with this object via a previous call to + ``subscribe(required, provided, subscriber)``. + + If the *subscriber*, or one equal to it, has been subscribed, + for the given *required* sequence and *provided* interface, + return that object. (This does not guarantee whether the *subscriber* + itself is returned, or an object equal to it.) + + If it has not, return ``None``. + + Unlike :meth:`subscriptions`, this method won't retrieve + components registered for more specific required interfaces or + less specific provided interfaces. + + .. versionadded:: 5.3.0 + """ + + def subscriptions(required, provided): + """ + Get a sequence of subscribers. + + Subscribers for a sequence of *required* interfaces, and a *provided* + interface are returned. This takes into account subscribers + registered with this object, as well as those registered with + base adapter registries in the resolution order, and interfaces that + extend *provided*. + + .. versionchanged:: 5.1.1 + Correct the method signature to remove the ``name`` parameter. + Subscribers have no names. + """ + + def subscribers(objects, provided): + """ + Get a sequence of subscription **adapters**. + + This is like :meth:`subscriptions`, but calls the returned + subscribers with *objects* (and optionally returns the results + of those calls), instead of returning the subscribers directly. + + :param objects: A sequence of objects; they will be used to + determine the *required* argument to :meth:`subscriptions`. + :param provided: A single interface, or ``None``, to pass + as the *provided* parameter to :meth:`subscriptions`. + If an interface is given, the results of calling each returned + subscriber with the the *objects* are collected and returned + from this method; each result should be an object implementing + the *provided* interface. If ``None``, the resulting subscribers + are still called, but the results are ignored. + :return: A sequence of the results of calling the subscribers + if *provided* is not ``None``. If there are no registered + subscribers, or *provided* is ``None``, this will be an empty + sequence. + + .. versionchanged:: 5.1.1 + Correct the method signature to remove the ``name`` parameter. + Subscribers have no names. + """ + +# begin formerly in zope.component + +class ComponentLookupError(LookupError): + """A component could not be found.""" + +class Invalid(Exception): + """A component doesn't satisfy a promise.""" + +class IObjectEvent(Interface): + """An event related to an object. + + The object that generated this event is not necessarily the object + referred to by location. + """ + + object = Attribute("The subject of the event.") + + +@implementer(IObjectEvent) +class ObjectEvent: + + def __init__(self, object): # pylint:disable=redefined-builtin + self.object = object + + +class IComponentLookup(Interface): + """Component Manager for a Site + + This object manages the components registered at a particular site. The + definition of a site is intentionally vague. + """ + + adapters = Attribute( + "Adapter Registry to manage all registered adapters.") + + utilities = Attribute( + "Adapter Registry to manage all registered utilities.") + + def queryAdapter(object, interface, name='', default=None): # pylint:disable=redefined-builtin + """Look for a named adapter to an interface for an object + + If a matching adapter cannot be found, returns the default. + """ + + def getAdapter(object, interface, name=''): # pylint:disable=redefined-builtin + """Look for a named adapter to an interface for an object + + If a matching adapter cannot be found, a `ComponentLookupError` + is raised. + """ + + def queryMultiAdapter(objects, interface, name='', default=None): + """Look for a multi-adapter to an interface for multiple objects + + If a matching adapter cannot be found, returns the default. + """ + + def getMultiAdapter(objects, interface, name=''): + """Look for a multi-adapter to an interface for multiple objects + + If a matching adapter cannot be found, a `ComponentLookupError` + is raised. + """ + + def getAdapters(objects, provided): + """Look for all matching adapters to a provided interface for objects + + Return an iterable of name-adapter pairs for adapters that + provide the given interface. + """ + + def subscribers(objects, provided): + """Get subscribers + + Subscribers are returned that provide the provided interface + and that depend on and are computed from the sequence of + required objects. + """ + + def handle(*objects): + """Call handlers for the given objects + + Handlers registered for the given objects are called. + """ + + def queryUtility(interface, name='', default=None): + """Look up a utility that provides an interface. + + If one is not found, returns default. + """ + + def getUtilitiesFor(interface): + """Look up the registered utilities that provide an interface. + + Returns an iterable of name-utility pairs. + """ + + def getAllUtilitiesRegisteredFor(interface): + """Return all registered utilities for an interface + + This includes overridden utilities. + + An iterable of utility instances is returned. No names are + returned. + """ + +class IRegistration(Interface): + """A registration-information object + """ + + registry = Attribute("The registry having the registration") + + name = Attribute("The registration name") + + info = Attribute("""Information about the registration + + This is information deemed useful to people browsing the + configuration of a system. It could, for example, include + commentary or information about the source of the configuration. + """) + +class IUtilityRegistration(IRegistration): + """Information about the registration of a utility + """ + + factory = Attribute("The factory used to create the utility. Optional.") + component = Attribute("The object registered") + provided = Attribute("The interface provided by the component") + +class _IBaseAdapterRegistration(IRegistration): + """Information about the registration of an adapter + """ + + factory = Attribute("The factory used to create adapters") + + required = Attribute("""The adapted interfaces + + This is a sequence of interfaces adapters by the registered + factory. The factory will be caled with a sequence of objects, as + positional arguments, that provide these interfaces. + """) + + provided = Attribute("""The interface provided by the adapters. + + This interface is implemented by the factory + """) + +class IAdapterRegistration(_IBaseAdapterRegistration): + """Information about the registration of an adapter + """ + +class ISubscriptionAdapterRegistration(_IBaseAdapterRegistration): + """Information about the registration of a subscription adapter + """ + +class IHandlerRegistration(IRegistration): + + handler = Attribute("An object called used to handle an event") + + required = Attribute("""The handled interfaces + + This is a sequence of interfaces handled by the registered + handler. The handler will be caled with a sequence of objects, as + positional arguments, that provide these interfaces. + """) + +class IRegistrationEvent(IObjectEvent): + """An event that involves a registration""" + + +@implementer(IRegistrationEvent) +class RegistrationEvent(ObjectEvent): + """There has been a change in a registration + """ + def __repr__(self): + return "{} event:\n{!r}".format(self.__class__.__name__, self.object) + +class IRegistered(IRegistrationEvent): + """A component or factory was registered + """ + +@implementer(IRegistered) +class Registered(RegistrationEvent): + pass + +class IUnregistered(IRegistrationEvent): + """A component or factory was unregistered + """ + +@implementer(IUnregistered) +class Unregistered(RegistrationEvent): + """A component or factory was unregistered + """ + + +class IComponentRegistry(Interface): + """Register components + """ + + def registerUtility(component=None, provided=None, name='', + info='', factory=None): + """Register a utility + + :param factory: + Factory for the component to be registered. + + :param component: + The registered component + + :param provided: + This is the interface provided by the utility. If the + component provides a single interface, then this + argument is optional and the component-implemented + interface will be used. + + :param name: + The utility name. + + :param info: + An object that can be converted to a string to provide + information about the registration. + + Only one of *component* and *factory* can be used. + + A `IRegistered` event is generated with an `IUtilityRegistration`. + """ + + def unregisterUtility(component=None, provided=None, name='', + factory=None): + """Unregister a utility + + :returns: + A boolean is returned indicating whether the registry was + changed. If the given *component* is None and there is no + component registered, or if the given *component* is not + None and is not registered, then the function returns + False, otherwise it returns True. + + :param factory: + Factory for the component to be unregistered. + + :param component: + The registered component The given component can be + None, in which case any component registered to provide + the given provided interface with the given name is + unregistered. + + :param provided: + This is the interface provided by the utility. If the + component is not None and provides a single interface, + then this argument is optional and the + component-implemented interface will be used. + + :param name: + The utility name. + + Only one of *component* and *factory* can be used. + An `IUnregistered` event is generated with an `IUtilityRegistration`. + """ + + def registeredUtilities(): + """Return an iterable of `IUtilityRegistration` instances. + + These registrations describe the current utility registrations + in the object. + """ + + def registerAdapter(factory, required=None, provided=None, name='', + info=''): + """Register an adapter factory + + :param factory: + The object used to compute the adapter + + :param required: + This is a sequence of specifications for objects to be + adapted. If omitted, then the value of the factory's + ``__component_adapts__`` attribute will be used. The + ``__component_adapts__`` attribute is + normally set in class definitions using + the `.adapter` + decorator. If the factory doesn't have a + ``__component_adapts__`` adapts attribute, then this + argument is required. + + :param provided: + This is the interface provided by the adapter and + implemented by the factory. If the factory + implements a single interface, then this argument is + optional and the factory-implemented interface will be + used. + + :param name: + The adapter name. + + :param info: + An object that can be converted to a string to provide + information about the registration. + + A `IRegistered` event is generated with an `IAdapterRegistration`. + """ + + def unregisterAdapter(factory=None, required=None, + provided=None, name=''): + """Unregister an adapter factory + + :returns: + A boolean is returned indicating whether the registry was + changed. If the given component is None and there is no + component registered, or if the given component is not + None and is not registered, then the function returns + False, otherwise it returns True. + + :param factory: + This is the object used to compute the adapter. The + factory can be None, in which case any factory + registered to implement the given provided interface + for the given required specifications with the given + name is unregistered. + + :param required: + This is a sequence of specifications for objects to be + adapted. If the factory is not None and the required + arguments is omitted, then the value of the factory's + __component_adapts__ attribute will be used. The + __component_adapts__ attribute attribute is normally + set in class definitions using adapts function, or for + callables using the adapter decorator. If the factory + is None or doesn't have a __component_adapts__ adapts + attribute, then this argument is required. + + :param provided: + This is the interface provided by the adapter and + implemented by the factory. If the factory is not + None and implements a single interface, then this + argument is optional and the factory-implemented + interface will be used. + + :param name: + The adapter name. + + An `IUnregistered` event is generated with an `IAdapterRegistration`. + """ + + def registeredAdapters(): + """Return an iterable of `IAdapterRegistration` instances. + + These registrations describe the current adapter registrations + in the object. + """ + + def registerSubscriptionAdapter(factory, required=None, provides=None, + name='', info=''): + """Register a subscriber factory + + :param factory: + The object used to compute the adapter + + :param required: + This is a sequence of specifications for objects to be + adapted. If omitted, then the value of the factory's + ``__component_adapts__`` attribute will be used. The + ``__component_adapts__`` attribute is + normally set using the adapter + decorator. If the factory doesn't have a + ``__component_adapts__`` adapts attribute, then this + argument is required. + + :param provided: + This is the interface provided by the adapter and + implemented by the factory. If the factory implements + a single interface, then this argument is optional and + the factory-implemented interface will be used. + + :param name: + The adapter name. + + Currently, only the empty string is accepted. Other + strings will be accepted in the future when support for + named subscribers is added. + + :param info: + An object that can be converted to a string to provide + information about the registration. + + A `IRegistered` event is generated with an + `ISubscriptionAdapterRegistration`. + """ + + def unregisterSubscriptionAdapter(factory=None, required=None, + provides=None, name=''): + """Unregister a subscriber factory. + + :returns: + A boolean is returned indicating whether the registry was + changed. If the given component is None and there is no + component registered, or if the given component is not + None and is not registered, then the function returns + False, otherwise it returns True. + + :param factory: + This is the object used to compute the adapter. The + factory can be None, in which case any factories + registered to implement the given provided interface + for the given required specifications with the given + name are unregistered. + + :param required: + This is a sequence of specifications for objects to be + adapted. If omitted, then the value of the factory's + ``__component_adapts__`` attribute will be used. The + ``__component_adapts__`` attribute is + normally set using the adapter + decorator. If the factory doesn't have a + ``__component_adapts__`` adapts attribute, then this + argument is required. + + :param provided: + This is the interface provided by the adapter and + implemented by the factory. If the factory is not + None implements a single interface, then this argument + is optional and the factory-implemented interface will + be used. + + :param name: + The adapter name. + + Currently, only the empty string is accepted. Other + strings will be accepted in the future when support for + named subscribers is added. + + An `IUnregistered` event is generated with an + `ISubscriptionAdapterRegistration`. + """ + + def registeredSubscriptionAdapters(): + """Return an iterable of `ISubscriptionAdapterRegistration` instances. + + These registrations describe the current subscription adapter + registrations in the object. + """ + + def registerHandler(handler, required=None, name='', info=''): + """Register a handler. + + A handler is a subscriber that doesn't compute an adapter + but performs some function when called. + + :param handler: + The object used to handle some event represented by + the objects passed to it. + + :param required: + This is a sequence of specifications for objects to be + adapted. If omitted, then the value of the factory's + ``__component_adapts__`` attribute will be used. The + ``__component_adapts__`` attribute is + normally set using the adapter + decorator. If the factory doesn't have a + ``__component_adapts__`` adapts attribute, then this + argument is required. + + :param name: + The handler name. + + Currently, only the empty string is accepted. Other + strings will be accepted in the future when support for + named handlers is added. + + :param info: + An object that can be converted to a string to provide + information about the registration. + + + A `IRegistered` event is generated with an `IHandlerRegistration`. + """ + + def unregisterHandler(handler=None, required=None, name=''): + """Unregister a handler. + + A handler is a subscriber that doesn't compute an adapter + but performs some function when called. + + :returns: A boolean is returned indicating whether the registry was + changed. + + :param handler: + This is the object used to handle some event + represented by the objects passed to it. The handler + can be None, in which case any handlers registered for + the given required specifications with the given are + unregistered. + + :param required: + This is a sequence of specifications for objects to be + adapted. If omitted, then the value of the factory's + ``__component_adapts__`` attribute will be used. The + ``__component_adapts__`` attribute is + normally set using the adapter + decorator. If the factory doesn't have a + ``__component_adapts__`` adapts attribute, then this + argument is required. + + :param name: + The handler name. + + Currently, only the empty string is accepted. Other + strings will be accepted in the future when support for + named handlers is added. + + An `IUnregistered` event is generated with an `IHandlerRegistration`. + """ + + def registeredHandlers(): + """Return an iterable of `IHandlerRegistration` instances. + + These registrations describe the current handler registrations + in the object. + """ + + +class IComponents(IComponentLookup, IComponentRegistry): + """Component registration and access + """ + + +# end formerly in zope.component diff --git a/contrib/python/zope.interface/py3/zope/interface/registry.py b/contrib/python/zope.interface/py3/zope/interface/registry.py new file mode 100644 index 0000000000..292499dbec --- /dev/null +++ b/contrib/python/zope.interface/py3/zope/interface/registry.py @@ -0,0 +1,723 @@ +############################################################################## +# +# Copyright (c) 2006 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. +# +############################################################################## +"""Basic components support +""" +from collections import defaultdict + +try: + from zope.event import notify +except ImportError: # pragma: no cover + def notify(*arg, **kw): pass + +from zope.interface.interfaces import ISpecification +from zope.interface.interfaces import ComponentLookupError +from zope.interface.interfaces import IAdapterRegistration +from zope.interface.interfaces import IComponents +from zope.interface.interfaces import IHandlerRegistration +from zope.interface.interfaces import ISubscriptionAdapterRegistration +from zope.interface.interfaces import IUtilityRegistration +from zope.interface.interfaces import Registered +from zope.interface.interfaces import Unregistered + +from zope.interface.interface import Interface +from zope.interface.declarations import implementedBy +from zope.interface.declarations import implementer +from zope.interface.declarations import implementer_only +from zope.interface.declarations import providedBy +from zope.interface.adapter import AdapterRegistry + +__all__ = [ + # Components is public API, but + # the *Registration classes are just implementations + # of public interfaces. + 'Components', +] + +class _UnhashableComponentCounter: + # defaultdict(int)-like object for unhashable components + + def __init__(self, otherdict): + # [(component, count)] + self._data = [item for item in otherdict.items()] + + def __getitem__(self, key): + for component, count in self._data: + if component == key: + return count + return 0 + + def __setitem__(self, component, count): + for i, data in enumerate(self._data): + if data[0] == component: + self._data[i] = component, count + return + self._data.append((component, count)) + + def __delitem__(self, component): + for i, data in enumerate(self._data): + if data[0] == component: + del self._data[i] + return + raise KeyError(component) # pragma: no cover + +def _defaultdict_int(): + return defaultdict(int) + +class _UtilityRegistrations: + + def __init__(self, utilities, utility_registrations): + # {provided -> {component: count}} + self._cache = defaultdict(_defaultdict_int) + self._utilities = utilities + self._utility_registrations = utility_registrations + + self.__populate_cache() + + def __populate_cache(self): + for ((p, _), data) in iter(self._utility_registrations.items()): + component = data[0] + self.__cache_utility(p, component) + + def __cache_utility(self, provided, component): + try: + self._cache[provided][component] += 1 + except TypeError: + # The component is not hashable, and we have a dict. Switch to a strategy + # that doesn't use hashing. + prov = self._cache[provided] = _UnhashableComponentCounter(self._cache[provided]) + prov[component] += 1 + + def __uncache_utility(self, provided, component): + provided = self._cache[provided] + # It seems like this line could raise a TypeError if component isn't + # hashable and we haven't yet switched to _UnhashableComponentCounter. However, + # we can't actually get in that situation. In order to get here, we would + # have had to cache the utility already which would have switched + # the datastructure if needed. + count = provided[component] + count -= 1 + if count == 0: + del provided[component] + else: + provided[component] = count + return count > 0 + + def _is_utility_subscribed(self, provided, component): + try: + return self._cache[provided][component] > 0 + except TypeError: + # Not hashable and we're still using a dict + return False + + def registerUtility(self, provided, name, component, info, factory): + subscribed = self._is_utility_subscribed(provided, component) + + self._utility_registrations[(provided, name)] = component, info, factory + self._utilities.register((), provided, name, component) + + if not subscribed: + self._utilities.subscribe((), provided, component) + + self.__cache_utility(provided, component) + + def unregisterUtility(self, provided, name, component): + del self._utility_registrations[(provided, name)] + self._utilities.unregister((), provided, name) + + subscribed = self.__uncache_utility(provided, component) + + if not subscribed: + self._utilities.unsubscribe((), provided, component) + + +@implementer(IComponents) +class Components: + + _v_utility_registrations_cache = None + + def __init__(self, name='', bases=()): + # __init__ is used for test cleanup as well as initialization. + # XXX add a separate API for test cleanup. + assert isinstance(name, str) + self.__name__ = name + self._init_registries() + self._init_registrations() + self.__bases__ = tuple(bases) + self._v_utility_registrations_cache = None + + def __repr__(self): + return "<{} {}>".format(self.__class__.__name__, self.__name__) + + def __reduce__(self): + # Mimic what a persistent.Persistent object does and elide + # _v_ attributes so that they don't get saved in ZODB. + # This allows us to store things that cannot be pickled in such + # attributes. + reduction = super().__reduce__() + # (callable, args, state, listiter, dictiter) + # We assume the state is always a dict; the last three items + # are technically optional and can be missing or None. + filtered_state = {k: v for k, v in reduction[2].items() + if not k.startswith('_v_')} + reduction = list(reduction) + reduction[2] = filtered_state + return tuple(reduction) + + def _init_registries(self): + # Subclasses have never been required to call this method + # if they override it, merely to fill in these two attributes. + self.adapters = AdapterRegistry() + self.utilities = AdapterRegistry() + + def _init_registrations(self): + self._utility_registrations = {} + self._adapter_registrations = {} + self._subscription_registrations = [] + self._handler_registrations = [] + + @property + def _utility_registrations_cache(self): + # We use a _v_ attribute internally so that data aren't saved in ZODB, + # because this object cannot be pickled. + cache = self._v_utility_registrations_cache + if (cache is None + or cache._utilities is not self.utilities + or cache._utility_registrations is not self._utility_registrations): + cache = self._v_utility_registrations_cache = _UtilityRegistrations( + self.utilities, + self._utility_registrations) + return cache + + def _getBases(self): + # Subclasses might override + return self.__dict__.get('__bases__', ()) + + def _setBases(self, bases): + # Subclasses might override + self.adapters.__bases__ = tuple([ + base.adapters for base in bases]) + self.utilities.__bases__ = tuple([ + base.utilities for base in bases]) + self.__dict__['__bases__'] = tuple(bases) + + __bases__ = property( + lambda self: self._getBases(), + lambda self, bases: self._setBases(bases), + ) + + def registerUtility(self, component=None, provided=None, name='', + info='', event=True, factory=None): + if factory: + if component: + raise TypeError("Can't specify factory and component.") + component = factory() + + if provided is None: + provided = _getUtilityProvided(component) + + if name == '': + name = _getName(component) + + reg = self._utility_registrations.get((provided, name)) + if reg is not None: + if reg[:2] == (component, info): + # already registered + return + self.unregisterUtility(reg[0], provided, name) + + self._utility_registrations_cache.registerUtility( + provided, name, component, info, factory) + + if event: + notify(Registered( + UtilityRegistration(self, provided, name, component, info, + factory) + )) + + def unregisterUtility(self, component=None, provided=None, name='', + factory=None): + if factory: + if component: + raise TypeError("Can't specify factory and component.") + component = factory() + + if provided is None: + if component is None: + raise TypeError("Must specify one of component, factory and " + "provided") + provided = _getUtilityProvided(component) + + old = self._utility_registrations.get((provided, name)) + if (old is None) or ((component is not None) and + (component != old[0])): + return False + + if component is None: + component = old[0] + + # Note that component is now the old thing registered + self._utility_registrations_cache.unregisterUtility( + provided, name, component) + + notify(Unregistered( + UtilityRegistration(self, provided, name, component, *old[1:]) + )) + + return True + + def registeredUtilities(self): + for ((provided, name), data + ) in iter(self._utility_registrations.items()): + yield UtilityRegistration(self, provided, name, *data) + + def queryUtility(self, provided, name='', default=None): + return self.utilities.lookup((), provided, name, default) + + def getUtility(self, provided, name=''): + utility = self.utilities.lookup((), provided, name) + if utility is None: + raise ComponentLookupError(provided, name) + return utility + + def getUtilitiesFor(self, interface): + yield from self.utilities.lookupAll((), interface) + + def getAllUtilitiesRegisteredFor(self, interface): + return self.utilities.subscriptions((), interface) + + def registerAdapter(self, factory, required=None, provided=None, + name='', info='', event=True): + if provided is None: + provided = _getAdapterProvided(factory) + required = _getAdapterRequired(factory, required) + if name == '': + name = _getName(factory) + self._adapter_registrations[(required, provided, name) + ] = factory, info + self.adapters.register(required, provided, name, factory) + + if event: + notify(Registered( + AdapterRegistration(self, required, provided, name, + factory, info) + )) + + + def unregisterAdapter(self, factory=None, + required=None, provided=None, name='', + ): + if provided is None: + if factory is None: + raise TypeError("Must specify one of factory and provided") + provided = _getAdapterProvided(factory) + + if (required is None) and (factory is None): + raise TypeError("Must specify one of factory and required") + + required = _getAdapterRequired(factory, required) + old = self._adapter_registrations.get((required, provided, name)) + if (old is None) or ((factory is not None) and + (factory != old[0])): + return False + + del self._adapter_registrations[(required, provided, name)] + self.adapters.unregister(required, provided, name) + + notify(Unregistered( + AdapterRegistration(self, required, provided, name, + *old) + )) + + return True + + def registeredAdapters(self): + for ((required, provided, name), (component, info) + ) in iter(self._adapter_registrations.items()): + yield AdapterRegistration(self, required, provided, name, + component, info) + + def queryAdapter(self, object, interface, name='', default=None): + return self.adapters.queryAdapter(object, interface, name, default) + + def getAdapter(self, object, interface, name=''): + adapter = self.adapters.queryAdapter(object, interface, name) + if adapter is None: + raise ComponentLookupError(object, interface, name) + return adapter + + def queryMultiAdapter(self, objects, interface, name='', + default=None): + return self.adapters.queryMultiAdapter( + objects, interface, name, default) + + def getMultiAdapter(self, objects, interface, name=''): + adapter = self.adapters.queryMultiAdapter(objects, interface, name) + if adapter is None: + raise ComponentLookupError(objects, interface, name) + return adapter + + def getAdapters(self, objects, provided): + for name, factory in self.adapters.lookupAll( + list(map(providedBy, objects)), + provided): + adapter = factory(*objects) + if adapter is not None: + yield name, adapter + + def registerSubscriptionAdapter(self, + factory, required=None, provided=None, + name='', info='', + event=True): + if name: + raise TypeError("Named subscribers are not yet supported") + if provided is None: + provided = _getAdapterProvided(factory) + required = _getAdapterRequired(factory, required) + self._subscription_registrations.append( + (required, provided, name, factory, info) + ) + self.adapters.subscribe(required, provided, factory) + + if event: + notify(Registered( + SubscriptionRegistration(self, required, provided, name, + factory, info) + )) + + def registeredSubscriptionAdapters(self): + for data in self._subscription_registrations: + yield SubscriptionRegistration(self, *data) + + def unregisterSubscriptionAdapter(self, factory=None, + required=None, provided=None, name='', + ): + if name: + raise TypeError("Named subscribers are not yet supported") + if provided is None: + if factory is None: + raise TypeError("Must specify one of factory and provided") + provided = _getAdapterProvided(factory) + + if (required is None) and (factory is None): + raise TypeError("Must specify one of factory and required") + + required = _getAdapterRequired(factory, required) + + if factory is None: + new = [(r, p, n, f, i) + for (r, p, n, f, i) + in self._subscription_registrations + if not (r == required and p == provided) + ] + else: + new = [(r, p, n, f, i) + for (r, p, n, f, i) + in self._subscription_registrations + if not (r == required and p == provided and f == factory) + ] + + if len(new) == len(self._subscription_registrations): + return False + + + self._subscription_registrations[:] = new + self.adapters.unsubscribe(required, provided, factory) + + notify(Unregistered( + SubscriptionRegistration(self, required, provided, name, + factory, '') + )) + + return True + + def subscribers(self, objects, provided): + return self.adapters.subscribers(objects, provided) + + def registerHandler(self, + factory, required=None, + name='', info='', + event=True): + if name: + raise TypeError("Named handlers are not yet supported") + required = _getAdapterRequired(factory, required) + self._handler_registrations.append( + (required, name, factory, info) + ) + self.adapters.subscribe(required, None, factory) + + if event: + notify(Registered( + HandlerRegistration(self, required, name, factory, info) + )) + + def registeredHandlers(self): + for data in self._handler_registrations: + yield HandlerRegistration(self, *data) + + def unregisterHandler(self, factory=None, required=None, name=''): + if name: + raise TypeError("Named subscribers are not yet supported") + + if (required is None) and (factory is None): + raise TypeError("Must specify one of factory and required") + + required = _getAdapterRequired(factory, required) + + if factory is None: + new = [(r, n, f, i) + for (r, n, f, i) + in self._handler_registrations + if r != required + ] + else: + new = [(r, n, f, i) + for (r, n, f, i) + in self._handler_registrations + if not (r == required and f == factory) + ] + + if len(new) == len(self._handler_registrations): + return False + + self._handler_registrations[:] = new + self.adapters.unsubscribe(required, None, factory) + + notify(Unregistered( + HandlerRegistration(self, required, name, factory, '') + )) + + return True + + def handle(self, *objects): + self.adapters.subscribers(objects, None) + + def rebuildUtilityRegistryFromLocalCache(self, rebuild=False): + """ + Emergency maintenance method to rebuild the ``.utilities`` + registry from the local copy maintained in this object, or + detect the need to do so. + + Most users will never need to call this, but it can be helpful + in the event of suspected corruption. + + By default, this method only checks for corruption. To make it + actually rebuild the registry, pass `True` for *rebuild*. + + :param bool rebuild: If set to `True` (not the default), + this method will actually register and subscribe utilities + in the registry as needed to synchronize with the local cache. + + :return: A dictionary that's meant as diagnostic data. The keys + and values may change over time. When called with a false *rebuild*, + the keys ``"needed_registered"`` and ``"needed_subscribed"`` will be + non-zero if any corruption was detected, but that will not be corrected. + + .. versionadded:: 5.3.0 + """ + regs = dict(self._utility_registrations) + utils = self.utilities + needed_registered = 0 + did_not_register = 0 + needed_subscribed = 0 + did_not_subscribe = 0 + + + # Avoid the expensive change process during this; we'll call + # it once at the end if needed. + assert 'changed' not in utils.__dict__ + utils.changed = lambda _: None + + if rebuild: + register = utils.register + subscribe = utils.subscribe + else: + register = subscribe = lambda *args: None + + try: + for (provided, name), (value, _info, _factory) in regs.items(): + if utils.registered((), provided, name) != value: + register((), provided, name, value) + needed_registered += 1 + else: + did_not_register += 1 + + if utils.subscribed((), provided, value) is None: + needed_subscribed += 1 + subscribe((), provided, value) + else: + did_not_subscribe += 1 + finally: + del utils.changed + if rebuild and (needed_subscribed or needed_registered): + utils.changed(utils) + + return { + 'needed_registered': needed_registered, + 'did_not_register': did_not_register, + 'needed_subscribed': needed_subscribed, + 'did_not_subscribe': did_not_subscribe + } + +def _getName(component): + try: + return component.__component_name__ + except AttributeError: + return '' + +def _getUtilityProvided(component): + provided = list(providedBy(component)) + if len(provided) == 1: + return provided[0] + raise TypeError( + "The utility doesn't provide a single interface " + "and no provided interface was specified.") + +def _getAdapterProvided(factory): + provided = list(implementedBy(factory)) + if len(provided) == 1: + return provided[0] + raise TypeError( + "The adapter factory doesn't implement a single interface " + "and no provided interface was specified.") + +def _getAdapterRequired(factory, required): + if required is None: + try: + required = factory.__component_adapts__ + except AttributeError: + raise TypeError( + "The adapter factory doesn't have a __component_adapts__ " + "attribute and no required specifications were specified" + ) + elif ISpecification.providedBy(required): + raise TypeError("the required argument should be a list of " + "interfaces, not a single interface") + + result = [] + for r in required: + if r is None: + r = Interface + elif not ISpecification.providedBy(r): + if isinstance(r, type): + r = implementedBy(r) + else: + raise TypeError("Required specification must be a " + "specification or class, not %r" % type(r) + ) + result.append(r) + return tuple(result) + + +@implementer(IUtilityRegistration) +class UtilityRegistration: + + def __init__(self, registry, provided, name, component, doc, factory=None): + (self.registry, self.provided, self.name, self.component, self.info, + self.factory + ) = registry, provided, name, component, doc, factory + + def __repr__(self): + return '{}({!r}, {}, {!r}, {}, {!r}, {!r})'.format( + self.__class__.__name__, + self.registry, + getattr(self.provided, '__name__', None), self.name, + getattr(self.component, '__name__', repr(self.component)), + self.factory, self.info, + ) + + def __hash__(self): + return id(self) + + def __eq__(self, other): + return repr(self) == repr(other) + + def __ne__(self, other): + return repr(self) != repr(other) + + def __lt__(self, other): + return repr(self) < repr(other) + + def __le__(self, other): + return repr(self) <= repr(other) + + def __gt__(self, other): + return repr(self) > repr(other) + + def __ge__(self, other): + return repr(self) >= repr(other) + +@implementer(IAdapterRegistration) +class AdapterRegistration: + + def __init__(self, registry, required, provided, name, component, doc): + (self.registry, self.required, self.provided, self.name, + self.factory, self.info + ) = registry, required, provided, name, component, doc + + def __repr__(self): + return '{}({!r}, {}, {}, {!r}, {}, {!r})'.format( + self.__class__.__name__, + self.registry, + '[' + ", ".join([r.__name__ for r in self.required]) + ']', + getattr(self.provided, '__name__', None), self.name, + getattr(self.factory, '__name__', repr(self.factory)), self.info, + ) + + def __hash__(self): + return id(self) + + def __eq__(self, other): + return repr(self) == repr(other) + + def __ne__(self, other): + return repr(self) != repr(other) + + def __lt__(self, other): + return repr(self) < repr(other) + + def __le__(self, other): + return repr(self) <= repr(other) + + def __gt__(self, other): + return repr(self) > repr(other) + + def __ge__(self, other): + return repr(self) >= repr(other) + +@implementer_only(ISubscriptionAdapterRegistration) +class SubscriptionRegistration(AdapterRegistration): + pass + + +@implementer_only(IHandlerRegistration) +class HandlerRegistration(AdapterRegistration): + + def __init__(self, registry, required, name, handler, doc): + (self.registry, self.required, self.name, self.handler, self.info + ) = registry, required, name, handler, doc + + @property + def factory(self): + return self.handler + + provided = None + + def __repr__(self): + return '{}({!r}, {}, {!r}, {}, {!r})'.format( + self.__class__.__name__, + self.registry, + '[' + ", ".join([r.__name__ for r in self.required]) + ']', + self.name, + getattr(self.factory, '__name__', repr(self.factory)), self.info, + ) diff --git a/contrib/python/zope.interface/py3/zope/interface/ro.py b/contrib/python/zope.interface/py3/zope/interface/ro.py new file mode 100644 index 0000000000..17468e9231 --- /dev/null +++ b/contrib/python/zope.interface/py3/zope/interface/ro.py @@ -0,0 +1,665 @@ +############################################################################## +# +# Copyright (c) 2003 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. +# +############################################################################## +""" +Compute a resolution order for an object and its bases. + +.. versionchanged:: 5.0 + The resolution order is now based on the same C3 order that Python + uses for classes. In complex instances of multiple inheritance, this + may result in a different ordering. + + In older versions, the ordering wasn't required to be C3 compliant, + and for backwards compatibility, it still isn't. If the ordering + isn't C3 compliant (if it is *inconsistent*), zope.interface will + make a best guess to try to produce a reasonable resolution order. + Still (just as before), the results in such cases may be + surprising. + +.. rubric:: Environment Variables + +Due to the change in 5.0, certain environment variables can be used to control errors +and warnings about inconsistent resolution orders. They are listed in priority order, with +variables at the bottom generally overriding variables above them. + +ZOPE_INTERFACE_WARN_BAD_IRO + If this is set to "1", then if there is at least one inconsistent resolution + order discovered, a warning (:class:`InconsistentResolutionOrderWarning`) will + be issued. Use the usual warning mechanisms to control this behaviour. The warning + text will contain additional information on debugging. +ZOPE_INTERFACE_TRACK_BAD_IRO + If this is set to "1", then zope.interface will log information about each + inconsistent resolution order discovered, and keep those details in memory in this module + for later inspection. +ZOPE_INTERFACE_STRICT_IRO + If this is set to "1", any attempt to use :func:`ro` that would produce a non-C3 + ordering will fail by raising :class:`InconsistentResolutionOrderError`. + +.. important:: + + ``ZOPE_INTERFACE_STRICT_IRO`` is intended to become the default in the future. + +There are two environment variables that are independent. + +ZOPE_INTERFACE_LOG_CHANGED_IRO + If this is set to "1", then if the C3 resolution order is different from + the legacy resolution order for any given object, a message explaining the differences + will be logged. This is intended to be used for debugging complicated IROs. +ZOPE_INTERFACE_USE_LEGACY_IRO + If this is set to "1", then the C3 resolution order will *not* be used. The + legacy IRO will be used instead. This is a temporary measure and will be removed in the + future. It is intended to help during the transition. + It implies ``ZOPE_INTERFACE_LOG_CHANGED_IRO``. + +.. rubric:: Debugging Behaviour Changes in zope.interface 5 + +Most behaviour changes from zope.interface 4 to 5 are related to +inconsistent resolution orders. ``ZOPE_INTERFACE_STRICT_IRO`` is the +most effective tool to find such inconsistent resolution orders, and +we recommend running your code with this variable set if at all +possible. Doing so will ensure that all interface resolution orders +are consistent, and if they're not, will immediately point the way to +where this is violated. + +Occasionally, however, this may not be enough. This is because in some +cases, a C3 ordering can be found (the resolution order is fully +consistent) that is substantially different from the ad-hoc legacy +ordering. In such cases, you may find that you get an unexpected value +returned when adapting one or more objects to an interface. To debug +this, *also* enable ``ZOPE_INTERFACE_LOG_CHANGED_IRO`` and examine the +output. The main thing to look for is changes in the relative +positions of interfaces for which there are registered adapters. +""" +__docformat__ = 'restructuredtext' + +__all__ = [ + 'ro', + 'InconsistentResolutionOrderError', + 'InconsistentResolutionOrderWarning', +] + +__logger = None + +def _logger(): + global __logger # pylint:disable=global-statement + if __logger is None: + import logging + __logger = logging.getLogger(__name__) + return __logger + +def _legacy_mergeOrderings(orderings): + """Merge multiple orderings so that within-ordering order is preserved + + Orderings are constrained in such a way that if an object appears + in two or more orderings, then the suffix that begins with the + object must be in both orderings. + + For example: + + >>> _mergeOrderings([ + ... ['x', 'y', 'z'], + ... ['q', 'z'], + ... [1, 3, 5], + ... ['z'] + ... ]) + ['x', 'y', 'q', 1, 3, 5, 'z'] + + """ + + seen = set() + result = [] + for ordering in reversed(orderings): + for o in reversed(ordering): + if o not in seen: + seen.add(o) + result.insert(0, o) + + return result + +def _legacy_flatten(begin): + result = [begin] + i = 0 + for ob in iter(result): + i += 1 + # The recursive calls can be avoided by inserting the base classes + # into the dynamically growing list directly after the currently + # considered object; the iterator makes sure this will keep working + # in the future, since it cannot rely on the length of the list + # by definition. + result[i:i] = ob.__bases__ + return result + +def _legacy_ro(ob): + return _legacy_mergeOrderings([_legacy_flatten(ob)]) + +### +# Compare base objects using identity, not equality. This matches what +# the CPython MRO algorithm does, and is *much* faster to boot: that, +# plus some other small tweaks makes the difference between 25s and 6s +# in loading 446 plone/zope interface.py modules (1925 InterfaceClass, +# 1200 Implements, 1100 ClassProvides objects) +### + + +class InconsistentResolutionOrderWarning(PendingDeprecationWarning): + """ + The warning issued when an invalid IRO is requested. + """ + +class InconsistentResolutionOrderError(TypeError): + """ + The error raised when an invalid IRO is requested in strict mode. + """ + + def __init__(self, c3, base_tree_remaining): + self.C = c3.leaf + base_tree = c3.base_tree + self.base_ros = { + base: base_tree[i + 1] + for i, base in enumerate(self.C.__bases__) + } + # Unfortunately, this doesn't necessarily directly match + # up to any transformation on C.__bases__, because + # if any were fully used up, they were removed already. + self.base_tree_remaining = base_tree_remaining + + TypeError.__init__(self) + + def __str__(self): + import pprint + return "{}: For object {!r}.\nBase ROs:\n{}\nConflict Location:\n{}".format( + self.__class__.__name__, + self.C, + pprint.pformat(self.base_ros), + pprint.pformat(self.base_tree_remaining), + ) + + +class _NamedBool(int): # cannot actually inherit bool + + def __new__(cls, val, name): + inst = super(cls, _NamedBool).__new__(cls, val) + inst.__name__ = name + return inst + + +class _ClassBoolFromEnv: + """ + Non-data descriptor that reads a transformed environment variable + as a boolean, and caches the result in the class. + """ + + def __get__(self, inst, klass): + import os + for cls in klass.__mro__: + my_name = None + for k in dir(klass): + if k in cls.__dict__ and cls.__dict__[k] is self: + my_name = k + break + if my_name is not None: + break + else: # pragma: no cover + raise RuntimeError("Unable to find self") + + env_name = 'ZOPE_INTERFACE_' + my_name + val = os.environ.get(env_name, '') == '1' + val = _NamedBool(val, my_name) + setattr(klass, my_name, val) + setattr(klass, 'ORIG_' + my_name, self) + return val + + +class _StaticMRO: + # A previously resolved MRO, supplied by the caller. + # Used in place of calculating it. + + had_inconsistency = None # We don't know... + + def __init__(self, C, mro): + self.leaf = C + self.__mro = tuple(mro) + + def mro(self): + return list(self.__mro) + + +class C3: + # Holds the shared state during computation of an MRO. + + @staticmethod + def resolver(C, strict, base_mros): + strict = strict if strict is not None else C3.STRICT_IRO + factory = C3 + if strict: + factory = _StrictC3 + elif C3.TRACK_BAD_IRO: + factory = _TrackingC3 + + memo = {} + base_mros = base_mros or {} + for base, mro in base_mros.items(): + assert base in C.__bases__ + memo[base] = _StaticMRO(base, mro) + + return factory(C, memo) + + __mro = None + __legacy_ro = None + direct_inconsistency = False + + def __init__(self, C, memo): + self.leaf = C + self.memo = memo + kind = self.__class__ + + base_resolvers = [] + for base in C.__bases__: + if base not in memo: + resolver = kind(base, memo) + memo[base] = resolver + base_resolvers.append(memo[base]) + + self.base_tree = [ + [C] + ] + [ + memo[base].mro() for base in C.__bases__ + ] + [ + list(C.__bases__) + ] + + self.bases_had_inconsistency = any(base.had_inconsistency for base in base_resolvers) + + if len(C.__bases__) == 1: + self.__mro = [C] + memo[C.__bases__[0]].mro() + + @property + def had_inconsistency(self): + return self.direct_inconsistency or self.bases_had_inconsistency + + @property + def legacy_ro(self): + if self.__legacy_ro is None: + self.__legacy_ro = tuple(_legacy_ro(self.leaf)) + return list(self.__legacy_ro) + + TRACK_BAD_IRO = _ClassBoolFromEnv() + STRICT_IRO = _ClassBoolFromEnv() + WARN_BAD_IRO = _ClassBoolFromEnv() + LOG_CHANGED_IRO = _ClassBoolFromEnv() + USE_LEGACY_IRO = _ClassBoolFromEnv() + BAD_IROS = () + + def _warn_iro(self): + if not self.WARN_BAD_IRO: + # For the initial release, one must opt-in to see the warning. + # In the future (2021?) seeing at least the first warning will + # be the default + return + import warnings + warnings.warn( + "An inconsistent resolution order is being requested. " + "(Interfaces should follow the Python class rules known as C3.) " + "For backwards compatibility, zope.interface will allow this, " + "making the best guess it can to produce as meaningful an order as possible. " + "In the future this might be an error. Set the warning filter to error, or set " + "the environment variable 'ZOPE_INTERFACE_TRACK_BAD_IRO' to '1' and examine " + "ro.C3.BAD_IROS to debug, or set 'ZOPE_INTERFACE_STRICT_IRO' to raise exceptions.", + InconsistentResolutionOrderWarning, + ) + + @staticmethod + def _can_choose_base(base, base_tree_remaining): + # From C3: + # nothead = [s for s in nonemptyseqs if cand in s[1:]] + for bases in base_tree_remaining: + if not bases or bases[0] is base: + continue + + for b in bases: + if b is base: + return False + return True + + @staticmethod + def _nonempty_bases_ignoring(base_tree, ignoring): + return list(filter(None, [ + [b for b in bases if b is not ignoring] + for bases + in base_tree + ])) + + def _choose_next_base(self, base_tree_remaining): + """ + Return the next base. + + The return value will either fit the C3 constraints or be our best + guess about what to do. If we cannot guess, this may raise an exception. + """ + base = self._find_next_C3_base(base_tree_remaining) + if base is not None: + return base + return self._guess_next_base(base_tree_remaining) + + def _find_next_C3_base(self, base_tree_remaining): + """ + Return the next base that fits the constraints, or ``None`` if there isn't one. + """ + for bases in base_tree_remaining: + base = bases[0] + if self._can_choose_base(base, base_tree_remaining): + return base + return None + + class _UseLegacyRO(Exception): + pass + + def _guess_next_base(self, base_tree_remaining): + # Narf. We may have an inconsistent order (we won't know for + # sure until we check all the bases). Python cannot create + # classes like this: + # + # class B1: + # pass + # class B2(B1): + # pass + # class C(B1, B2): # -> TypeError; this is like saying C(B1, B2, B1). + # pass + # + # However, older versions of zope.interface were fine with this order. + # A good example is ``providedBy(IOError())``. Because of the way + # ``classImplements`` works, it winds up with ``__bases__`` == + # ``[IEnvironmentError, IIOError, IOSError, <implementedBy Exception>]`` + # (on Python 3). But ``IEnvironmentError`` is a base of both ``IIOError`` + # and ``IOSError``. Previously, we would get a resolution order of + # ``[IIOError, IOSError, IEnvironmentError, IStandardError, IException, Interface]`` + # but the standard Python algorithm would forbid creating that order entirely. + + # Unlike Python's MRO, we attempt to resolve the issue. A few + # heuristics have been tried. One was: + # + # Strip off the first (highest priority) base of each direct + # base one at a time and seeing if we can come to an agreement + # with the other bases. (We're trying for a partial ordering + # here.) This often resolves cases (such as the IOSError case + # above), and frequently produces the same ordering as the + # legacy MRO did. If we looked at all the highest priority + # bases and couldn't find any partial ordering, then we strip + # them *all* out and begin the C3 step again. We take care not + # to promote a common root over all others. + # + # If we only did the first part, stripped off the first + # element of the first item, we could resolve simple cases. + # But it tended to fail badly. If we did the whole thing, it + # could be extremely painful from a performance perspective + # for deep/wide things like Zope's OFS.SimpleItem.Item. Plus, + # anytime you get ExtensionClass.Base into the mix, you're + # likely to wind up in trouble, because it messes with the MRO + # of classes. Sigh. + # + # So now, we fall back to the old linearization (fast to compute). + self._warn_iro() + self.direct_inconsistency = InconsistentResolutionOrderError(self, base_tree_remaining) + raise self._UseLegacyRO + + def _merge(self): + # Returns a merged *list*. + result = self.__mro = [] + base_tree_remaining = self.base_tree + base = None + while 1: + # Take last picked base out of the base tree wherever it is. + # This differs slightly from the standard Python MRO and is needed + # because we have no other step that prevents duplicates + # from coming in (e.g., in the inconsistent fallback path) + base_tree_remaining = self._nonempty_bases_ignoring(base_tree_remaining, base) + + if not base_tree_remaining: + return result + try: + base = self._choose_next_base(base_tree_remaining) + except self._UseLegacyRO: + self.__mro = self.legacy_ro + return self.legacy_ro + + result.append(base) + + def mro(self): + if self.__mro is None: + self.__mro = tuple(self._merge()) + return list(self.__mro) + + +class _StrictC3(C3): + __slots__ = () + def _guess_next_base(self, base_tree_remaining): + raise InconsistentResolutionOrderError(self, base_tree_remaining) + + +class _TrackingC3(C3): + __slots__ = () + def _guess_next_base(self, base_tree_remaining): + import traceback + bad_iros = C3.BAD_IROS + if self.leaf not in bad_iros: + if bad_iros == (): + import weakref + # This is a race condition, but it doesn't matter much. + bad_iros = C3.BAD_IROS = weakref.WeakKeyDictionary() + bad_iros[self.leaf] = t = ( + InconsistentResolutionOrderError(self, base_tree_remaining), + traceback.format_stack() + ) + _logger().warning("Tracking inconsistent IRO: %s", t[0]) + return C3._guess_next_base(self, base_tree_remaining) + + +class _ROComparison: + # Exists to compute and print a pretty string comparison + # for differing ROs. + # Since we're used in a logging context, and may actually never be printed, + # this is a class so we can defer computing the diff until asked. + + # Components we use to build up the comparison report + class Item: + prefix = ' ' + def __init__(self, item): + self.item = item + def __str__(self): + return "{}{}".format( + self.prefix, + self.item, + ) + + class Deleted(Item): + prefix = '- ' + + class Inserted(Item): + prefix = '+ ' + + Empty = str + + class ReplacedBy: # pragma: no cover + prefix = '- ' + suffix = '' + def __init__(self, chunk, total_count): + self.chunk = chunk + self.total_count = total_count + + def __iter__(self): + lines = [ + self.prefix + str(item) + self.suffix + for item in self.chunk + ] + while len(lines) < self.total_count: + lines.append('') + + return iter(lines) + + class Replacing(ReplacedBy): + prefix = "+ " + suffix = '' + + + _c3_report = None + _legacy_report = None + + def __init__(self, c3, c3_ro, legacy_ro): + self.c3 = c3 + self.c3_ro = c3_ro + self.legacy_ro = legacy_ro + + def __move(self, from_, to_, chunk, operation): + for x in chunk: + to_.append(operation(x)) + from_.append(self.Empty()) + + def _generate_report(self): + if self._c3_report is None: + import difflib + # The opcodes we get describe how to turn 'a' into 'b'. So + # the old one (legacy) needs to be first ('a') + matcher = difflib.SequenceMatcher(None, self.legacy_ro, self.c3_ro) + # The reports are equal length sequences. We're going for a + # side-by-side diff. + self._c3_report = c3_report = [] + self._legacy_report = legacy_report = [] + for opcode, leg1, leg2, c31, c32 in matcher.get_opcodes(): + c3_chunk = self.c3_ro[c31:c32] + legacy_chunk = self.legacy_ro[leg1:leg2] + + if opcode == 'equal': + # Guaranteed same length + c3_report.extend(self.Item(x) for x in c3_chunk) + legacy_report.extend(self.Item(x) for x in legacy_chunk) + if opcode == 'delete': + # Guaranteed same length + assert not c3_chunk + self.__move(c3_report, legacy_report, legacy_chunk, self.Deleted) + if opcode == 'insert': + # Guaranteed same length + assert not legacy_chunk + self.__move(legacy_report, c3_report, c3_chunk, self.Inserted) + if opcode == 'replace': # pragma: no cover (How do you make it output this?) + # Either side could be longer. + chunk_size = max(len(c3_chunk), len(legacy_chunk)) + c3_report.extend(self.Replacing(c3_chunk, chunk_size)) + legacy_report.extend(self.ReplacedBy(legacy_chunk, chunk_size)) + + return self._c3_report, self._legacy_report + + @property + def _inconsistent_label(self): + inconsistent = [] + if self.c3.direct_inconsistency: + inconsistent.append('direct') + if self.c3.bases_had_inconsistency: + inconsistent.append('bases') + return '+'.join(inconsistent) if inconsistent else 'no' + + def __str__(self): + c3_report, legacy_report = self._generate_report() + assert len(c3_report) == len(legacy_report) + + left_lines = [str(x) for x in legacy_report] + right_lines = [str(x) for x in c3_report] + + # We have the same number of lines in the report; this is not + # necessarily the same as the number of items in either RO. + assert len(left_lines) == len(right_lines) + + padding = ' ' * 2 + max_left = max(len(x) for x in left_lines) + max_right = max(len(x) for x in right_lines) + + left_title = 'Legacy RO (len={})'.format(len(self.legacy_ro)) + + right_title = 'C3 RO (len={}; inconsistent={})'.format( + len(self.c3_ro), + self._inconsistent_label, + ) + lines = [ + (padding + left_title.ljust(max_left) + padding + right_title.ljust(max_right)), + padding + '=' * (max_left + len(padding) + max_right) + ] + lines += [ + padding + left.ljust(max_left) + padding + right + for left, right in zip(left_lines, right_lines) + ] + + return '\n'.join(lines) + + +# Set to `Interface` once it is defined. This is used to +# avoid logging false positives about changed ROs. +_ROOT = None + +def ro(C, strict=None, base_mros=None, log_changed_ro=None, use_legacy_ro=None): + """ + ro(C) -> list + + Compute the precedence list (mro) according to C3. + + :return: A fresh `list` object. + + .. versionchanged:: 5.0.0 + Add the *strict*, *log_changed_ro* and *use_legacy_ro* + keyword arguments. These are provisional and likely to be + removed in the future. They are most useful for testing. + """ + # The ``base_mros`` argument is for internal optimization and + # not documented. + resolver = C3.resolver(C, strict, base_mros) + mro = resolver.mro() + + log_changed = log_changed_ro if log_changed_ro is not None else resolver.LOG_CHANGED_IRO + use_legacy = use_legacy_ro if use_legacy_ro is not None else resolver.USE_LEGACY_IRO + + if log_changed or use_legacy: + legacy_ro = resolver.legacy_ro + assert isinstance(legacy_ro, list) + assert isinstance(mro, list) + changed = legacy_ro != mro + if changed: + # Did only Interface move? The fix for issue #8 made that + # somewhat common. It's almost certainly not a problem, though, + # so allow ignoring it. + legacy_without_root = [x for x in legacy_ro if x is not _ROOT] + mro_without_root = [x for x in mro if x is not _ROOT] + changed = legacy_without_root != mro_without_root + + if changed: + comparison = _ROComparison(resolver, mro, legacy_ro) + _logger().warning( + "Object %r has different legacy and C3 MROs:\n%s", + C, comparison + ) + if resolver.had_inconsistency and legacy_ro == mro: + comparison = _ROComparison(resolver, mro, legacy_ro) + _logger().warning( + "Object %r had inconsistent IRO and used the legacy RO:\n%s" + "\nInconsistency entered at:\n%s", + C, comparison, resolver.direct_inconsistency + ) + if use_legacy: + return legacy_ro + + return mro + + +def is_consistent(C): + """ + Check if the resolution order for *C*, as computed by :func:`ro`, is consistent + according to C3. + """ + return not C3.resolver(C, False, None).had_inconsistency diff --git a/contrib/python/zope.interface/py3/zope/interface/verify.py b/contrib/python/zope.interface/py3/zope/interface/verify.py new file mode 100644 index 0000000000..0ab0b3f96b --- /dev/null +++ b/contrib/python/zope.interface/py3/zope/interface/verify.py @@ -0,0 +1,185 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 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. +# +############################################################################## +"""Verify interface implementations +""" +import inspect +import sys +from types import FunctionType +from types import MethodType + +from zope.interface.exceptions import BrokenImplementation +from zope.interface.exceptions import BrokenMethodImplementation +from zope.interface.exceptions import DoesNotImplement +from zope.interface.exceptions import Invalid +from zope.interface.exceptions import MultipleInvalid + +from zope.interface.interface import fromMethod, fromFunction, Method + +__all__ = [ + 'verifyObject', + 'verifyClass', +] + +# This will be monkey-patched when running under Zope 2, so leave this +# here: +MethodTypes = (MethodType, ) + + +def _verify(iface, candidate, tentative=False, vtype=None): + """ + Verify that *candidate* might correctly provide *iface*. + + This involves: + + - Making sure the candidate claims that it provides the + interface using ``iface.providedBy`` (unless *tentative* is `True`, + in which case this step is skipped). This means that the candidate's class + declares that it `implements <zope.interface.implementer>` the interface, + or the candidate itself declares that it `provides <zope.interface.provider>` + the interface + + - Making sure the candidate defines all the necessary methods + + - Making sure the methods have the correct signature (to the + extent possible) + + - Making sure the candidate defines all the necessary attributes + + :return bool: Returns a true value if everything that could be + checked passed. + :raises zope.interface.Invalid: If any of the previous + conditions does not hold. + + .. versionchanged:: 5.0 + If multiple methods or attributes are invalid, all such errors + are collected and reported. Previously, only the first error was reported. + As a special case, if only one such error is present, it is raised + alone, like before. + """ + + if vtype == 'c': + tester = iface.implementedBy + else: + tester = iface.providedBy + + excs = [] + if not tentative and not tester(candidate): + excs.append(DoesNotImplement(iface, candidate)) + + for name, desc in iface.namesAndDescriptions(all=True): + try: + _verify_element(iface, name, desc, candidate, vtype) + except Invalid as e: + excs.append(e) + + if excs: + if len(excs) == 1: + raise excs[0] + raise MultipleInvalid(iface, candidate, excs) + + return True + +def _verify_element(iface, name, desc, candidate, vtype): + # Here the `desc` is either an `Attribute` or `Method` instance + try: + attr = getattr(candidate, name) + except AttributeError: + if (not isinstance(desc, Method)) and vtype == 'c': + # We can't verify non-methods on classes, since the + # class may provide attrs in it's __init__. + return + # TODO: This should use ``raise...from`` + raise BrokenImplementation(iface, desc, candidate) + + if not isinstance(desc, Method): + # If it's not a method, there's nothing else we can test + return + + if inspect.ismethoddescriptor(attr) or inspect.isbuiltin(attr): + # The first case is what you get for things like ``dict.pop`` + # on CPython (e.g., ``verifyClass(IFullMapping, dict))``). The + # second case is what you get for things like ``dict().pop`` on + # CPython (e.g., ``verifyObject(IFullMapping, dict()))``. + # In neither case can we get a signature, so there's nothing + # to verify. Even the inspect module gives up and raises + # ValueError: no signature found. The ``__text_signature__`` attribute + # isn't typically populated either. + # + # Note that on PyPy 2 or 3 (up through 7.3 at least), these are + # not true for things like ``dict.pop`` (but might be true for C extensions?) + return + + if isinstance(attr, FunctionType): + if isinstance(candidate, type) and vtype == 'c': + # This is an "unbound method". + # Only unwrap this if we're verifying implementedBy; + # otherwise we can unwrap @staticmethod on classes that directly + # provide an interface. + meth = fromFunction(attr, iface, name=name, imlevel=1) + else: + # Nope, just a normal function + meth = fromFunction(attr, iface, name=name) + elif (isinstance(attr, MethodTypes) + and type(attr.__func__) is FunctionType): + meth = fromMethod(attr, iface, name) + elif isinstance(attr, property) and vtype == 'c': + # Without an instance we cannot be sure it's not a + # callable. + # TODO: This should probably check inspect.isdatadescriptor(), + # a more general form than ``property`` + return + + else: + if not callable(attr): + raise BrokenMethodImplementation(desc, "implementation is not a method", + attr, iface, candidate) + # sigh, it's callable, but we don't know how to introspect it, so + # we have to give it a pass. + return + + # Make sure that the required and implemented method signatures are + # the same. + mess = _incompat(desc.getSignatureInfo(), meth.getSignatureInfo()) + if mess: + raise BrokenMethodImplementation(desc, mess, attr, iface, candidate) + + + +def verifyClass(iface, candidate, tentative=False): + """ + Verify that the *candidate* might correctly provide *iface*. + """ + return _verify(iface, candidate, tentative, vtype='c') + +def verifyObject(iface, candidate, tentative=False): + return _verify(iface, candidate, tentative, vtype='o') + +verifyObject.__doc__ = _verify.__doc__ + +_MSG_TOO_MANY = 'implementation requires too many arguments' + +def _incompat(required, implemented): + #if (required['positional'] != + # implemented['positional'][:len(required['positional'])] + # and implemented['kwargs'] is None): + # return 'imlementation has different argument names' + if len(implemented['required']) > len(required['required']): + return _MSG_TOO_MANY + if ((len(implemented['positional']) < len(required['positional'])) + and not implemented['varargs']): + return "implementation doesn't allow enough arguments" + if required['kwargs'] and not implemented['kwargs']: + return "implementation doesn't support keyword arguments" + if required['varargs'] and not implemented['varargs']: + return "implementation doesn't support variable arguments" |