[Zodb-checkins] CVS: Zope3/src/zope/interface - ro.py:1.1.2.1 surrogate.py:1.1.2.1 declarations.py:1.17.10.1 interface.py:1.13.4.1

Jim Fulton jim at zope.com
Fri Oct 10 07:17:09 EDT 2003


Update of /cvs-repository/Zope3/src/zope/interface
In directory cvs.zope.org:/tmp/cvs-serv8781/zope/interface

Modified Files:
      Tag: adaptergeddon-branch
	declarations.py interface.py 
Added Files:
      Tag: adaptergeddon-branch
	ro.py surrogate.py 
Log Message:
Committing some initial work on the adaptergeddon branch to facilitate
colaboration.



=== Added File Zope3/src/zope/interface/ro.py ===
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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 it's bases

$Id: ro.py,v 1.1.2.1 2003/10/10 11:16:38 jim Exp $
"""

def ro(object):
    """Compute a "resolution order" for an object
    """
    return mergeOrderings([_flatten(object, [])])

def mergeOrderings(orderings, seen=None):
    """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']

    """

    if seen is None:
        seen = {}
    result = []
    orderings.reverse()
    for ordering in orderings:
        ordering = list(ordering)
        ordering.reverse()
        for o in ordering:
            if o not in seen:
                seen[o] = 1
                result.append(o)

    result.reverse()
    return result

def _flatten(ob, result):
    result.append(ob)
    for base in ob.__bases__:
        _flatten(base, result)

    return result


=== Added File Zope3/src/zope/interface/surrogate.py ===
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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.
#
##############################################################################
"""Surrogate-interface registry implementation

