[Zodb-checkins] CVS: Zope3/src/zope/interface - adapter.py:1.9 interface.py:1.24 interfaces.py:1.22 surrogate.py:NONE

Jim Fulton jim at zope.com
Mon Mar 8 12:27:28 EST 2004


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

Modified Files:
	adapter.py interface.py interfaces.py 
Removed Files:
	surrogate.py 
Log Message:
Removed the old adapter registry code.

Renamed the surrogate registry to AdapterRegistry and moved the
surrogate code to zope.interface.adapter.

Removed the old surrogate module.


=== Zope3/src/zope/interface/adapter.py 1.8 => 1.9 ===
--- Zope3/src/zope/interface/adapter.py:1.8	Fri Mar  5 17:09:28 2004
+++ Zope3/src/zope/interface/adapter.py	Mon Mar  8 12:26:56 2004
@@ -13,200 +13,1131 @@
 ##############################################################################
 """Adapter-style interface registry
 
-See Adapter class.
+This implementationb is based on a notion of "surrogate" interfaces.
 
 $Id$
 """
-__metaclass__ = type # All classes are new style when run with Python 2.2+
 
-from zope.interface import Interface, implements, providedBy
-from zope.interface import Declaration
-from zope.interface.interfaces import IInterface
-from zope.interface.interfaces import IAdapterRegistry
-from _flatten import _flatten
 
-class AdapterRegistry:
-    """Adapter-style interface registry
+# Implementation notes
+
+# We keep a collection of surrogates.
+
+# A surrogate is a surrogate for a specification (interface or
+# declaration).  We use weak references in order to remove surrogates
+# if the corresponding specification goes away.
+
+# Each surrogate keeps track of:
+
+# - The adapters registered directly for that surrogate, and
+
+# - The "implied" adapters, which is the adapters that can be computed
+#   from instances of that surrogate.
+
+# The later data structure takes into account adapters registered for
+# specifications that the registered surrogate extends.
+
+# The registrations are of the form:
+
+#   {(subscription, with, name, specification) -> factories}
+
+# where:
+
+#   'subscription' is a flag indicating if this registration is for
+#   subscription adapters.
+
+#   'with' is a tuple of specs that is non-empty only in the case
+#   of multi-adapters.  
+
+#   'name' is a unicode adapter name.  Unnamed adapters have an empty
+#   name.
+
+#   'specification' is the interface being adapted to.
+
+#   'factories' is normally a tuple of factories, but can be anything.
+#   (See the "raw" option to the query-adapter calls.)  For subscription
+#   adapters, it is a tuple of tuples of factories.
+
+# The implied adapters are held in a single dictionary. The items in the
+# dictionary are of 3 forms:
+
+#   (subscription, specification) -> factories
+
+#      for simple unnamed adapters
+
+#   (subscription, specification, name) -> factories
+
+#      for named adapters
+
+#   (subscription, specification, name, order) -> {with -> factories}
+
+#      for multi-adapters.  
+
+
+import weakref
+from sets import Set
+from zope.interface.ro import ro
+from zope.interface.declarations import providedBy
+from zope.interface.interface import InterfaceClass
+
+Default = InterfaceClass("Default", (), {})
+
+class ReadProperty(object):
+
+    def __init__(self, func):
+        self.func = func
+
+    def __get__(self, inst, class_):
+        if inst is None:
+            return self
+        return self.func(inst)
+
+class Surrogate(object):
+    """Specification surrogate
+
+    A specification surrogate is used to hold adapter registrations on
+    behalf of a specification.
     """
 
-    implements(IAdapterRegistry)
+    def __init__(self, spec, registry):
+        self.spec = spec.weakref()
+        spec.subscribe(self)
+        self.adapters = {}
+        self.dependents = weakref.WeakKeyDictionary()
+
+        self.__bases__ = [registry.get(base) for base in spec.__bases__]
+        for base in self.__bases__:
+            base.subscribe(self)
+
+    def dirty(self):
+        if 'get' in self.__dict__:
+            # Not already dirty
+            del self.selfImplied
+            del self.multImplied
+            del self.get
+        for dependent in self.dependents.keys():
+            dependent.dirty()
+
+    def clean(self):
+        self.selfImplied, self.multImplied = adapterImplied(self.adapters)
+
+        implied = {}
+
+        ancestors = ro(self)
+
+        # Collect implied data in reverse order to have more specific data
+        # override less-specific data.
+        ancestors.reverse()
+        for ancestor in ancestors:
+            for key, adapters in ancestor.selfImplied.iteritems():
+                subscription = key[0]
+                if subscription:
+                    adapters = tuple(map(tuple, adapters))
+                    implied[key] = tuple(Set(implied.get(key, ()) + adapters))
+                else:
+                    implied[key] = adapters
+            for k, ancestor_adapters in ancestor.multImplied.iteritems():
+                implied_adapters = implied.get(k)
+                if implied_adapters:
+                    subscription = k[0]
+                    if subscription:
+                        for key, adapters in ancestor_adapters.iteritems():
+                            # XXX: remove dupes?
+                            implied_adapters[key] = implied_adapters.get(
+                                key, []) + adapters
+                    else:
+                        implied_adapters.update(ancestor_adapters)
+                else:
+                    implied[k] = ancestor_adapters.copy()
 
-    # The implementation uses a mapping:
-    #
-    #  { (required, provided) -> (registered_provided, component) }
-    #
-    # Where the registered provides is what was registered and
-    # provided may be some base interface
-
-    def __init__(self, data=None):
-        if data is None:
-            data = {}
-        self._reg = data
-
-    def _registerAllProvided(self, require, primary_provide, object, provide):
-        # Registers a component using (require, provide) as a key.
-        # Also registers superinterfaces of the provided interface,
-        # stopping when the registry already has a component
-        # that provides a more general interface or when the Base is Interface.
-
-        reg = self._reg
-        reg[(require, provide)] = (primary_provide, object)
-        bases = getattr(provide, '__bases__', ())
-        for base in bases:
-            if base is Interface:
-                # Never register the say-nothing Interface.
-                continue
-            existing = reg.get((require, base), None)
-            if existing is not None:
-                existing_provide = existing[0]
-                if existing_provide is not primary_provide:
-                    if not existing_provide.extends(primary_provide):
-                        continue
-                    # else we are registering a general component
-                    # after a more specific component.
-            self._registerAllProvided(require, primary_provide, object, base)
-
-
-    def register(self, require, provide, object):
-
-        if require is not None and not IInterface.providedBy(require):
-            raise TypeError(
-                "The require argument must be an interface (or None)")
-        if not IInterface.providedBy(provide):
-            raise TypeError(
-                "The provide argument must be an interface")
-
-        # Invalidate our cache
-        self._v_cache = {}
-
-        self._registerAllProvided(require, provide, object, provide)
-
-    def get(self, ob_interface_provide, default=None, filter=None):
-        """
-        Finds a registered component that provides the given interface.
-        Returns None if not found.
-        """
-
-        if filter is None:
-            cache = getattr(self, '_v_cache', self)
-            if cache is self:
-                cache = self._v_cache = {}
-
-            # get the cache key
-            key = ob_interface_provide
-
-            cached = cache.get(key, self)
-            if cached is self:
-                cached = self._uncached_get(ob_interface_provide,
-                                            default, filter)
-                cache[key] = cached
-            return cached
 
-        return self._uncached_get(ob_interface_provide,
-                                  default, filter)
+        self.get = implied.get
 
-    def _uncached_get(self, (ob_interface, provide), default, filter):
+    def get(self, key):
+        """Get an implied value
 
