Mercurial > hg > mercurial-source
changeset 37962:943d77fc07a3
thirdparty: vendor zope.interface 4.4.3
I've been trying to formalize interfaces for various components
of Mercurial. So far, we've been using the "abc" package. This
package is "good enough" for a lot of tasks. But it quickly
falls over. For example, if you declare an @abc.abstractproperty,
you must implement that attribute with a @property or the class
compile time checking performed by abc will complain. This often
forces you to implement dumb @property wrappers to return a
_ prefixed attribute of the sane name. That's ugly.
I've also wanted to implement automated checking that classes
conform to various interfaces and don't expose other "public"
attributes.
After doing a bit of research and asking around, the general
consensus seems to be that zope.interface is the best package for
doing interface-based programming in Python. It has built-in
support for verifying classes and objects conform to interfaces.
It allows an interface's properties to be defined during __init__.
There's even an "adapter registry" that allow you to register
interfaces and look up which classes implement them. That could
potentially be useful for places where our custom registry.py
modules currently facilitates central registrations, but at a
type level. Imagine extensions providing alternate implementations
of things like the local repository interface to allow opening
repositories with custom requirements.
Anyway, this commit vendors zope.interface 4.4.3. The contents of
the source tarball have been copied into mercurial/thirdparty/zope/
without modifications.
Test modules have been removed because they are not interesting
to us.
The LICENSE.txt file has been copied so it lives next to the
source.
The Python modules don't use relative imports. zope/__init__.py
defines a namespace package. So we'll need to modify the source
code before this package is usable inside Mercurial. This will
be done in subsequent commits.
# no-check-commit for various style failures
Differential Revision: https://phab.mercurial-scm.org/D2928
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/mercurial/thirdparty/zope/__init__.py @@ -0,0 +1,7 @@ +# this is a namespace package +try: + import pkg_resources + pkg_resources.declare_namespace(__name__) +except ImportError: + import pkgutil + __path__ = pkgutil.extend_path(__path__, __name__)
new file mode 100644 --- /dev/null +++ b/mercurial/thirdparty/zope/interface/LICENSE.txt @@ -0,0 +1,44 @@ +Zope Public License (ZPL) Version 2.1 + +A copyright notice accompanies this license document that identifies the +copyright holders. + +This license has been certified as open source. It has also been designated as +GPL compatible by the Free Software Foundation (FSF). + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions in source code must retain the accompanying copyright +notice, this list of conditions, and the following disclaimer. + +2. Redistributions in binary form must reproduce the accompanying copyright +notice, this list of conditions, and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +3. Names of the copyright holders must not be used to endorse or promote +products derived from this software without prior written permission from the +copyright holders. + +4. The right to distribute this software or to use it for any purpose does not +give you the right to use Servicemarks (sm) or Trademarks (tm) of the +copyright +holders. Use of them is covered by separate agreement with the copyright +holders. + +5. If any files are modified, you must cause the modified files to carry +prominent notices stating that you changed the files and the date of any +change. + +Disclaimer + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED +OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
new file mode 100644 --- /dev/null +++ b/mercurial/thirdparty/zope/interface/__init__.py @@ -0,0 +1,90 @@ +############################################################################## +# +# 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' + +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 classImplementsOnly +from zope.interface.declarations import classProvides +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 implements +from zope.interface.declarations import implementsOnly +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 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)
new file mode 100644 --- /dev/null +++ b/mercurial/thirdparty/zope/interface/_compat.py @@ -0,0 +1,58 @@ +############################################################################## +# +# 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 +""" +import sys +import types + +if sys.version_info[0] < 3: + + def _normalize_name(name): + if isinstance(name, basestring): + return unicode(name) + raise TypeError("name must be a regular or unicode string") + + CLASS_TYPES = (type, types.ClassType) + STRING_TYPES = (basestring,) + + _BUILTINS = '__builtin__' + + PYTHON3 = False + PYTHON2 = True + +else: + + 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") + + CLASS_TYPES = (type,) + STRING_TYPES = (str,) + + _BUILTINS = 'builtins' + + PYTHON3 = True + PYTHON2 = False + +def _skip_under_py3k(test_method): + import unittest + return unittest.skipIf(sys.version_info[0] >= 3, "Only on Python 2")(test_method) + + +def _skip_under_py2(test_method): + import unittest + return unittest.skipIf(sys.version_info[0] < 3, "Only on Python 3")(test_method)
new file mode 100644 --- /dev/null +++ b/mercurial/thirdparty/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
new file mode 100644 --- /dev/null +++ b/mercurial/thirdparty/zope/interface/_zope_interface_coptimizations.c @@ -0,0 +1,1726 @@ +/*########################################################################### + # + # 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" + +#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 + +#if PY_MAJOR_VERSION >= 3 +#define PY3K +#endif + +static PyObject *str__dict__, *str__implemented__, *strextends; +static PyObject *BuiltinImplementationSpecifications, *str__provides__; +static PyObject *str__class__, *str__providedBy__; +static PyObject *empty, *fallback, *str_implied, *str_cls, *str_implements; +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 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 SpecType; /* 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 (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 != NULL && PyObject_TypeCheck(result, &SpecType)) + return result; + + PyErr_Clear(); + + /* We do a getattr here so as not to be defeated by proxies */ + cls = PyObject_GetAttr(ob, str__class__); + if (cls == 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; + + result = PyObject_GetAttr(ob, str__providedBy__); + if (result == 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, &SpecType) + || + 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; +} + +/* + Get an attribute from an inst dict. Return a borrowed reference. + + This has a number of advantages: + + - It avoids layers of Python api + + - It doesn't waste time looking for descriptors + + - It fails wo raising an exception, although that shouldn't really + matter. + +*/ +static PyObject * +inst_attr(PyObject *self, PyObject *name) +{ + PyObject **dictp, *v; + + dictp = _PyObject_GetDictPtr(self); + if (dictp && *dictp && (v = PyDict_GetItem(*dictp, name))) + return v; + PyErr_SetObject(PyExc_AttributeError, name); + return NULL; +} + + +static PyObject * +Spec_extends(PyObject *self, PyObject *other) +{ + PyObject *implied; + + implied = inst_attr(self, str_implied); + if (implied == NULL) + return NULL; + +#ifdef Py_True + if (PyDict_GetItem(implied, other) != NULL) + { + Py_INCREF(Py_True); + return Py_True; + } + Py_INCREF(Py_False); + return Py_False; +#else + return PyInt_FromLong(PyDict_GetItem(implied, other) != NULL); +#endif +} + +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(PyObject *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, &SpecType)) + item = Spec_extends(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, &SpecType)) + item = Spec_extends(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 PyTypeObject SpecType = { + PyVarObject_HEAD_INIT(NULL, 0) + /* tp_name */ "_interface_coptimizations." + "SpecificationBase", + /* 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)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, + "Base type for Specification objects", + /* 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 */ Spec_methods, +}; + +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__); + if (provides != NULL) + 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, +}; + +static PyObject * +CPB_descr_get(PyObject *self, PyObject *inst, PyObject *cls) +{ + PyObject *mycls, *implements; + + mycls = inst_attr(self, str_cls); + if (mycls == NULL) + return NULL; + + if (cls == mycls) + { + if (inst == NULL) + { + Py_INCREF(self); + return OBJECT(self); + } + + implements = inst_attr(self, str_implements); + Py_XINCREF(implements); + return implements; + } + + PyErr_SetObject(PyExc_AttributeError, str__provides__); + return NULL; +} + +static PyTypeObject CPBType = { + PyVarObject_HEAD_INIT(NULL, 0) + /* tp_name */ "_interface_coptimizations." + "ClassProvidesBase", + /* 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, + "C Base class for ClassProvides", + /* 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 */ &SpecType, + /* tp_dict */ 0, /* internal use */ + /* tp_descr_get */ (descrgetfunc)CPB_descr_get, +}; + +/* ==================================================================== */ +/* ========== Begin: __call__ and __adapt__ =========================== */ + +/* + def __adapt__(self, obj): + """Adapt an object to the reciever + """ + 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, &SpecType)) + { + PyObject *implied; + + implied = inst_attr(decl, str_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; +} + +static struct PyMethodDef ib_methods[] = { + {"__adapt__", (PyCFunction)__adapt__, METH_O, + "Adapt an object to the reciever"}, + {NULL, NULL} /* sentinel */ +}; + +/* + def __call__(self, obj, alternate=_marker): + conform = getattr(obj, '__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 + elif alternate is not _marker: + return alternate + else: + raise TypeError("Could not adapt", obj, self) +*/ +static PyObject * +ib_call(PyObject *self, PyObject *args, PyObject *kwargs) +{ + PyObject *conform, *obj, *alternate=NULL, *adapter; + + static char *kwlist[] = {"obj", "alternate", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O", kwlist, + &obj, &alternate)) + return NULL; + + conform = PyObject_GetAttr(obj, str__conform__); + if (conform != NULL) + { + adapter = PyObject_CallMethodObjArgs(self, str_call_conform, + conform, NULL); + Py_DECREF(conform); + if (adapter == NULL || adapter != Py_None) + return adapter; + Py_DECREF(adapter); + } + else + PyErr_Clear(); + + 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 PyTypeObject InterfaceBase = { + PyVarObject_HEAD_INIT(NULL, 0) + /* tp_name */ "_zope_interface_coptimizations." + "InterfaceBase", + /* 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)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 , + /* tp_doc */ "Interface base type providing __call__ and __adapt__", + /* 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 */ ib_methods, +}; + +/* =================== 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 * +tuplefy(PyObject *v) +{ + if (! PyTuple_Check(v)) + { + v = PyObject_CallFunctionObjArgs(OBJECT(&PyTuple_Type), v, NULL); + if (v == NULL) + return NULL; + } + else + Py_INCREF(v); + + return v; +} +static PyObject * +_lookup(lookup *self, + PyObject *required, PyObject *provided, PyObject *name, + PyObject *default_) +{ + PyObject *result, *key, *cache; + +#ifdef PY3K + if ( name && !PyUnicode_Check(name) ) +#else + if ( name && !PyString_Check(name) && !PyUnicode_Check(name) ) +#endif + { + PyErr_SetString(PyExc_ValueError, + "name is not a string or unicode"); + return NULL; + } + cache = _getcache(self, provided, name); + if (cache == NULL) + return NULL; + + required = tuplefy(required); + if (required == 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", 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; + +#ifdef PY3K + if ( name && !PyUnicode_Check(name) ) +#else + if ( name && !PyString_Check(name) && !PyUnicode_Check(name) ) +#endif + { + 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", 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: + 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; + +#ifdef PY3K + if ( name && !PyUnicode_Check(name) ) +#else + if ( name && !PyString_Check(name) && !PyUnicode_Check(name) ) +#endif + { + 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) + { + 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", 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", 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; + + ASSURE_DICT(self->_mcache); + cache = _subcache(self->_mcache, provided); + if (cache == NULL) + return NULL; + + required = tuplefy(required); + if (required == 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", 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; + + ASSURE_DICT(self->_scache); + cache = _subcache(self->_scache, provided); + if (cache == NULL) + return NULL; + + required = tuplefy(required); + if (required == 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(_implied); + DEFINE_STRING(_implements); + DEFINE_STRING(_cls); + 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); +#undef DEFINE_STRING + adapter_hooks = PyList_New(0); + if (adapter_hooks == NULL) + return NULL; + + /* Initialize types: */ + SpecType.tp_new = PyBaseObject_Type.tp_new; + if (PyType_Ready(&SpecType) < 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; + + InterfaceBase.tp_new = PyBaseObject_Type.tp_new; + if (PyType_Ready(&InterfaceBase) < 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(&SpecType)) < 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(&InterfaceBase)) < 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
new file mode 100644 --- /dev/null +++ b/mercurial/thirdparty/zope/interface/adapter.py @@ -0,0 +1,712 @@ +############################################################################## +# +# 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 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 STRING_TYPES + +_BLANK = u'' + +class BaseAdapterRegistry(object): + + # 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 = [] + + # {order -> {required -> {provided -> {name -> [value]}}}} + # where the remarks about adapters above apply + self._subscribers = [] + + # Set, with a reference count, keeping track of the interfaces + # for which we have provided components: + self._provided = {} + + # 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): + 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) + + 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, STRING_TYPES): + raise ValueError('name is not a string') + if value is None: + self.unregister(required, provided, name, value) + return + + required = tuple(map(_convert_None_to_Interface, required)) + name = _normalize_name(name) + order = len(required) + byorder = self._adapters + while len(byorder) <= order: + byorder.append({}) + components = byorder[order] + key = required + (provided,) + + for k in key: + d = components.get(k) + if d is None: + d = {} + 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 registered(self, required, provided, name=_BLANK): + required = tuple(map(_convert_None_to_Interface, required)) + name = _normalize_name(name) + order = len(required) + byorder = self._adapters + 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 unregister(self, required, provided, name, value=None): + required = tuple(map(_convert_None_to_Interface, 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(map(_convert_None_to_Interface, required)) + name = _BLANK + order = len(required) + byorder = self._subscribers + while len(byorder) <= order: + byorder.append({}) + components = byorder[order] + key = required + (provided,) + + for k in key: + d = components.get(k) + if d is None: + d = {} + components[k] = d + components = d + + components[name] = 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 unsubscribe(self, required, provided, value=None): + required = tuple(map(_convert_None_to_Interface, 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(_BLANK) + if not old: + # this is belt-and-suspenders against the failure of cleanup below + return # pragma: no cover + + if value is None: + new = () + else: + new = tuple([v for v in old if v is not value]) + + if new == old: + return + + if new: + components[_BLANK] = new + else: + # Instead of setting components[_BLANK] = 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[_BLANK] + 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) + + self.changed(self) + + # XXX hack to fake out twisted's use of a private api. We need to get them + # to use the new registed method. + def get(self, _): # pragma: no cover + class XXXTwistedFakeOut: + selfImplied = {} + return XXXTwistedFakeOut + + +_not_in_mapping = object() +class LookupBaseFallback(object): + + 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=_BLANK, default=None): + if not isinstance(name, STRING_TYPES): + 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=_BLANK, default=None): + if not isinstance(name, STRING_TYPES): + 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=_BLANK, default=None): + return self.adapter_hook(provided, object, name, default) + + def adapter_hook(self, provided, object, name=_BLANK, default=None): + if not isinstance(name, STRING_TYPES): + 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: + 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 + +LookupBasePy = LookupBaseFallback # BBB + +try: + from zope.interface._zope_interface_coptimizations import LookupBase +except ImportError: + LookupBase = LookupBaseFallback + + +class VerifyingBaseFallback(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) + +VerifyingBasePy = VerifyingBaseFallback #BBB + +try: + from zope.interface._zope_interface_coptimizations import VerifyingBase +except ImportError: + VerifyingBase = VerifyingBaseFallback + + +class AdapterLookupBase(object): + + def __init__(self, registry): + self._registry = registry + self._required = {} + self.init_extendors() + super(AdapterLookupBase, self).__init__() + + def changed(self, ignored=None): + super(AdapterLookupBase, self).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=_BLANK): + 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=_BLANK, default=None): + factory = self.lookup(map(providedBy, objects), provided, name) + if factory is None: + return default + + result = factory(*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, _BLANK, + result, 0, order) + + self._subscribe(*required) + + return result + + def subscribers(self, objects, provided): + subscriptions = self.subscriptions(map(providedBy, 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): + + LookupClass = AdapterLookup + + def __init__(self, bases=()): + # AdapterRegisties are invalidating registries, so + # we need to keep track of out invalidating subregistries. + self._v_subregistries = weakref.WeakKeyDictionary() + + super(AdapterRegistry, self).__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(AdapterRegistry, self)._setBases(bases) + + def changed(self, originally_changed): + super(AdapterRegistry, self).changed(originally_changed) + + for sub in self._v_subregistries.keys(): + sub.changed(originally_changed) + + +class VerifyingAdapterLookup(AdapterLookupBase, VerifyingBase): + pass + +@implementer(IAdapterRegistry) +class VerifyingAdapterRegistry(BaseAdapterRegistry): + + 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): + 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): + 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): + 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)
new file mode 100644 --- /dev/null +++ b/mercurial/thirdparty/zope/interface/advice.py @@ -0,0 +1,205 @@ +############################################################################## +# +# 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 +try: + from types import ClassType +except ImportError: + __python3 = True +else: + __python3 = False + +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 addClassAdvisor(callback, depth=2): + """Set up 'callback' to be passed the containing class upon creation + + This function is designed to be called by an "advising" function executed + in a class suite. The "advising" function supplies a callback that it + wishes to have executed when the containing class is created. The + callback will be given one argument: the newly created containing class. + The return value of the callback will be used in place of the class, so + the callback should return the input if it does not wish to replace the + class. + + The optional 'depth' argument to this function determines the number of + frames between this function and the targeted class suite. 'depth' + defaults to 2, since this skips this function's frame and one calling + function frame. If you use this function from a function called directly + in the class suite, the default will be correct, otherwise you will need + to determine the correct depth yourself. + + This function works by installing a special class factory function in + place of the '__metaclass__' of the containing class. Therefore, only + callbacks *after* the last '__metaclass__' assignment in the containing + class will be executed. Be sure that classes using "advising" functions + declare any '__metaclass__' *first*, to ensure all callbacks are run.""" + # This entire approach is invalid under Py3K. Don't even try to fix + # the coverage for this block there. :( + if __python3: # pragma: no cover + raise TypeError('Class advice impossible in Python3') + + frame = sys._getframe(depth) + kind, module, caller_locals, caller_globals = getFrameInfo(frame) + + # This causes a problem when zope interfaces are used from doctest. + # In these cases, kind == "exec". + # + #if kind != "class": + # raise SyntaxError( + # "Advice must be in the body of a class statement" + # ) + + previousMetaclass = caller_locals.get('__metaclass__') + if __python3: # pragma: no cover + defaultMetaclass = caller_globals.get('__metaclass__', type) + else: + defaultMetaclass = caller_globals.get('__metaclass__', ClassType) + + + def advise(name, bases, cdict): + + if '__metaclass__' in cdict: + del cdict['__metaclass__'] + + if previousMetaclass is None: + if bases: + # find best metaclass or use global __metaclass__ if no bases + meta = determineMetaclass(bases) + else: + meta = defaultMetaclass + + elif isClassAdvisor(previousMetaclass): + # special case: we can't compute the "true" metaclass here, + # so we need to invoke the previous metaclass and let it + # figure it out for us (and apply its own advice in the process) + meta = previousMetaclass + + else: + meta = determineMetaclass(bases, previousMetaclass) + + newClass = meta(name,bases,cdict) + + # this lets the callback replace the class completely, if it wants to + return callback(newClass) + + # introspection data only, not used by inner function + advise.previousMetaclass = previousMetaclass + advise.callback = callback + + # install the advisor + caller_locals['__metaclass__'] = advise + + +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 not candidates: # pragma: no cover + # they're all "classic" classes + assert(not __python3) # This should not happen under Python 3 + return ClassType + + elif 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""" + + if not __python3: # pragma: no cover + classes = [c for c in classes if c is not ClassType] + 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
new file mode 100644 --- /dev/null +++ b/mercurial/thirdparty/zope/interface/common/__init__.py @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package.
new file mode 100644 --- /dev/null +++ b/mercurial/thirdparty/zope/interface/common/idatetime.py @@ -0,0 +1,575 @@ +############################################################################## +# 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 zope.interface import Interface, Attribute +from zope.interface import classImplements + +from datetime import timedelta, date, datetime, time, tzinfo + + +class ITimeDeltaClass(Interface): + """This is the timedelta class 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. + + 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.""" + + 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. + + 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. + + See also 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. + + See also 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.""" + + 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()). + See also 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)). + + See also 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. + + See also 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. + + See also 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. + + See also 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. + """ + + 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. See also method timetz(). + """ + + def timetz(): + """Return time object with same hour, minute, second, microsecond, + and tzinfo. + + See also method 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 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.gmtimr().""" + + 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. + See also 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.""" + + 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. + + 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)
new file mode 100644 --- /dev/null +++ b/mercurial/thirdparty/zope/interface/common/interfaces.py @@ -0,0 +1,102 @@ +############################################################################## +# +# 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): pass +class IStandardError(IException): pass +class IWarning(IException): pass +class ISyntaxError(IStandardError): pass +class ILookupError(IStandardError): pass +class IValueError(IStandardError): pass +class IRuntimeError(IStandardError): pass +class IArithmeticError(IStandardError): pass +class IAssertionError(IStandardError): pass +class IAttributeError(IStandardError): pass +class IDeprecationWarning(IWarning): pass +class IEOFError(IStandardError): pass +class IEnvironmentError(IStandardError): pass +class IFloatingPointError(IArithmeticError): pass +class IIOError(IEnvironmentError): pass +class IImportError(IStandardError): pass +class IIndentationError(ISyntaxError): pass +class IIndexError(ILookupError): pass +class IKeyError(ILookupError): pass +class IKeyboardInterrupt(IStandardError): pass +class IMemoryError(IStandardError): pass +class INameError(IStandardError): pass +class INotImplementedError(IRuntimeError): pass +class IOSError(IEnvironmentError): pass +class IOverflowError(IArithmeticError): pass +class IOverflowWarning(IWarning): pass +class IReferenceError(IStandardError): pass +class IRuntimeWarning(IWarning): pass +class IStopIteration(IException): pass +class ISyntaxWarning(IWarning): pass +class ISystemError(IStandardError): pass +class ISystemExit(IException): pass +class ITabError(IIndentationError): pass +class ITypeError(IStandardError): pass +class IUnboundLocalError(INameError): pass +class IUnicodeError(IValueError): pass +class IUserWarning(IWarning): pass +class IZeroDivisionError(IArithmeticError): pass + +classImplements(ArithmeticError, IArithmeticError) +classImplements(AssertionError, IAssertionError) +classImplements(AttributeError, IAttributeError) +classImplements(DeprecationWarning, IDeprecationWarning) +classImplements(EnvironmentError, IEnvironmentError) +classImplements(EOFError, IEOFError) +classImplements(Exception, IException) +classImplements(FloatingPointError, IFloatingPointError) +classImplements(ImportError, IImportError) +classImplements(IndentationError, IIndentationError) +classImplements(IndexError, IIndexError) +classImplements(IOError, IIOError) +classImplements(KeyboardInterrupt, IKeyboardInterrupt) +classImplements(KeyError, IKeyError) +classImplements(LookupError, ILookupError) +classImplements(MemoryError, IMemoryError) +classImplements(NameError, INameError) +classImplements(NotImplementedError, INotImplementedError) +classImplements(OSError, IOSError) +classImplements(OverflowError, IOverflowError) +try: + classImplements(OverflowWarning, IOverflowWarning) +except NameError: #pragma NO COVER + pass # OverflowWarning was removed in Python 2.5 +classImplements(ReferenceError, IReferenceError) +classImplements(RuntimeError, IRuntimeError) +classImplements(RuntimeWarning, IRuntimeWarning) +try: + classImplements(StandardError, IStandardError) +except NameError: #pragma NO COVER + pass # StandardError does not exist in Python 3 +classImplements(StopIteration, IStopIteration) +classImplements(SyntaxError, ISyntaxError) +classImplements(SyntaxWarning, ISyntaxWarning) +classImplements(SystemError, ISystemError) +classImplements(SystemExit, ISystemExit) +classImplements(TabError, ITabError) +classImplements(TypeError, ITypeError) +classImplements(UnboundLocalError, IUnboundLocalError) +classImplements(UnicodeError, IUnicodeError) +classImplements(UserWarning, IUserWarning) +classImplements(ValueError, IValueError) +classImplements(Warning, IWarning) +classImplements(ZeroDivisionError, IZeroDivisionError) +
new file mode 100644 --- /dev/null +++ b/mercurial/thirdparty/zope/interface/common/mapping.py @@ -0,0 +1,125 @@ +############################################################################## +# +# 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 +""" +from zope.interface import Interface + +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(IItemMapping): + """Basic mapping interface + """ + + 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.""" + + +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(IReadMapping): + """Mapping objects whose items can be enumerated. + """ + + 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. + """ + + def __len__(): + """Return the number of items. + """ + +class IMapping(IWriteMapping, IEnumerableMapping): + ''' Simple mapping interface ''' + +class IIterableMapping(IEnumerableMapping): + + def iterkeys(): + "iterate over keys; equivalent to __iter__" + + def itervalues(): + "iterate over values" + + def iteritems(): + "iterate over items" + +class IClonableMapping(Interface): + + def copy(): + "return copy of dict" + +class IExtendedReadMapping(IIterableMapping): + + def has_key(key): + """Tell if a key exists in the mapping; equivalent to __contains__""" + +class IExtendedWriteMapping(IWriteMapping): + + 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, *args): + """remove specified key and return the corresponding value + *args may contain a single default value, or may not be supplied. + If key is not found, default is returned if given, otherwise + KeyError is raised""" + + def popitem(): + """remove and return some (key, value) pair as a + 2-tuple; but raise KeyError if mapping is empty""" + +class IFullMapping( + IExtendedReadMapping, IExtendedWriteMapping, IClonableMapping, IMapping): + ''' Full mapping interface ''' # IMapping included so tests for IMapping + # succeed with IFullMapping
new file mode 100644 --- /dev/null +++ b/mercurial/thirdparty/zope/interface/common/sequence.py @@ -0,0 +1,160 @@ +############################################################################## +# +# 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 +""" +__docformat__ = 'restructuredtext' +from zope.interface import Interface + +class IMinimalSequence(Interface): + """Most basic sequence interface. + + All sequences are iterable. This requires at least one of the + following: + + - a `__getitem__()` method that takes a single argument; interger + 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(IMinimalSequence): + + def __len__(): + """`x.__len__()` <==> `len(x)`""" + +class IReadSequence(IFiniteSequence): + """read interface shared by tuple and list""" + + def __contains__(item): + """`x.__contains__(item)` <==> `item in x`""" + + 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`""" + + def __getslice__(i, j): + """`x.__getslice__(i, j)` <==> `x[i:j]` + + Use of negative indices is not supported. + + Deprecated since Python 2.0 but still a part of `UserList`. + """ + +class IExtendedReadSequence(IReadSequence): + """Full read interface for lists""" + + def count(item): + """Return number of occurrences of value""" + + def index(item, *args): + """Return first index of value + + `L.index(value, [start, [stop]])` -> integer""" + +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 __setslice__(i, j, other): + """`x.__setslice__(i, j, other)` <==> `x[i:j]=other` + + Use of negative indices is not supported. + + Deprecated since Python 2.0 but still a part of `UserList`. + """ + + def __delslice__(i, j): + """`x.__delslice__(i, j)` <==> `del x[i:j]` + + Use of negative indices is not supported. + + Deprecated since Python 2.0 but still a part of `UserList`. + """ + 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 file mode 100644 --- /dev/null +++ b/mercurial/thirdparty/zope/interface/declarations.py @@ -0,0 +1,929 @@ +############################################################################## +# 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.advice import addClassAdvisor +from zope.interface.interface import InterfaceClass +from zope.interface.interface import SpecificationBase +from zope.interface.interface import Specification +from zope.interface._compat import CLASS_TYPES as DescriptorAwareMetaClasses +from zope.interface._compat import PYTHON3 + +# Registry of class-implementation specifications +BuiltinImplementationSpecifications = {} + +_ADVICE_ERROR = ('Class advice impossible in Python3. ' + 'Use the @%s class decorator instead.') + +_ADVICE_WARNING = ('The %s API is deprecated, and will not work in Python3 ' + 'Use the @%s class decorator instead.') + +class named(object): + + def __init__(self, name): + self.name = name + + def __call__(self, ob): + ob.__component_name__ = self.name + return ob + +class Declaration(Specification): + """Interface declarations""" + + def __init__(self, *interfaces): + Specification.__init__(self, _normalizeargs(interfaces)) + + def changed(self, originally_changed): + Specification.changed(self, originally_changed) + try: + del self._v_attrs + except AttributeError: + pass + + 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)] + ] + ) + + def __add__(self, other): + """Add two specifications or a specification and an interface + """ + seen = {} + result = [] + for i in self.interfaces(): + seen[i] = 1 + result.append(i) + for i in other.interfaces(): + if i not in seen: + seen[i] = 1 + result.append(i) + + return Declaration(*result) + + __radd__ = __add__ + + +############################################################################## +# +# Implementation specifications +# +# These specify interfaces implemented by instances of classes + +class Implements(Declaration): + + # class whose specification should be used as additional base + inherit = None + + # interfaces actually declared for a class + declared = () + + __name__ = '?' + + @classmethod + def named(cls, name, *interfaces): + # 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__(*interfaces) + return inst + + def __repr__(self): + return '<implementedBy %s>' % (self.__name__) + + def __reduce__(self): + return implementedBy, (self.inherit, ) + + def __cmp(self, other): + # Yes, I did mean to name this __cmp, rather than __cmp__. + # It is a private method used by __lt__ and __gt__. + # This is based on, and compatible with, InterfaceClass. + # (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. + if other is None: + return -1 + + n1 = (self.__name__, self.__module__) + n2 = (getattr(other, '__name__', ''), getattr(other, '__module__', '')) + + # This spelling works under Python3, which doesn't have cmp(). + return (n1 > n2) - (n1 < n2) + + def __hash__(self): + return Declaration.__hash__(self) + + # We want equality 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 semantics being identity based. + + def __lt__(self, other): + c = self.__cmp(other) + return c < 0 + + def __le__(self, other): + c = self.__cmp(other) + return c <= 0 + + def __gt__(self, other): + c = self.__cmp(other) + return c > 0 + + def __ge__(self, other): + c = self.__cmp(other) + return c >= 0 + +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 implementedByFallback(cls): + """Return the interfaces implemented for a class' instances + + The value returned is an IDeclaration. + """ + try: + 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, DescriptorAwareMetaClasses) + 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 + +implementedBy = implementedByFallback + +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 (``IDeclaration`` objects). + + The interfaces given (including the interfaces in the specifications) + replace any previous declarations. + """ + spec = implementedBy(cls) + spec.declared = () + spec.inherit = None + classImplements(cls, *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 (``IDeclaration`` objects). + + The interfaces given (including the interfaces in the specifications) + are added to any interfaces previously declared. + """ + spec = implementedBy(cls) + spec.declared += tuple(_normalizeargs(interfaces)) + + # compute the bases + bases = [] + seen = {} + for b in spec.declared: + if b not in seen: + seen[b] = 1 + bases.append(b) + + if spec.inherit is not None: + + for c in spec.inherit.__bases__: + b = implementedBy(c) + if b not in seen: + seen[b] = 1 + bases.append(b) + + spec.__bases__ = tuple(bases) + +def _implements_advice(cls): + interfaces, classImplements = cls.__dict__['__implements_advice_data__'] + del cls.__implements_advice_data__ + 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 (IDeclaration objects). + + The interfaces given (including the interfaces in the + specifications) are added to any interfaces previously + declared. + + 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. + """ + + def __init__(self, *interfaces): + self.interfaces = interfaces + + def __call__(self, ob): + if isinstance(ob, DescriptorAwareMetaClasses): + 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 (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 pr function.... + raise ValueError('The implementer_only decorator is not ' + 'supported for methods or functions.') + else: + # Assume it's a class: + classImplementsOnly(ob, *self.interfaces) + return ob + +def _implements(name, interfaces, classImplements): + # This entire approach is invalid under Py3K. Don't even try to fix + # the coverage for this block there. :( + frame = sys._getframe(2) + locals = frame.f_locals + + # Try to make sure we were called from a class def. In 2.2.0 we can't + # check for __module__ since it doesn't seem to be added to the locals + # until later on. + if locals is frame.f_globals or '__module__' not in locals: + raise TypeError(name+" can be used only from a class definition.") + + if '__implements_advice_data__' in locals: + raise TypeError(name+" can be used only once in a class definition.") + + locals['__implements_advice_data__'] = interfaces, classImplements + addClassAdvisor(_implements_advice, depth=3) + +def implements(*interfaces): + """Declare interfaces implemented by instances of a class + + This function is called in a class definition. + + The arguments 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. + + 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:: + + implements(I1) + + is equivalent to calling:: + + classImplements(C, I1) + + after the class has been created. + """ + # This entire approach is invalid under Py3K. Don't even try to fix + # the coverage for this block there. :( + if PYTHON3: + raise TypeError(_ADVICE_ERROR % 'implementer') + _implements("implements", interfaces, classImplements) + +def implementsOnly(*interfaces): + """Declare the only interfaces implemented by instances of a class + + This function is called in a class definition. + + The arguments are one or more interfaces or interface + specifications (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:: + + implementsOnly(I1) + + is equivalent to calling:: + + classImplementsOnly(I1) + + after the class has been created. + """ + # This entire approach is invalid under Py3K. Don't even try to fix + # the coverage for this block there. :( + if PYTHON3: + raise TypeError(_ADVICE_ERROR % 'implementer_only') + _implements("implementsOnly", interfaces, classImplementsOnly) + +############################################################################## +# +# 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, *(interfaces + (implementedBy(cls), ))) + + def __reduce__(self): + 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): + """Cache instance declarations + + 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): + """Declare interfaces declared directly for an object + + The arguments after the object are one or more interfaces or interface + specifications (``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 Py3k tests: there is no normal + # class which isn't descriptor aware. + if not isinstance(object, + DescriptorAwareMetaClasses): + raise TypeError("Attempt to make an interface declaration on a " + "non-descriptor-aware class") + + interfaces = _normalizeargs(interfaces) + if cls is None: + cls = type(object) + + issub = False + for damc in DescriptorAwareMetaClasses: + if issubclass(cls, damc): + issub = True + break + if issub: + # we have a class or type. We'll use a special descriptor + # that provides some extra caching + object.__provides__ = ClassProvides(object, cls, *interfaces) + else: + object.__provides__ = Provides(cls, *interfaces) + + +def alsoProvides(object, *interfaces): + """Declare interfaces declared directly for an object + + The arguments after the object are one or more interfaces or interface + specifications (``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): + """ 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.") + +class ClassProvidesBaseFallback(object): + + def __get__(self, inst, cls): + 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__') + +ClassProvidesBasePy = ClassProvidesBaseFallback # BBB +ClassProvidesBase = ClassProvidesBaseFallback + +# Try to get C base: +try: + import zope.interface._zope_interface_coptimizations +except ImportError: + pass +else: + from zope.interface._zope_interface_coptimizations import ClassProvidesBase + + +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. + """ + def __init__(self, cls, metacls, *interfaces): + self._cls = cls + self._implements = implementedBy(cls) + self.__args = (cls, metacls, ) + interfaces + Declaration.__init__(self, *(interfaces + (implementedBy(metacls), ))) + + def __reduce__(self): + return self.__class__, self.__args + + # Copy base-class method for speed + __get__ = ClassProvidesBase.__get__ + +def directlyProvidedBy(object): + """Return the interfaces directly provided by the given object + + The value returned is an ``IDeclaration``. + """ + provides = getattr(object, "__provides__", None) + if (provides is None # no spec + or + # 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: + isinstance(provides, Implements) + ): + return _empty + + # Strip off the class part of the spec: + return Declaration(provides.__bases__[:-1]) + +def classProvides(*interfaces): + """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 + (``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:: + + classProvides(I1) + + is equivalent to calling:: + + directlyProvides(theclass, I1) + + after the class has been created. + """ + # This entire approach is invalid under Py3K. Don't even try to fix + # the coverage for this block there. :( + + if PYTHON3: + raise TypeError(_ADVICE_ERROR % 'provider') + + frame = sys._getframe(1) + locals = frame.f_locals + + # Try to make sure we were called from a class def + if (locals is frame.f_globals) or ('__module__' not in locals): + raise TypeError("classProvides can be used only from a " + "class definition.") + + if '__provides__' in locals: + raise TypeError( + "classProvides can only be used once in a class definition.") + + locals["__provides__"] = _normalizeargs(interfaces) + + addClassAdvisor(_classProvides_advice, depth=2) + +def _classProvides_advice(cls): + # This entire approach is invalid under Py3K. Don't even try to fix + # the coverage for this block there. :( + interfaces = cls.__dict__['__provides__'] + del cls.__provides__ + directlyProvides(cls, *interfaces) + return cls + +class provider: + """Class decorator version of classProvides""" + + 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 + (``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:: + + moduleImplements(I1) + + is equivalent to:: + + directlyProvides(sys.modules[__name__], I1) + """ + frame = sys._getframe(1) + locals = frame.f_locals + + # Try to make sure we were called from a class def + 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.") + + locals["__provides__"] = Provides(ModuleType, + *_normalizeargs(interfaces)) + +############################################################################## +# +# 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. +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 + +def getObjectSpecificationFallback(ob): + + provides = getattr(ob, '__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) + +getObjectSpecification = getObjectSpecificationFallback + +def providedByFallback(ob): + + # Here we have either a special object, an old-style declaration + # or a descriptor + + # Try to get __providedBy__ + try: + 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 +providedBy = providedByFallback + +class ObjectSpecificationDescriptorFallback(object): + """Implement the `__providedBy__` attribute + + The `__providedBy__` attribute computes the interfaces peovided by + an object. + """ + + def __get__(self, inst, cls): + """Get an object specification for an object + """ + if inst is None: + return getObjectSpecification(cls) + + provides = getattr(inst, '__provides__', None) + if provides is not None: + return provides + + return implementedBy(cls) + +ObjectSpecificationDescriptor = ObjectSpecificationDescriptorFallback + +############################################################################## + +def _normalizeargs(sequence, output = None): + """Normalize declaration arguments + + Normalization arguments might contain Declarions, tuples, or single + interfaces. + + Anything but individial 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 = Declaration() + +try: + import zope.interface._zope_interface_coptimizations +except ImportError: + pass +else: + from zope.interface._zope_interface_coptimizations import implementedBy + from zope.interface._zope_interface_coptimizations import providedBy + from zope.interface._zope_interface_coptimizations import ( + getObjectSpecification) + from zope.interface._zope_interface_coptimizations import ( + ObjectSpecificationDescriptor) + +objectSpecificationDescriptor = ObjectSpecificationDescriptor()
new file mode 100644 --- /dev/null +++ b/mercurial/thirdparty/zope/interface/document.py @@ -0,0 +1,120 @@ +############################################################################## +# +# 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 + + +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: "``%s``" % (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 = "%s -- %s" % (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 = "%s%s" % (desc.getName(), desc.getSignatureString()) + item = "%s -- %s" % (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)
new file mode 100644 --- /dev/null +++ b/mercurial/thirdparty/zope/interface/exceptions.py @@ -0,0 +1,67 @@ +############################################################################## +# +# 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 +""" + +class Invalid(Exception): + """A specification is violated + """ + +class DoesNotImplement(Invalid): + """ This object does not implement """ + def __init__(self, interface): + self.interface = interface + + def __str__(self): + return """An object does not implement interface %(interface)s + + """ % self.__dict__ + +class BrokenImplementation(Invalid): + """An attribute is not completely implemented. + """ + + def __init__(self, interface, name): + self.interface=interface + self.name=name + + def __str__(self): + return """An object has failed to implement interface %(interface)s + + The %(name)s attribute was not provided. + """ % self.__dict__ + +class BrokenMethodImplementation(Invalid): + """An method is not completely implemented. + """ + + def __init__(self, method, mess): + self.method=method + self.mess=mess + + def __str__(self): + return """The implementation of %(method)s violates its contract + because %(mess)s. + """ % self.__dict__ + +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. + """
new file mode 100644 --- /dev/null +++ b/mercurial/thirdparty/zope/interface/interface.py @@ -0,0 +1,686 @@ +############################################################################## +# +# 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 +""" +from __future__ import generators + +import sys +from types import MethodType +from types import FunctionType +import warnings +import weakref + +from zope.interface.exceptions import Invalid +from zope.interface.ro import ro + + +CO_VARARGS = 4 +CO_VARKEYWORDS = 8 +TAGGED_DATA = '__interface_tagged_values__' + +_decorator_non_return = 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(object): + + # We can't say this yet because we don't have enough + # infrastructure in place. + # + #implements(IElement) + + def __init__(self, __name__, __doc__=''): + """Create an 'attribute' description + """ + if not __doc__ and __name__.find(' ') >= 0: + __doc__ = __name__ + __name__ = None + + self.__name__=__name__ + self.__doc__=__doc__ + self.__tagged_values = {} + + def getName(self): + """ Returns the name of the object. """ + return self.__name__ + + def getDoc(self): + """ Returns the documentation for the object. """ + return self.__doc__ + + def getTaggedValue(self, tag): + """ Returns the value associated with '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) + + def getTaggedValueTags(self): + """ Returns a list of all tags. """ + return self.__tagged_values.keys() + + def setTaggedValue(self, tag, value): + """ Associates 'value' with 'key'. """ + self.__tagged_values[tag] = value + +class SpecificationBasePy(object): + + 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 + + __call__ = isOrExtends + +SpecificationBase = SpecificationBasePy +try: + from zope.interface._zope_interface_coptimizations import SpecificationBase +except ImportError: + pass + +_marker = object() +class InterfaceBasePy(object): + """Base class that wants to be replaced with a C base :) + """ + + def __call__(self, obj, alternate=_marker): + """Adapt an object to the interface + """ + conform = getattr(obj, '__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 + elif alternate is not _marker: + return alternate + else: + raise TypeError("Could not adapt", obj, self) + + def __adapt__(self, obj): + """Adapt an object to the reciever + """ + if self.providedBy(obj): + return obj + + for hook in adapter_hooks: + adapter = hook(self, obj) + if adapter is not None: + return adapter + + +InterfaceBase = InterfaceBasePy +try: + from zope.interface._zope_interface_coptimizations import InterfaceBase +except ImportError: + pass + + +adapter_hooks = [] +try: + from zope.interface._zope_interface_coptimizations import adapter_hooks +except ImportError: + pass + + +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. + """ + + # Copy some base class methods for speed + isOrExtends = SpecificationBase.isOrExtends + providedBy = SpecificationBase.providedBy + + def __init__(self, bases=()): + self._implied = {} + self.dependents = weakref.WeakKeyDictionary() + self.__bases__ = tuple(bases) + + def subscribe(self, dependent): + self.dependents[dependent] = self.dependents.get(dependent, 0) + 1 + + def unsubscribe(self, dependent): + n = self.dependents.get(dependent, 0) - 1 + if not n: + del self.dependents[dependent] + elif n > 0: + self.dependents[dependent] = n + else: + raise KeyError(dependent) + + def __setBases(self, bases): + # Register ourselves as a dependent of our old bases + for b in self.__bases__: + b.unsubscribe(self) + + # Register ourselves as a dependent of our bases + self.__dict__['__bases__'] = bases + for b in bases: + b.subscribe(self) + + self.changed(self) + + __bases__ = property( + + lambda self: self.__dict__.get('__bases__', ()), + __setBases, + ) + + def changed(self, originally_changed): + """We, or something we depend on, have changed + """ + try: + del self._v_attrs + except AttributeError: + pass + + implied = self._implied + implied.clear() + + ancestors = ro(self) + + try: + if Interface not in ancestors: + ancestors.append(Interface) + except NameError: + pass # defining Interface itself + + 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: + for dependent in tuple(self.dependents.keys()): + dependent.changed(originally_changed) + + + 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 + """ + try: + attrs = self._v_attrs + except AttributeError: + 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 + + if attr is None: + return default + else: + return attr + +class InterfaceClass(Element, InterfaceBase, Specification): + """Prototype (scarecrow) Interfaces Implementation.""" + + # We can't say this yet because we don't have enough + # infrastructure in place. + # + #implements(IInterface) + + def __init__(self, name, bases=(), attrs=None, __doc__=None, + __module__=None): + + 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 + + self.__module__ = __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) + + for base in bases: + if not isinstance(base, InterfaceClass): + raise TypeError('Expected base interfaces') + + Specification.__init__(self, bases) + + # Make sure that all recorded attributes (and methods) are of type + # `Attribute` and `Method` + for name, attr in list(attrs.items()): + if name in ('__locals__', '__qualname__', '__annotations__'): + # __locals__: Python 3 sometimes adds this. + # __qualname__: PEP 3155 (Python 3.3+) + # __annotations__: PEP 3107 (Python 3.0+) + del attrs[name] + continue + if isinstance(attr, Attribute): + attr.interface = self + if not attr.__name__: + attr.__name__ = name + elif isinstance(attr, FunctionType): + attrs[name] = fromFunction(attr, self, name=name) + elif attr is _decorator_non_return: + del attrs[name] + else: + raise InvalidInterface("Concrete attribute, " + name) + + self.__attrs = attrs + + self.__identifier__ = "%s.%s" % (self.__module__, self.__name__) + + 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): + """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): + """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 call in self.queryTaggedValue('invariants', []): + try: + call(obj) + except Invalid as e: + if errors is None: + raise + else: + errors.append(e) + for base in self.__bases__: + try: + base.validateInvariants(obj, errors) + except Invalid: + if errors is None: + raise + if errors: + raise Invalid(errors) + + def __repr__(self): # pragma: no cover + try: + return self._v_repr + except AttributeError: + name = self.__name__ + m = self.__module__ + if m: + name = '%s.%s' % (m, name) + r = "<%s %s>" % (self.__class__.__name__, name) + self._v_repr = r + return r + + 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__ + + def __cmp(self, other): + # Yes, I did mean to name this __cmp, rather than __cmp__. + # It is a private method used by __lt__ and __gt__. + # I don't want to override __eq__ because I want the default + # __eq__, which is really fast. + """Make interfaces sortable + + TODO: It would ne nice if: + + More specific interfaces should sort before less specific ones. + Otherwise, sort on name and module. + + But this is too complicated, and we're going to punt on it + for now. + + For now, sort on interface and module name. + + 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 None: + return -1 + + n1 = (getattr(self, '__name__', ''), getattr(self, '__module__', '')) + n2 = (getattr(other, '__name__', ''), getattr(other, '__module__', '')) + + # This spelling works under Python3, which doesn't have cmp(). + return (n1 > n2) - (n1 < n2) + + def __hash__(self): + d = self.__dict__ + if '__module__' not in d or '__name__' not in d: # pragma: no cover + warnings.warn('Hashing uninitialized InterfaceClass instance') + return 1 + return hash((self.__name__, self.__module__)) + + def __eq__(self, other): + c = self.__cmp(other) + return c == 0 + + def __ne__(self, other): + c = self.__cmp(other) + return c != 0 + + def __lt__(self, other): + c = self.__cmp(other) + return c < 0 + + def __le__(self, other): + c = self.__cmp(other) + return c <= 0 + + def __gt__(self, other): + c = self.__cmp(other) + return c > 0 + + def __ge__(self, other): + c = self.__cmp(other) + return c >= 0 + + +Interface = InterfaceClass("Interface", __module__ = 'zope.interface') + +class Attribute(Element): + """Attribute descriptions + """ + + # We can't say this yet because we don't have enough + # infrastructure in place. + # + # implements(IAttribute) + + interface = None + + +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) + +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 + nr = na-len(defaults) + 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 zope.interface.interfaces import IAttribute + classImplements(Attribute, IAttribute) + + from zope.interface.interfaces import IMethod + classImplements(Method, IMethod) + + from zope.interface.interfaces import IInterface + classImplements(InterfaceClass, IInterface) + + from zope.interface.interfaces import ISpecification + classImplements(Specification, ISpecification) + +# We import this here to deal with module dependencies. +from zope.interface.declarations import implementedBy +from zope.interface.declarations import providedBy +from zope.interface.exceptions import InvalidInterface +from zope.interface.exceptions import BrokenImplementation
new file mode 100644 --- /dev/null +++ b/mercurial/thirdparty/zope/interface/interfaces.py @@ -0,0 +1,1294 @@ +############################################################################## +# +# 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 + + +_BLANK = u'' + +class IElement(Interface): + """Objects that have basic documentation and tagged values. + """ + + __name__ = Attribute('__name__', 'The object name') + __doc__ = Attribute('__doc__', 'The object doc string') + + def getTaggedValue(tag): + """Returns the value associated with `tag`. + + Raise a `KeyError` of the tag isn't set. + """ + + def queryTaggedValue(tag, default=None): + """Returns the value associated with `tag`. + + Return the default value of the tag isn't set. + """ + + def getTaggedValueTags(): + """Returns a list of all tags.""" + + def setTaggedValue(tag, value): + """Associates `value` with `key`.""" + + +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 keys: + + o `positional` - All positional arguments. + + o `required` - A list of all required arguments. + + o `optional` - A list of all optional arguments. + + o `varargs` - The name of the varargs argument. + + o `kwargs` - The name of the kwargs argument. + """ + + def getSignatureString(): + """Return a signature string suitable for inclusion in documentation. + + This method returns the function signature string. For example, if you + have `func(a, b, c=1, d='f')`, then the signature string is `(a, b, + c=1, d='f')`. + """ + +class ISpecification(Interface): + """Object Behavioral specifications""" + + def providedBy(object): + """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 if 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. + + (This is similar to the method-resolution order for new-style classes.) + """) + + __iro__ = Attribute("""Interface-resolution order + + A tuple of the 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: + + o 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. + + o Descriptions of attributes. Attribute descriptions include + the name of the attribute and prose documentation describing + the attributes usage. + + o 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. + + o 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 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: + + o You assert that your object implement the interfaces. + + There are several ways that you can assert that an object + implements an interface: + + 1. Call zope.interface.implements in your class definition. + + 2. Call zope.interfaces.directlyProvides on your object. + + 3. Call 'zope.interface.classImplements' to assert 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. + + o You query interface meta-data. See the IInterface methods and + attributes for details. + + """ + + def names(all=False): + """Get the interface attribute names + + Return a sequence 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): + """Get the interface attribute names and descriptions + + Return a sequence 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. + """ + + 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. + """ + + def implementedBy(class_): + """Return the interfaces implemented for a class' instances + + The value returned is an IDeclaration. + """ + + 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. + """ + + def implementer(*interfaces): + """Create a decorator for declaring interfaces implemented by a facory + + A callable is returned that makes an implements declaration on + objects passed to it. + """ + + 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. + """ + + 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. + """ + + def directlyProvidedBy(object): + """Return the interfaces directly provided by the given object + + The value returned is an IDeclaration. + """ + + def directlyProvides(object, *interfaces): + """Declare interfaces declared directly for an object + + The arguments after the object are one or more interfaces or + interface specifications (IDeclaration objects). + + The interfaces given (including the interfaces in the + specifications) replace interfaces previously + declared for the object. + + 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. + """ + + def alsoProvides(object, *interfaces): + """Declare additional interfaces directly for an object:: + + alsoProvides(ob, I1) + + is equivalent to:: + + directlyProvides(ob, directlyProvidedBy(ob), I1) + """ + + def noLongerProvides(object, interface): + """Remove an interface from the list of an object's directly + provided interfaces:: + + 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. + """ + + def implements(*interfaces): + """Declare interfaces implemented by instances of a class + + This function is called in a class definition (Python 2.x only). + + The arguments 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. + + 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:: + + implements(I1) + + is equivalent to calling:: + + classImplements(C, I1) + + after the class has been created. + + Consider the following example (Python 2.x only):: + + class C(A, B): + implements(I1, I2) + + + Instances of ``C`` implement ``I1``, ``I2``, and whatever interfaces + instances of ``A`` and ``B`` implement. + """ + + def implementsOnly(*interfaces): + """Declare the only interfaces implemented by instances of a class + + This function is called in a class definition (Python 2.x only). + + The arguments are one or more interfaces or interface + specifications (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:: + + implementsOnly(I1) + + is equivalent to calling:: + + classImplementsOnly(I1) + + after the class has been created. + + Consider the following example (Python 2.x only):: + + class C(A, B): + implementsOnly(I1, I2) + + + Instances of ``C`` implement ``I1``, ``I2``, regardless of what + instances of ``A`` and ``B`` implement. + """ + + def classProvides(*interfaces): + """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 (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:: + + classProvides(I1) + + is equivalent to calling:: + + directlyProvides(theclass, I1) + + after the class has been created. + """ + def provider(*interfaces): + """A class decorator version of classProvides""" + + 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) + """ + + 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. + """ + +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=_BLANK): + """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=_BLANK, default=None): + """Adapt a sequence of objects to a named, provided, interface + """ + + def lookup1(required, provided, name=_BLANK, 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=_BLANK, default=None): + """Adapt an object using a registered adapter factory. + """ + + def adapter_hook(provided, object, name=_BLANK, default=None): + """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): + """Return the names for which there are registered objects + """ + + def subscribe(required, provided, subscriber, name=_BLANK): + """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. + """ + + def subscriptions(required, provided, name=_BLANK): + """Get a sequence of subscribers + + Subscribers for a *sequence* of required interfaces, and a provided + interface are returned. + """ + + def subscribers(objects, provided, name=_BLANK): + """Get a sequence of subscription adapters + """ + +# 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 + refered to by location. + """ + + object = Attribute("The subject of the event.") + + +@implementer(IObjectEvent) +class ObjectEvent(object): + + def __init__(self, object): + 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=_BLANK, default=None): + """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=_BLANK): + """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=_BLANK, 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=_BLANK): + """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 comuted 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 "%s event:\n%r" % (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 + """ + pass + +class IComponentRegistry(Interface): + """Register components + """ + + def registerUtility(component=None, provided=None, name=_BLANK, + info=_BLANK, factory=None): + """Register a utility + + factory + Factory for the component to be registerd. + + component + The registered component + + 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. + + name + The utility name. + + 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 Registered event is generated with an IUtilityRegistration. + """ + + def unregisterUtility(component=None, provided=None, name=_BLANK, + factory=None): + """Unregister a utility + + 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. + + factory + Factory for the component to be unregisterd. + + 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. + + 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. + + name + The utility name. + + Only one of component and factory can be used. + An UnRegistered 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=_BLANK, + info=_BLANK): + """Register an adapter factory + + Parameters: + + factory + The object used to compute the adapter + + 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 usually attribute is + normally set in class definitions using adapts + function, or for callables using the adapter + decorator. If the factory doesn't have a + __component_adapts__ adapts attribute, then this + argument is required. + + 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. + + name + The adapter name. + + info + An object that can be converted to a string to provide + information about the registration. + + A Registered event is generated with an IAdapterRegistration. + """ + + def unregisterAdapter(factory=None, required=None, + provided=None, name=_BLANK): + """Unregister an adapter factory + + 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. + + Parameters: + + 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. + + 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. + + 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. + + name + The adapter name. + + An Unregistered 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=_BLANK, info=''): + """Register a subscriber factory + + Parameters: + + factory + The object used to compute the adapter + + 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 usually attribute is + normally set in class definitions using adapts + function, or for callables using the adapter + decorator. If the factory doesn't have a + __component_adapts__ adapts attribute, then this + argument is required. + + 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. + + 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. + + info + An object that can be converted to a string to provide + information about the registration. + + A Registered event is generated with an + ISubscriptionAdapterRegistration. + """ + + def unregisterSubscriptionAdapter(factory=None, required=None, + provides=None, name=_BLANK): + """Unregister a subscriber factory. + + 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. + + Parameters: + + 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. + + 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. + + 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. + + 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 Unregistered 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=_BLANK, info=''): + """Register a handler. + + A handler is a subscriber that doesn't compute an adapter + but performs some function when called. + + Parameters: + + handler + The object used to handle some event represented by + the objects passed to it. + + 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 usually attribute is + normally set in class definitions using adapts + function, or for callables using the adapter + decorator. If the factory doesn't have a + __component_adapts__ adapts attribute, then this + argument is required. + + 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. + + info + An object that can be converted to a string to provide + information about the registration. + + + A Registered event is generated with an IHandlerRegistration. + """ + + def unregisterHandler(handler=None, required=None, name=_BLANK): + """Unregister a handler. + + A handler is a subscriber that doesn't compute an adapter + but performs some function when called. + + A boolean is returned indicating whether the registry was + changed. + + Parameters: + + 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. + + 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 usually attribute is + normally set in class definitions using adapts + function, or for callables using the adapter + decorator. If the factory doesn't have a + __component_adapts__ adapts attribute, then this + argument is required. + + 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 Unregistered 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
new file mode 100644 --- /dev/null +++ b/mercurial/thirdparty/zope/interface/registry.py @@ -0,0 +1,654 @@ +############################################################################## +# +# 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 +from zope.interface._compat import CLASS_TYPES +from zope.interface._compat import STRING_TYPES + + +class _UnhashableComponentCounter(object): + # 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(object): + + 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(object): + + _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, STRING_TYPES) + self.__name__ = name + self._init_registries() + self._init_registrations() + self.__bases__ = tuple(bases) + self._v_utility_registrations_cache = None + + def __repr__(self): + return "<%s %s>" % (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(Components, self).__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=u'', + info=u'', 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 == u'': + 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=u'', + 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=u'', default=None): + return self.utilities.lookup((), provided, name, default) + + def getUtility(self, provided, name=u''): + utility = self.utilities.lookup((), provided, name) + if utility is None: + raise ComponentLookupError(provided, name) + return utility + + def getUtilitiesFor(self, interface): + for name, utility in self.utilities.lookupAll((), interface): + yield name, utility + + def getAllUtilitiesRegisteredFor(self, interface): + return self.utilities.subscriptions((), interface) + + def registerAdapter(self, factory, required=None, provided=None, + name=u'', info=u'', event=True): + if provided is None: + provided = _getAdapterProvided(factory) + required = _getAdapterRequired(factory, required) + if name == u'': + 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=u'', + ): + 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=u'', default=None): + return self.adapters.queryAdapter(object, interface, name, default) + + def getAdapter(self, object, interface, name=u''): + adapter = self.adapters.queryAdapter(object, interface, name) + if adapter is None: + raise ComponentLookupError(object, interface, name) + return adapter + + def queryMultiAdapter(self, objects, interface, name=u'', + default=None): + return self.adapters.queryMultiAdapter( + objects, interface, name, default) + + def getMultiAdapter(self, objects, interface, name=u''): + 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=u'', info=u'', + 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=u'', + ): + 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=u'', info=u'', + 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=u''): + 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 _getName(component): + try: + return component.__component_name__ + except AttributeError: + return u'' + +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, CLASS_TYPES): + r = implementedBy(r) + else: + raise TypeError("Required specification must be a " + "specification or class." + ) + result.append(r) + return tuple(result) + + +@implementer(IUtilityRegistration) +class UtilityRegistration(object): + + 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 '%s(%r, %s, %r, %s, %r, %r)' % ( + 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(object): + + 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 '%s(%r, %s, %s, %r, %s, %r)' % ( + 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 '%s(%r, %s, %r, %s, %r)' % ( + self.__class__.__name__, + self.registry, + '[' + ", ".join([r.__name__ for r in self.required]) + ']', + self.name, + getattr(self.factory, '__name__', repr(self.factory)), self.info, + )
new file mode 100644 --- /dev/null +++ b/mercurial/thirdparty/zope/interface/ro.py @@ -0,0 +1,64 @@ +############################################################################## +# +# 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 +""" +__docformat__ = 'restructuredtext' + +def _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 = {} + result = [] + for ordering in reversed(orderings): + for o in reversed(ordering): + if o not in seen: + seen[o] = 1 + result.insert(0, o) + + return result + +def _flatten(ob): + result = [ob] + 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 ro(object): + """Compute a "resolution order" for an object + """ + return _mergeOrderings([_flatten(object)])
new file mode 100644 --- /dev/null +++ b/mercurial/thirdparty/zope/interface/verify.py @@ -0,0 +1,120 @@ +############################################################################## +# +# 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 +""" +from zope.interface.exceptions import BrokenImplementation, DoesNotImplement +from zope.interface.exceptions import BrokenMethodImplementation +from types import FunctionType, MethodType +from zope.interface.interface import fromMethod, fromFunction, Method +import sys + +# This will be monkey-patched when running under Zope 2, so leave this +# here: +MethodTypes = (MethodType, ) + + +def _verify(iface, candidate, tentative=0, vtype=None): + """Verify that 'candidate' might correctly implements 'iface'. + + This involves: + + o Making sure the candidate defines all the necessary methods + + o Making sure the methods have the correct signature + + o Making sure the candidate asserts that it implements the interface + + Note that this isn't the same as verifying that the class does + implement the interface. + + If optional tentative is true, suppress the "is implemented by" test. + """ + + if vtype == 'c': + tester = iface.implementedBy + else: + tester = iface.providedBy + + if not tentative and not tester(candidate): + raise DoesNotImplement(iface) + + # Here the `desc` is either an `Attribute` or `Method` instance + for name, desc in iface.namesAndDescriptions(1): + 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__. + continue + + raise BrokenImplementation(iface, name) + + if not isinstance(desc, Method): + # If it's not a method, there's nothing else we can test + continue + + if isinstance(attr, FunctionType): + if sys.version_info[0] >= 3 and isinstance(candidate, type): + # This is an "unbound method" in Python 3. + 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': + # We without an instance we cannot be sure it's not a + # callable. + continue + else: + if not callable(attr): + raise BrokenMethodImplementation(name, "Not a method") + # sigh, it's callable, but we don't know how to introspect it, so + # we have to give it a pass. + continue + + # Make sure that the required and implemented method signatures are + # the same. + desc = desc.getSignatureInfo() + meth = meth.getSignatureInfo() + + mess = _incompat(desc, meth) + if mess: + raise BrokenMethodImplementation(name, mess) + + return True + +def verifyClass(iface, candidate, tentative=0): + return _verify(iface, candidate, tentative, vtype='c') + +def verifyObject(iface, candidate, tentative=0): + return _verify(iface, candidate, tentative, vtype='o') + +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 'implementation requires too many arguments' + 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"