$Id: surrogate.py,v 1.1.2.1 2003/10/10 11:16:38 jim Exp $
"""

# Optimization notes

# For each surrogate, we store a dict {key -> factories}

# where key is one of
#  provides,
#  provided, name
#  provided, name, order

# Currently, we stora all of the keys in a single dict:

# consider named adapters. Each key is a two-tuple.
# That's 5 extra words per key for the tuple.

# suppose that the dict was of the form:
#   {name -> {provided -> factroies}

# This saves n-1 * 5 words, where n is the number of entries for a
# given name. There is the overhead of an extra dictionary per
# word. Given that many interfaces only have a single entry, this
# extra overhead could be significant, however, there are some extra
# opportunities for savings. See below. There are two dict lookups,
# but, possibly, each lookup is cheaper, because the cache and
# comparisons are cheaper.

# It will often be the case that an implied dict (for a required
# interface and name (and order), will be equal to the implied dict
# for one of the interfaces bases. This will happen, for example, when
# there aren't adapters registered for an interface and the interface
# has one base.  In a case like this, the interface can share the
# implied dict with the base.

# Additional savings will be permitted for local surrogates.  For
# example, it will often be the case that the implied dictionary
# for a local surrogate will be the same as the implied dictionary for
# it's base (less local) surogate. In this case too, the dictionary
# can be saved.


import weakref
from zope.interface.ro import ro
from zope.interface.declarations import providedBy

class Surrogate(object):

    def __init__(self, interface, registry):
        self._adapters = {}
        self._implied = {}
        self._interface = interface
        interface.addDependent(self)

        self._registry = registry
        self.dependents = weakref.WeakKeyDictionary()
        self.__bases__ = [registry.get(base) for base in interface.__bases__]
        for base in self.__bases__:
            base.dependents[self] = 1
        self._computeImplied()

    def extends(self, other, strict=True):
        if other is self and strict:
            return False
        
        i = self._implied.get(other)
        if i is not None and not i:
            return True
        return False

    def isImplementedBy(self, ob):
        return self._interface.isImplementedBy(ob)

    def _adaptTo(self, specification, factory, name=None, with=()):
        self._adapters[with, name, specification] = factory
        self.changed()

    def changed(self):
        self._computeImplied()
        for dependent in self.dependents:
            dependent.changed()
        
    def _computeImplied(self):
        
        implied = self._implied
        implied.clear()

        ancestors = ro(self)


        for ancestor in ancestors:
            # We directly imply our ancestors:
            implied[ancestor] = ()

            # Work dict used to keep track of the registered interface
            # leading to an implied entry. This is so that we can can
            # override an implied entry of we get a closer "provided"
            # fit.
            registered = {}

            # Add adapters and interfaces directly implied by same:
            items = ancestor._adapters.iteritems()
            for (with, name, target), factory in items:
                if with:
                    self.__add_multi_adapter(with, name, target, target,
                                             implied, registered,
                                             (factory, ))
                elif name is not None:
                    self.__add_named_adapter(target, target, name,
                                             implied, registered,
                                             (factory, ))
                else:
                    self.__add_adapter(target, target,
                                       implied, registered,
                                       (factory, ))


    def __add_adapter(self, target, provided, implied, registered, factories):
        if (target not in implied
            or
            (target in registered and registered[target].extends(provided))
            ):
            registered[target] = provided
            implied[target] = factories
            for b in target.__bases__:
                self.__add_adapter(b, provided, implied, registered, factories)

    def __add_named_adapter(self, target, provided, name,
                            implied, registered, factories):
        key = target, name
        if (key not in implied
            or
            (key in registered and registered[key].extends(provided))
            ):
            registered[key] = provided
            implied[key] = factories
            for b in target.__bases__:
                self.__add_named_adapter(b, provided, name,
                                         implied, registered, factories)

    def __add_multi_adapter(self, interfaces, name, target, provided,
                            implied, registered, factories):

        order = len(interfaces)+1
        adapters = implied.get((target, name, order))
        if adapters is None:
            adapters = {}
            implied[(target, name, order)] = adapters

        adapters.setdefault(interfaces, factories)
        
        for b in target.__bases__:
            self.__add_multi_adapter(interfaces, name, b, provided,
                                     implied, registered, factories)

class SurrogateRegistry(object):

    def __init__(self):
        self._surrogates = weakref.WeakKeyDictionary()


    def get(self, declaration):
        surrogate = self._surrogates.get(declaration)
        if surrogate is None:
            surrogate = Surrogate(declaration, self)
            self._surrogates[declaration] = surrogate
        return surrogate

    def provideAdapter(self, required, provided, factory,
                       name=None, with=()):
        required = self.get(required)
        provided = self.get(provided)
        if with:
            with = tuple(map(self.get, with))
        else:
            with = ()
        required._adaptTo(provided, factory, name, with)

    def queryAdapter(self, ob, interface, default=None):
        """Query a simple adapter

        >>> import zope.interface
        >>> class F0(zope.interface.Interface):
        ...     pass
        >>> class F1(F0):
        ...     pass

        >>> class C:
        ...     zope.interface.implements(F1)
        >>> c = C()

        >>> registry = SurrogateRegistry()

        When we adapt an object to an interface it implements, we get
        the object back:

        >>> registry.queryAdapter(c, F0) is c
        1

        But adapting to some other interface returns the default:
            
        >>> class B0(zope.interface.Interface):
        ...     pass
        >>> class B1(B0):
        ...     pass

        >>> registry.queryAdapter(c, B0)
        >>> registry.queryAdapter(c, B0, 42)
        42

        Unless we define an adapter:

        >>> def f1(ob):
        ...     return 1

        >>> registry.provideAdapter(F0, B1, f1)
        >>> registry.queryAdapter(c, B0)
        1

        If we define a more specific adapter (for F1), we'll get that:

        >>> def f2(ob):
        ...     return 2

        >>> registry.provideAdapter(F1, B1, f2)
        >>> registry.queryAdapter(c, B0)
        2
        
        >>> def f3(ob):
        ...     return 3

        >>> registry.provideAdapter(F1, B0, f3)
        >>> registry.queryAdapter(c, B0)
        3


        """
        
        surrogates = self._surrogates
        
        surrogate = surrogates.get(interface)
        if surrogate is None:
            # If there is no surrogate for the interface, then we can't
            # have an adapter for it.

            # But we may implement it
            if interface.isImplementedBy(ob):
                return ob
            return default
        
        interface = surrogate
    
        for declaration in providedBy(ob):
            s = surrogates.get(declaration)
            if s is None:
                s = self.get(declaration)

            factories = s._implied.get(interface)
            if factories is not None:
                for factory in factories:
                    ob = factory(ob)
                return ob

        return default

    def queryNamedAdapter(self, ob, interface, name, default=None):
        """Query a named simple adapter

        >>> import zope.interface
        >>> class F0(zope.interface.Interface):
        ...     pass
        >>> class F1(F0):
        ...     pass

        >>> class C:
        ...     zope.interface.implements(F1)
        >>> c = C()

        >>> registry = SurrogateRegistry()

        If we ask for a named adapter, we won't get a result unless there
        is a named adapter, even if the object implements the interface:

        >>> registry.queryNamedAdapter(c, F0, 'bob')
            
        >>> class B0(zope.interface.Interface):
        ...     pass
        >>> class B1(B0):
        ...     pass


        >>> def f1(ob):
        ...     return 1

        >>> registry.provideAdapter(F0, B1, f1, name='bob')
        >>> registry.queryNamedAdapter(c, B0, 'bob')
        1
        >>> registry.queryNamedAdapter(c, B0, 'bruce')
        

        >>> def f2(ob):
        ...     return 2

        >>> registry.provideAdapter(F1, B1, f2, name='bob')
        >>> registry.queryNamedAdapter(c, B0, 'bob')
        2
        
        >>> def f3(ob):
        ...     return 3

        >>> registry.provideAdapter(F1, B0, f3, name='bob')
        >>> registry.queryNamedAdapter(c, B0, 'bob')
        3


        """
        surrogates = self._surrogates
        
        interface = surrogates.get(interface)
        if interface is None:
            # If there is no surrogate for the interface, then we can't
            # have an adapter for it.
            return default
    
        for declaration in providedBy(ob):
            s = surrogates.get(declaration)
            if s is None:
                s = self.get(declaration)

            factories = s._implied.get((interface, name))
            if factories is not None:
                for factory in factories:
                    ob = factory(ob)
                return ob

        return default

    def queryMultiAdapter(self, objects, interface, name):
        """

        >>> import zope.interface
        >>> class IF0(zope.interface.Interface):
        ...     pass
        >>> class IF1(IF0):
        ...     pass

        >>> class IR0(zope.interface.Interface):
        ...     pass
        >>> class IR1(IR0):
        ...     pass

        >>> class F1:
        ...     zope.interface.implements(IF1)
        >>> c = F1()

        >>> class R1:
        ...     zope.interface.implements(IR1)
        >>> r = R1()

        >>> registry = SurrogateRegistry()

        If we ask for a named adapter, we won't get a result unless there
        is a named adapter, even if the object implements the interface:

        >>> registry.queryMultiAdapter((c, r), IF0, 'bob')

        >>> class IB0(zope.interface.Interface):
        ...     pass
        >>> class IB1(IB0):
        ...     pass


        >>> def f1(ob):
        ...     return 1

        >>> registry.provideAdapter(IF0, IB1, f1, name='bob', with=[IR0])
        >>> registry.queryMultiAdapter((c, r), IB0, 'bob')
        1
        >>> registry.queryMultiAdapter((c, r), IB0, 'bruce')


        >>> def f2(ob):
        ...     return 2

        >>> registry.provideAdapter(IF1, IB1, f2, name='bob', with=[IR1])
        >>> registry.queryMultiAdapter((c, r), IB0, 'bob')
        2
        
        >>> def f3(ob):
        ...     return 3

        """
        surrogates = self._surrogates
        
        interface = surrogates.get(interface)
        if interface is None:
            # If there is no surrogate for the interface, then we can't
            # have an adapter for it.
            return None

        ob = objects[0]
        order = len(objects)
        objects = objects[1:]
        for declaration in providedBy(ob):
            s = surrogates.get(declaration)
            if s is None:
                s = self.get(declaration)

            adapters = s._implied.get((interface, name, order))
            if adapters:
                matched = None
                matched_factories = None
                for interfaces, factories in adapters.iteritems():
                    for iface, ob in zip(interfaces, objects):
                        if not iface.isImplementedBy(ob):
                            break # This one is no good
                    else:
                        # we didn't break, so we have a match
                        if matched is None:
                            matched = interfaces
                            matched_factories = factories
                        else:
                            for iface, m in zip(interfaces, matched):
                                if iface.extends(m):
                                    # new is better than old
                                    matched = interfaces
                                    matched_factories = factories
                                    break
                                elif m.extends(iface):
                                    # old is better than new
                                    break                

                for factory in matched_factories:
                    ob = factory(ob)
                return ob

        return None
        
        


=== Zope3/src/zope/interface/declarations.py 1.17 => 1.17.10.1 ===
--- Zope3/src/zope/interface/declarations.py:1.17	Fri Aug 15 20:44:44 2003
+++ Zope3/src/zope/interface/declarations.py	Fri Oct 10 07:16:38 2003
@@ -15,7 +15,8 @@
 """
 
 import sys