-        try:
-            flattened = ob_interface.flattened
-        except AttributeError:
-            # Somebodey (probably a test) passed us a bare interface
-            if ob_interface is not None:
-                flattened = Declaration(ob_interface).flattened()
-            else:
-                flattened = None,
-        else:
-            flattened = flattened()
-
-
-        for interface in flattened:
-            c = self._reg.get((interface, provide))
-            if c:
-                c = c[1]
-                if filter is None:
-                    return c
-                if filter(c):
-                    return c
-
-        c = self._reg.get((None, provide))
-        if c:
-            c = c[1]
-            if filter is None:
-                return c
-            if filter(c):
-                return c
+        This is only called when the surrogate is dirty
+        """
+        self.clean()
+        return self.__dict__['get'](key)
+
+    def selfImplied(self):
+        """Return selfImplied when dirty
+        """
+        self.clean()
+        return self.__dict__['selfImplied']
+    selfImplied = ReadProperty(selfImplied)
+
+    def multiImplied(self):
+        """Return _multiImplied when dirty
+        """
+        self.clean()
+        return self.__dict__['multiImplied']
+    multiImplied = ReadProperty(multiImplied)
+
+    def subscribe(self, dependent):
+        self.dependents[dependent] = 1
+
+    def unsubscribe(self, dependent):
+        del self.dependents[dependent]
+
+    def _adaptTo(self, specification, factories, name='', with=()):
+        if factories is None:
+            try:
+                del self.adapters[False, tuple(with), name, specification]
+            except KeyError:
+                pass
+        else:
+            self.adapters[False, tuple(with), name, specification] = factories
+
+        self.dirty()
+
+    def _subscriptionAdaptTo(self, specification, factories, name='', with=()):
+        if factories is None:
+            raise TypeError, ("Unregistering subscription adapters" 
+                              " isn't implemented")
+
+        # Add factories to our list of factory lists.
+        key = (True, tuple(with), name, specification)
+        factoriesList = self.adapters.get(key, ())
+        factoriesList = factoriesList + (factories,)
+        self.adapters[key] = factoriesList
+
+        self.dirty()
+
+    def changed(self, which=None):
+        self.dirty()
+
+    def __repr__(self):
+        return '<%s(%s)>' % (self.__class__.__name__, self.spec())
+
+class AdapterRegistry(object):
+    """Surrogate registry
+    """
+
+    # Implementation node:
+    # We are like a weakref dict ourselves. We can't use a weakref
+    # dict because we have to use spec.weakref() rather than
+    # weakref.ref(spec) to get weak refs to specs.
+
+    _surrogateClass = Surrogate
+
+    def __init__(self):
+        default = self._surrogateClass(Default, self)
+        self._default = default
+        surrogates = {Default.weakref(): default}
+        self._surrogates = surrogates
+
+        def _remove(k):
+            try:
+                del surrogates[k]
+            except KeyError:
+                pass
+
+        self._remove = _remove
+
+    def get(self, declaration):
+        if declaration is None:
+            return self._default
+
+        ref = declaration.weakref(self._remove)
+        surrogate = self._surrogates.get(ref)
+        if surrogate is None:
+            surrogate = self._surrogateClass(declaration, self)
+            self._surrogates[ref] = surrogate
+
+        return surrogate
+        
+    def provideAdapter(self, required, provided, factories, name=u'', with=()):
+        """Register an adapter
+
+        Note that the given name must be convertable to unicode.
+        Use an empty string for unnamed adapters. It is impossible to
+        have a named adapter with an empty name.
+        """
+        required = self.get(required)
+        if with:
+            with = tuple(with)
+        else:
+            with = ()
+
+        if not isinstance(name, basestring):
+            raise TypeError("The name provided to provideAdapter "
+                            "must be a string or unicode")
+        required._adaptTo(provided, factories, unicode(name), with)
+
+    def provideSubscriptionAdapter(self, required, provided, factories,
+                                   name=u'', with=()):
+        """Register a subscription adapter
+
+        Note that the given name must be convertable to unicode.
+        Use an empty string for unnamed subscription adapters. It is
+        impossible to have a named subscription adapter with an empty name.
+        """
+        required = self.get(required)
+        if with:
+            with = tuple(with)
+        else:
+            with = ()
+
+        if not isinstance(name, basestring):
+            raise TypeError("The name provided to provideAdapter "
+                            "must be a string or unicode")
+        required._subscriptionAdaptTo(provided, factories, unicode(name), with)
+
+    def queryAdapter(self, ob, interface, default=None, raw=False):
+        """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 = AdapterRegistry()
+
+           Adapting to some other interface for which there is no
+           adapter 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
+           """
+
+        declaration = providedBy(ob)
+        s = self.get(declaration)
+
+        factories = s.get((False, interface))
+        if factories is None:
+            factories = self._default.get((False, interface))
+
+        if factories is not None:
+            if raw:
+                return factories
+            
+            for factory in factories:
+                ob = factory(ob)
+            return ob
+
+        return default
+
+
+    def queryFactory(self, declaration, interface, default=None):
+        """Query for factory that adapts type-declaration things to interface.
+
+           This enables you to get an adapter, and/or check for the existence
+           of one, given just the type (interface) of a thing.
+
+           >>> import zope.interface
+           >>> from zope.interface import implementedBy
+           >>> class F0(zope.interface.Interface):
+           ...     pass
+           >>> class F1(F0):
+           ...     pass
+
+           >>> class C:
+           ...     zope.interface.implements(F1)
+           >>> c = C()
+
+           >>> registry = AdapterRegistry()
+
+           Adapting to some other interface for which there is no
+           adapter returns the default:
+
+           >>> class B0(zope.interface.Interface):
+           ...     pass
+           >>> class B1(B0):
+           ...     pass
+
+           >>> registry.queryFactory(F1, B0)
+           >>> registry.queryFactory(F1, B0, 42)
+           42
+
+           Unless we define an adapter:
+
+           >>> def f1(ob):
+           ...     return 1
+
+           >>> registry.provideAdapter(F0, B1, [f1])
+           >>> registry.queryFactory(F1, B0) == [f1]
+           True
+
+           If we define a more specific adapter (for F1), we'll get that:
+
+           >>> def f2(ob):
+           ...     return 2
+
+           >>> registry.provideAdapter(F1, B1, [f2])
+           >>> registry.queryFactory(F1, B0) == [f2]
+           True
+
+           >>> def f3(ob):
+           ...     return 3
+
+           >>> registry.provideAdapter(F1, B0, [f3])
+           >>> registry.queryFactory(F1, B0) == [f3]
+           True
+           """
+
+        s = self.get(declaration)
+
+        factories = s.get((False, interface))
+        if factories is None:
+            factories = self._default.get((False, interface))
+
+        if factories is not None:
+            return factories
+
+        return default
+
+
+    def querySubscriptionAdapter(self, ob, interface, name=u'', default=(),
+                            raw=False):
+        """Query for subscription adapters
+
+        >>> import zope.interface
+        >>> class IAnimal(zope.interface.Interface):
+        ...     pass
+        >>> class IPoultry(IAnimal):
+        ...     pass
+        >>> class IChicken(IPoultry):
+        ...     pass
+        >>> class ISeafood(IAnimal):
+        ...     pass
+
+        >>> class Poultry:
+        ...     zope.interface.implements(IPoultry)
+        >>> poultry = Poultry()
+
+        >>> registry = AdapterRegistry()
+
+        Adapting to some other interface for which there is no
+        subscription adapter returns the default:
+
+        >>> class IRecipe(zope.interface.Interface):
+        ...     pass
+        >>> class ISausages(IRecipe):
+        ...     pass
+        >>> class INoodles(IRecipe):
+        ...     pass
+        >>> class IKFC(IRecipe):
+        ...     pass
+
+        >>> list(registry.querySubscriptionAdapter(poultry, IRecipe))
+        []
+        >>> registry.querySubscriptionAdapter(poultry, IRecipe, default=42)
+        42
+
+        Unless we define a subscription adapter:
+
+        >>> def sausages(ob):
+        ...     return 'sausages'
+
+        >>> registry.provideSubscriptionAdapter(IAnimal, ISausages, [sausages])
+        >>> list(registry.querySubscriptionAdapter(poultry, ISausages))
+        ['sausages']
+
+        And define another subscription adapter:
+
+        >>> def noodles(ob):
+        ...     return 'noodles'
+
+        >>> registry.provideSubscriptionAdapter(IPoultry, INoodles, [noodles])
+        >>> meals = list(registry.querySubscriptionAdapter(poultry, IRecipe))
+        >>> meals.sort()
+        >>> meals
+        ['noodles', 'sausages']
+
+        >>> class Chicken:
+        ...     zope.interface.implements(IChicken)
+        >>> chicken = Chicken()
+
+        >>> def kfc(ob):
+        ...     return 'kfc'
+
+        >>> registry.provideSubscriptionAdapter(IChicken, IKFC, [kfc])
+        >>> meals = list(registry.querySubscriptionAdapter(chicken, IRecipe))
+        >>> meals.sort()
+        >>> meals
+        ['kfc', 'noodles', 'sausages']
+
+        And the answer for poultry hasn't changed:
+
+        >>> registry.provideSubscriptionAdapter(IPoultry, INoodles, [noodles])
+        >>> meals = list(registry.querySubscriptionAdapter(poultry, IRecipe))
+        >>> meals.sort()
+        >>> meals
+        ['noodles', 'sausages']
+        """
+
+        declaration = providedBy(ob)
+        s = self.get(declaration)
+
+        if name:
+            key = (True, interface, name)
+        else:
+            key = (True, interface)
+
+        factoriesLists = s.get(key)
+        if factoriesLists is None:
+            factoriesLists = self._default.get(key)
+
+        if factoriesLists is not None:
+            if raw:
+                return factoriesLists
+            
+            return [factory(ob)
+                    for factories in factoriesLists
+                    for factory in factories]
+
+        return default
+
+
+    def queryNamedAdapter(self, ob, interface, name, default=None, raw=False):
+        """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 = AdapterRegistry()
+
+        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
+
+        """
+
+        declaration = providedBy(ob)
+        s = self.get(declaration)
+        if name:
+            key = (False, interface, name)
+        else:
+            key = (False, interface)
+        factories = s.get(key)
+        if factories is None:
+            factories = self._default.get(key)
+        if factories is not None:
+            if raw:
+                return factories
+            for factory in factories:
+                ob = factory(ob)
+            return ob
 
         return default
 
-    def getForObject(self, object, interface, filter=None):
-        return self.get((providedBy(object), interface), filter=filter)
+    def queryMultiAdapter(self, objects, interface, name=u'',
+                          default=None, raw=False, _subscription=False):
+        """
+
+        >>> 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 = AdapterRegistry()
+
+        If we ask for a multi 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
+
+
+        >>> class f1:
+        ...     def __init__(self, x, y):
+        ...         self.x, self.y = x, y
+
+
+        >>> registry.provideAdapter(IF0, IB1, [f1], name='bob', with=[IR0])
+        >>> a = registry.queryMultiAdapter((c, r), IB0, 'bob')
+        >>> a.__class__ is f1
+        True
+        >>> a.x is c
+        True
+        >>> a.y is r
+        True
+
+        >>> registry.queryMultiAdapter((c, r), IB0, 'bruce')
+
+        >>> class f2(f1):
+        ...     pass
+
+        >>> registry.provideAdapter(IF1, IB1, [f2], name='bob', with=[IR1])
+        >>> a = registry.queryMultiAdapter((c, r), IB0, 'bob')
+        >>> a.__class__ is f2
+        True
+        >>> a.x is c
+        True
+        >>> a.y is r
+        True
+
+        """
+        ob = objects[0]
+        order = len(objects)
+        obs = objects[1:]
+
+        declaration = providedBy(ob)
+        if _subscription:
+            result = []
+
+        surrogates = (self.get(declaration), self._default)
+
+        for surrogate in surrogates:
+            adapters = surrogate.get((_subscription, interface, name, order))
+            if adapters:
+                if _subscription:
+                    matched_factories = []
+                else:
+                    matched = None
+                    matched_factories = None
+                for interfaces, factories in adapters.iteritems():
+                    for iface, ob in zip(interfaces, obs):
+                        if not iface.providedBy(ob):
+                            break # This one is no good
+                    else:
+                        if _subscription:
+                            matched_factories.extend(factories)
+                        else:
+                            # we didn't break, so we have a match
+                            if matched is None:
+                                matched = interfaces
+                                matched_factories = factories
+                            else:
+                                # see if the new match is better than the old
+                                # one:
+                                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
+                                
+
+                if matched_factories is not None:
+                    if raw:
+                        return matched_factories
+
+                    if not _subscription:
+                        assert len(matched_factories) == 1, \
+                               "matched_factories has more than 1 element: " \
+                               + repr(matched_factories)
+                        return matched_factories[0](*objects)
+                    else:
+                        for factories in matched_factories:
+                            assert len(factories) == 1
+                            result.append(factories[0](*objects))
+
+        if _subscription and result:
+            return result
+        else:
+            return default
 
-    def getRegistered(self, require, provide):
-        data = self._reg.get((require, provide))
-        if data:
-            registered_provide, object = data
-            if registered_provide == provide:
-                return object
-        return None
+    def querySubscriptionMultiAdapter(self, objects, interface, name=u'',
+                                 default=(), raw=False):
+        """
+        Subscription multiadaption works too:
+
+        >>> import zope.interface
+        >>> class IAnimal(zope.interface.Interface):
+        ...     pass
+        >>> class IPoultry(IAnimal):
+        ...     pass
+        >>> class IChicken(IPoultry):
+        ...     pass
+        >>> class ISeafood(IAnimal):
+        ...     pass
+
+        >>> class Animal:
+        ...     zope.interface.implements(IAnimal)
+        >>> animal = Animal()
+
+        >>> class Poultry:
+        ...     zope.interface.implements(IPoultry)
+        >>> poultry = Poultry()
+
+        >>> class Poultry:
+        ...     zope.interface.implements(IPoultry)
+        >>> poultry = Poultry()
+
+        >>> class Chicken:
+        ...     zope.interface.implements(IChicken)
+        >>> chicken = Chicken()
+        
+
+        >>> class IRecipe(zope.interface.Interface):
+        ...     pass
+        >>> class ISausages(IRecipe):
+        ...     pass
+        >>> class INoodles(IRecipe):
+        ...     pass
+        >>> class IKFC(IRecipe):
+        ...     pass
+
+        >>> class IDrink(zope.interface.Interface):
+        ...     pass
+
+        >>> class Drink:
+        ...     zope.interface.implements(IDrink)
+        >>> drink = Drink()
+
+        >>> class Meal:
+        ...     def __init__(self, animal, drink):
+        ...         self.animal, self.drink = animal, drink
+        >>> class Lunch(Meal):
+        ...     pass
+        >>> class Dinner(Meal):
+        ...     pass
+
+        >>> registry = AdapterRegistry()
+        >>> query = registry.querySubscriptionMultiAdapter
+        >>> provide = registry.provideSubscriptionAdapter
+
+        Can't find adapters for insufficiently specific interfaces:
+        
+        >>> provide(IPoultry, IRecipe, [Dinner], with=[IDrink])
+        >>> list(query((animal, drink), IRecipe))
+        []
+
+        But we can for equally specific:
+
+        >>> adapters = list(query((poultry, drink), IRecipe))
+        >>> len(adapters)
+        1
+
+        And for more specific:
+
+        >>> adapters = list(query((chicken, drink), IRecipe))
+        >>> len(adapters)
+        1
+
+        >>> provide(IAnimal, IRecipe, [Meal], with=[IDrink])
+        >>> provide(IAnimal, IRecipe, [Lunch], with=[IDrink])
+        >>> adapters = list(query((animal, drink), IRecipe)) 
+        >>> names = [a.__class__.__name__ for a in adapters]
+        >>> names.sort()
+        >>> names
+        ['Lunch', 'Meal']
+        >>> adapters[0].animal is animal
+        True
+        >>> adapters[0].drink is drink
+        True
+        >>> adapters[1].animal is animal
+        True
+        >>> adapters[1].drink is drink
+        True
+
+        Mixed specificities:
+
+        >>> registry = AdapterRegistry()
+        >>> query = registry.querySubscriptionMultiAdapter
+        >>> provide = registry.provideSubscriptionAdapter
+
+        >>> provide(IPoultry, IRecipe, [Meal], with=[IDrink])
+        >>> provide(IChicken, IRecipe, [Lunch], with=[IDrink])
+
+        We can only use IPoultry recipes on poultry -- we can't apply chicken
+        recipes because poultry isn't specific enough.  So there's only one
+        choice for poultry:
+        
+        >>> adapters = list(query((poultry, drink), IRecipe))
+        >>> len(adapters)
+        1
+
+        But using chicken, we can use poultry *and* chicken recipes:
+        
+        >>> adapters = list(query((chicken, drink), IRecipe))
+        >>> len(adapters)
+        2
+
+        We should get the same results if we swap the order of the input types:
+
+        >>> registry = AdapterRegistry()
+        >>> query = registry.querySubscriptionMultiAdapter
+        >>> provide = registry.provideSubscriptionAdapter
+
+        >>> provide(IDrink, IRecipe, [Meal], with=[IPoultry])
+        >>> provide(IDrink, IRecipe, [Lunch], with=[IChicken])
+
+        >>> adapters = list(query((drink,poultry), IRecipe))
+        >>> len(adapters)
+        1
+        >>> adapters = list(query((drink,chicken), IRecipe))
+        >>> len(adapters)
+        2
+
+        And check that names work, too:
+
+        >>> adapters = list(query((drink,poultry), IRecipe, name='Joes Diner'))
+        >>> len(adapters)
+        0
+        
+        >>> provide(IDrink, IRecipe, [Meal], with=[IPoultry],name='Joes Diner')
+        >>> adapters = list(query((drink,poultry), IRecipe, name='Joes Diner'))
+        >>> len(adapters)
+        1
+
+        """
+        return self.queryMultiAdapter(objects, interface, name, default, raw,
+                                      _subscription=True)
 
     def getRegisteredMatching(self,
                               required=None,
-                              provided=None):
+                              provided=None,
+                              name=None,
+                              with=None,
+                              ):
+        """Search for registered adapters
+
+           Return a 5-tuple with:
+
+           - (first) required interface
+
+           - provided interface
+
+           - a tuple of additional required interfaces (for multi-adapters)
+
+           - name, and
+
+           - a sequence of factories. (Note that this could be arbitrary data).
+           
+
+           Note, this is usually slow!
+
+           >>> from zope.interface import Interface
+
+           >>> class R1(Interface):
+           ...     pass
+           >>> class R12(Interface):
+           ...     pass
+           >>> class R2(R1):
+           ...     pass
+           >>> class R3(R2):
+           ...     pass
+           >>> class R4(R3):
+           ...     pass
+
+           >>> class P1(Interface):
+           ...     pass
+           >>> class P2(P1):
+           ...     pass
+           >>> class P3(P2):
+           ...     pass
+           >>> class P4(P3):
+           ...     pass
+
+
+           >>> registry = AdapterRegistry()
+           >>> registry.provideAdapter(None, P3, 'default P3')
+           >>> registry.provideAdapter(Interface, P3, 'any P3')
+           >>> registry.provideAdapter(R2, P3, 'R2 P3')
+           >>> registry.provideAdapter(R2, P3, "bobs R2 P3", name='bob')
+
+           >>> from pprint import PrettyPrinter
+           >>> pprint = PrettyPrinter(width=60).pprint
+           >>> def sorted(x):
+           ...    x = [(getattr(r, '__name__', None), p.__name__, w, n, f)
+           ...         for (r, p, w, n, f) in x]
+           ...    x.sort()
+           ...    pprint(x)
+
+           >>> sorted(registry.getRegisteredMatching())
+           [(None, 'P3', (), u'', 'default P3'),
+            ('Interface', 'P3', (), u'', 'any P3'),
+            ('R2', 'P3', (), u'', 'R2 P3'),
+            ('R2', 'P3', (), u'bob', 'bobs R2 P3')]
+
+           >>> sorted(registry.getRegisteredMatching(name=''))
+           [(None, 'P3', (), u'', 'default P3'),
+            ('Interface', 'P3', (), u'', 'any P3'),
+            ('R2', 'P3', (), u'', 'R2 P3')]
+
+           >>> sorted(registry.getRegisteredMatching(required=[R1]))
+           [(None, 'P3', (), u'', 'default P3'),
+            ('Interface', 'P3', (), u'', 'any P3')]
+
+           >>> sorted(registry.getRegisteredMatching(required=R1))
+           [(None, 'P3', (), u'', 'default P3'),
+            ('Interface', 'P3', (), u'', 'any P3')]
+
+           >>> sorted(registry.getRegisteredMatching(provided=[P1]))
+           [(None, 'P3', (), u'', 'default P3'),
+            ('Interface', 'P3', (), u'', 'any P3'),
+            ('R2', 'P3', (), u'', 'R2 P3'),
+            ('R2', 'P3', (), u'bob', 'bobs R2 P3')]
+
+           >>> sorted(registry.getRegisteredMatching(provided=P1))
+           [(None, 'P3', (), u'', 'default P3'),
+            ('Interface', 'P3', (), u'', 'any P3'),
+            ('R2', 'P3', (), u'', 'R2 P3'),
+            ('R2', 'P3', (), u'bob', 'bobs R2 P3')]
+
+           >>> sorted(registry.getRegisteredMatching(provided=P3))
+           [(None, 'P3', (), u'', 'default P3'),
+            ('Interface', 'P3', (), u'', 'any P3'),
+            ('R2', 'P3', (), u'', 'R2 P3'),
+            ('R2', 'P3', (), u'bob', 'bobs R2 P3')]
+
+           >>> sorted(registry.getRegisteredMatching(
+           ...     required = (R4, R12),
+           ...     provided = (P1, )))
+           [(None, 'P3', (), u'', 'default P3'),
+            ('Interface', 'P3', (), u'', 'any P3'),
+            ('R2', 'P3', (), u'', 'R2 P3'),
+            ('R2', 'P3', (), u'bob', 'bobs R2 P3')]
+
+           >>> sorted(registry.getRegisteredMatching(
+           ...     required = (R4, R12),
+           ...     provided = (P3, )))
+           [(None, 'P3', (), u'', 'default P3'),
+            ('Interface', 'P3', (), u'', 'any P3'),
+            ('R2', 'P3', (), u'', 'R2 P3'),
+            ('R2', 'P3', (), u'bob', 'bobs R2 P3')]
+
+           >>> sorted(registry.getRegisteredMatching(
+           ...     required = (R2, ),
+           ...     provided = (P3, )))
+           [(None, 'P3', (), u'', 'default P3'),
+            ('Interface', 'P3', (), u'', 'any P3'),
+            ('R2', 'P3', (), u'', 'R2 P3'),
+            ('R2', 'P3', (), u'bob', 'bobs R2 P3')]
+
+           >>> sorted(registry.getRegisteredMatching(
+           ...     required = (R2, ),
+           ...     provided = (P3, ),
+           ...     name='bob'))
+           [('R2', 'P3', (), u'bob', 'bobs R2 P3')]
+
+           >>> sorted(registry.getRegisteredMatching(
+           ...     required = (R3, ),
+           ...     provided = (P1, ),
+           ...     name='bob'))
+           [('R2', 'P3', (), u'bob', 'bobs R2 P3')]
+
+           """
+
+        if name is not None:
+            name = unicode(name)
+        
+        if isinstance(required, InterfaceClass):
+            required = (required, )
+        elif required is None:
+            required = [ref() for ref in self._surrogates.keys()
+                                   if ref() is not None]
+
+        required = tuple(required)+(None,)
+
+        if isinstance(provided, InterfaceClass):
+            provided = (provided, )
+
+
+        seen = {}
+
+        for required in required:
+            s = self.get(required)
+            for ancestor in ro(s):
+                if ancestor in seen:
+                    continue
+                seen[ancestor] = 1
+                adapters = ancestor.adapters
+                if adapters:
+                    items = adapters.iteritems()
+                    ancestor = ancestor.spec()
+                    if ancestor is Default:
+                        ancestor = None
+                    for key, factories in items:
+                        subscription, rwith, aname, target = key
+                        if subscription:
+                            raise NotImplementedError
+                        if with is not None and not mextends(with, rwith):
+                            continue
+                        if name is not None and aname != name:
+                            continue
+
+                        if provided:
+                            for p in provided:
+                                if target.extends(p, False):
+                                    break
+                            else:
+                                # None matching
+                                continue
+
+                        yield (ancestor, target, rwith, aname, factories)
+
+
+
+def mextends(with, rwith):
+    if len(with) == len(rwith):
+        for w, r in zip(with, rwith):
+            if not w.isOrExtends(r):
+                break
+        else:
+            return True
+    return False
+        
+
+def adapterImplied(adapters):
+    implied = {}
+    multi = {}
+    registered = {}
+    # Add adapters and interfaces directly implied by same:
 
+    for key, factories in adapters.iteritems():
+
+        # XXX Backward compatability
+        # Don't need to handle 3-tuples some day
+        try:
+            (subscription, with, name, target) = key
+        except ValueError:
+            (with, name, target) = key
+            subscription = False
+        if with:
+            _add_multi_adapter(with, name, target, target, multi,
+                               registered, factories, subscription)
+        elif name:
+            _add_named_adapter(target, target, name, implied,
+                               registered, factories, subscription)
+        else:
+            _add_adapter(target, target, implied, registered, factories,
+                         subscription)
 
-        required_interfaces = required
-        provided_interfaces = provided
+    return implied, multi
 
-        if IInterface.providedBy(required_interfaces):
-            required_interfaces = (required_interfaces, )
+def _add_adapter(target, provided, implied, registered, factories,
+                 subscription):
+    key = subscription, target
+    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__:
+            _add_adapter(b, provided, implied, registered, factories,
+                         subscription)
+
+def _add_named_adapter(target, provided, name,
+                        implied, registered, factories, subscription):
+    key = subscription, 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__:
+            _add_named_adapter(b, provided, name,
+                               implied, registered, factories, subscription)
+
+def _add_multi_adapter(interfaces, name, target, provided, implied,
+                       registered, factories, subscription):
+    order = len(interfaces)+1
+    key = subscription, target, name, order
+    adapters = implied.get(key)
+    if adapters is None:
+        adapters = {}
+        implied[key] = adapters
+
+    key = key, interfaces # The full key has all 5
+    if key not in registered or registered[key].extends(provided):
+        # This is either a new entry or it is an entry for a more
+        # general interface that is closer provided than what we had
+        # before
+        registered[key] = provided
+        adapters[interfaces] = factories
+
+    for b in target.__bases__:
+        _add_multi_adapter(interfaces, name, b, provided, implied,
+                           registered, factories, subscription)
 
-        if provided_interfaces:
-
-            if IInterface.providedBy(provided_interfaces):
-                provided_interfaces = (provided_interfaces, )
-
-            r = {}
-
-            if required_interfaces:
-                # Both specified
-                for required in _flatten(required_interfaces, 1):
-                    for provided in provided_interfaces:
-                        v = self._reg.get((required, provided))
-                        if v:
-                            rprovided, o = v
-                            r[required, rprovided] = o
-
-
-            else:
-                # Only provided specified
-                for (required, provided), (rprovided, o) in self._reg.items():
-                    for p in provided_interfaces:
-                        if provided.extends(p, 0):
-                            r[required, rprovided] = o
-                            break
-
-            return [(required, provided, o)
-                    for ((required, provided), o) in r.items()]
-
-
-        elif required_interfaces:
-            # Just required specified
-            required_interfaces = _flatten(required_interfaces, 1)
-            return [(required, provided, o)
-                    for (required, provided), (rprovided, o)
-                    in self._reg.items()
-                    if ((required in required_interfaces)
-                        and
-                        provided == rprovided
-                        )
-                   ]
-
-        else:
-            # Nothing specified
-            return [(required, provided, o)
-                    for (required, provided), (rprovided, o)
-                    in self._reg.items()
-                    if provided == rprovided
-                   ]


=== Zope3/src/zope/interface/interface.py 1.23 => 1.24 ===
--- Zope3/src/zope/interface/interface.py:1.23	Sat Mar  6 10:38:46 2004
+++ Zope3/src/zope/interface/interface.py	Mon Mar  8 12:26:56 2004
@@ -204,7 +204,7 @@
     def isImplementedByInstancesOf(self, cls):
         warnings.warn(
             "isImplementedByInstancesOf has been renamed to implementedBy",
-            DeprecationWarning, stacklevel=1,
+            DeprecationWarning, stacklevel=2,
             )
         return self.implementedBy(cls)
 


=== Zope3/src/zope/interface/interfaces.py 1.21 => 1.22 ===
--- Zope3/src/zope/interface/interfaces.py:1.21	Fri Mar  5 17:09:28 2004
+++ Zope3/src/zope/interface/interfaces.py	Mon Mar  8 12:26:56 2004
@@ -324,107 +324,6 @@
         """Returns the number of distinct interfaces registered.
         """
 
-
-class IAdapterRegistry(Interface):
-    """Adapter-style registry
-
-    This registry stores objects registered to convert (or participate
-    in the conversion from) one interface to another. The interface
-    converted is the "required" interface. We say that the interface
-    converted to is the "provided" interface.
-
-    The objects registered here don't need to be adapters. What's
-    important is that they are registered according to a required and
-    a provided interface.
-
-    The provided interface may not be None.
-
-    The required interface may be None. Adapters with a required
-    interface of None adapt non-components. An adapter that adapts all
-    components should specify a required interface of
-    Interface.Interface.
-
-    """
-
-    def register(require, provide, object):
-        """Register an object for a required and provided interface.
-
-        There are no restrictions on what the object might be.
-        Any restrictions (e.g. callability, or interface
-        implementation) must be enforced by higher-level code.
-
-        The require argument may be None.
-
-        """
-
-    def get((implements, provides), default=None, filter=None):
-        """Return a registered object
-
-        The registered object is one that was registered to require an
-        interface that one of the interfaces in the 'implements'
-        specification argument extends or equals and that provides an
-        interface that extends or equals the 'provides' argument.  An
-        attempt will be made to find the component that most closely
-        matches the input arguments.
-
-        The object returned could have been registered to require None.
-
-        Note that the implements may be None, it which case a
-        component will be returned only if it was registered with a
-        require of None.
-
-        An optional filter may be provided. If provided, the returned
-        object must pass the filter. Search will continue until a
-        suitable match can be found. The filter should take a single
-        argument and return a true value if the object passes the
-        filter, or false otherwise.
-
-        """
-
-    def getForObject(object, interface, filter=None):
-        """Get an adapter for object that implements the specified interface
-
-        The filter option has the same meaning as in the get method.
-        """
-
-    def getRegistered(require, provide):
-        """return data registered specifically for the given interfaces
-
-        None is returned if nothing is registered.
-        """
-
-    def getRegisteredMatching(required=None,
-                              provided=None):
-        """Return information about registered data
-
-        Zero or more required and provided interfaces may be
-        specified. Registration information matching any of the
-        specified interfaces is returned.
-
-        The arguments may be interfaces, or sequences of interfaces.
-
-        The returned value is a sequence of three-element tuples:
-
-        - required interface
-
-        - provided interface
-
-        - the object registered specifically for the required and
-          provided interfaces.
-
-        To understand how the matching works, imagine that we have
-        interfaces R1, R2, P1, and P2. R2 extends R1. P2 extends P1.
-        We've registered C to require R1 and provide P2.  Given this,
-        if we call getRegisteredMatching:
-
-          registry.getRegisteredMatching([R2], [P1])
-
-        the returned value will include:
-
-          (R1, P2, C)
-        """
-
-
 class IImplementorRegistry(Interface):
     """Implementor registry
 

=== Removed File Zope3/src/zope/interface/surrogate.py ===




More information about the Zodb-checkins mailing list