-from zope.interface.interface import InterfaceClass, mergeOrderings
+from zope.interface.interface import InterfaceClass
+from ro import mergeOrderings
 import exceptions
 from types import ClassType
 from zope.interface.advice import addClassAdvisor


=== Zope3/src/zope/interface/interface.py 1.13 => 1.13.4.1 ===
--- Zope3/src/zope/interface/interface.py:1.13	Fri Oct  3 16:36:30 2003
+++ Zope3/src/zope/interface/interface.py	Fri Oct 10 07:16:38 2003
@@ -18,7 +18,9 @@
 """
 
 import sys
+import weakref
 from types import FunctionType
+from ro import ro
 
 CO_VARARGS = 4
 CO_VARKEYWORDS = 8
@@ -113,7 +115,7 @@
 
         Element.__init__(self, name, __doc__)
 
-        self.__iro__ = mergeOrderings([_flattenInterface(self, [])])
+        self.__iro__ = ro(self)
 
         for k, v in attrs.items():
             if isinstance(v, Attribute):
@@ -129,6 +131,10 @@
 
         self.__identifier__ = "%s.%s" % (self.__module__, self.__name__)
 
+        self._dependents = weakref.WeakKeyDictionary()
+
+    def addDependent(self, ob):
+        self._dependents[ob] = 1
 
     def getBases(self):
         return self.__bases__
@@ -328,48 +334,6 @@
         c = self.__cmp(self, other)
         #print '>', self, other, c > 0, c
         return c > 0
-
-
-def mergeOrderings(orderings, seen=None):
-    """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']
-
-    """
-
-    if seen is None:
-        seen = {}
-    result = []
-    orderings.reverse()
-    for ordering in orderings:
-        ordering = list(ordering)
-        ordering.reverse()
-        for o in ordering:
-            if o not in seen:
-                seen[o] = 1
-                result.append(o)
-
-    result.reverse()
-    return result
-
-def _flattenInterface(iface, result):
-    result.append(iface)
-    for base in iface.__bases__:
-        _flattenInterface(base, result)
-
-    return result
 
 Interface = InterfaceClass("Interface", __module__ = 'zope.interface')
 




More information about the Zodb-checkins mailing list