[Zope3-checkins] CVS: Zope3/src/zope/interface - adapter.txt:1.1 adapter.py:1.10 interfaces.py:1.23

Jim Fulton jim at zope.com
Mon Mar 15 15:42:25 EST 2004


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

Modified Files:
	adapter.py interfaces.py 
Added Files:
	adapter.txt 
Log Message:
Defined (and changed) the adapter registration and lookup apis:

- Simpler methods

- Multi-adapter registration and lookup the default, so always
  register multiple (including one or zero) required specifications.

- Subscriptions no longer specify names (yagni)

- Removed factory semantics from the api

Changed the implementation to allow fast lookup of all names for
a given adapter.  (Still need to provide an access api.)


=== Added File Zope3/src/zope/interface/adapter.txt ===
================
Adapter Registry
================

Adapter registries provide a way to register objects that depend on
one or more interface specifications and provide (perhaps indirectly)
some interface.  In addition, the registrations have names. (You can
think of the names as qualifiers of the provided interfaces.)

The term "interface specification" refers both to interfaces and to
interface declarations, such as declarations of interfaces implemented
by a class.


Single Adapters
===============

Let's look at a simple example, using a single required specification::

  >>> from zope.interface.adapter import AdapterRegistry
  >>> import zope.interface

  >>> class IR1(zope.interface.Interface):
  ...     pass
  >>> class IP1(zope.interface.Interface):
  ...     pass
  >>> class IP2(IP1):
  ...     pass

  >>> registry = AdapterRegistry()

We'll register an object that depends on IR1 and "provides" IP2::

  >>> registry.register([IR1], IP2, '', 12)

Given the registration, we can look it up again::

  >>> registry.lookup([IR1], IP2, '')
  12

Note that we used an integer in the example.  In real applications,
one would use some objects that actually depend on or provide
interfaces. The registry doesn't care about what gets registered, so
we'll use integers and strings to keep the examples simple. There is
one exception.  Registering a value of None unregisters any
previously-registered value.

If an object depends on a specification, it can be looked up with a
specification that extends the specification that it depends on::

  >>> class IR2(IR1):
  ...     pass
  >>> registry.lookup([IR2], IP2, '')
  12

We can use a class implementation specification to look up the object::

  >>> class C2:
  ...     zope.interface.implements(IR2)

  >>> registry.lookup([zope.interface.implementedBy(C2)], IP2, '')
  12


and it can be looked up for interfaces that it's provided interface
extends::

  >>> registry.lookup([IR1], IP1, '')
  12
  >>> registry.lookup([IR2], IP1, '')
  12

But if you require a specification that doesn't extend the specification the
object depends on, you won't get anything::

  >>> registry.lookup([zope.interface.Interface], IP1, '')

By the way, you can pass a default value to lookup::

  >>> registry.lookup([zope.interface.Interface], IP1, '', 42)
  42

If you try to get an interface the object doesn't provide, you also
won't get anything::

  >>> class IP3(IP2):
  ...     pass
  >>> registry.lookup([IR1], IP3, '')

You also won't get anything if you use the wrong name::

  >>> registry.lookup([IR1], IP1, 'bob')
  >>> registry.register([IR1], IP2, 'bob', "Bob's 12")
  >>> registry.lookup([IR1], IP1, 'bob')
  "Bob's 12"

You can leave the name off when doing a lookup:

  >>> registry.lookup([IR1], IP1)
  12

If we register an object that provides IP1::

  >>> registry.register([IR1], IP1, '', 11)

then that object will be prefered over O(12)::

  >>> registry.lookup([IR1], IP1, '')
  11

Also, if we register an object for IR2, then that will be prefered
when using IR2::

  >>> registry.register([IR2], IP1, '', 21)
  >>> registry.lookup([IR2], IP1, '')
  21

lookup1
-------

Lookup of single adapters is common enough that there is a
specialized version of lookup that takes a single required interface:

  >>> registry.lookup1(IR2, IP1, '')
  21
  >>> registry.lookup1(IR2, IP1)
  21


Default Adapters
----------------
  
Sometimes, you want to provide an adapter that will adapt anything.
For that, provide None as the required interface::

  >>> registry.register([None], IP1, '', 1)
  
then we can use that adapter for interfaces we don't have specific
adapters for::

  >>> class IQ(zope.interface.Interface):
  ...     pass
  >>> registry.lookup([IQ], IP1, '')
  1

Of course, specific adapters are still used when applicable::

  >>> registry.lookup([IR2], IP1, '')
  21

Class adapters
--------------

You can register adapters for class declarations, which is almost the
same as registering them for a class::

  >>> registry.register([zope.interface.implementedBy(C2)], IP1, '', 'C21')
  >>> registry.lookup([zope.interface.implementedBy(C2)], IP1, '')
  'C21'

Unregistering
-------------

You can unregister by registering None, rather than an object:;

  >>> registry.register([zope.interface.implementedBy(C2)], IP1, '', None)
  >>> registry.lookup([zope.interface.implementedBy(C2)], IP1, '')
  21

Of course, this means that None can't be registered. This is an
exception to the statement, made earlier, that the registry doesn't
care what gets registered.

Multi-adapters
==============

You can adapt multiple specifications::

  >>> registry.register([IR1, IQ], IP2, '', '1q2')
  >>> registry.lookup([IR1, IQ], IP2, '')
  '1q2'
  >>> registry.lookup([IR2, IQ], IP1, '')
  '1q2'

  >>> class IS(zope.interface.Interface):
  ...     pass
  >>> registry.lookup([IR2, IS], IP1, '')

  >>> class IQ2(IQ):
  ...     pass

  >>> registry.lookup([IR2, IQ2], IP1, '')
  '1q2'
  
Default Adapters
----------------

As with single adapters, you can define default adapters by specifying
None for the *first* specification:

  >>> registry.register([None, IQ], IP2, '', 'q2')
  >>> registry.lookup([IS, IQ], IP2, '')
  'q2'

Null Adapters
=============

You can also adapt no specification:

  >>> registry.register([], IP2, '', 2)
  >>> registry.lookup([], IP2, '')
  2
  >>> registry.lookup([], IP1, '')
  2


Subscriptions
=============

Normally, we want to look up an object that most-closely matches a
specification.  Sometimes, we want to get all of the objects that
match some specification.  We use subscriptions for this.  We
subscribe objects against specifications and then later find all of
the subscribed objects::

  >>> registry.subscribe([IR1], IP2, 'sub12 1')
  >>> registry.subscriptions([IR1], IP2)
  ['sub12 1']

Note that, unlike regular adapters, subscriptions are unnamed.

The order of returned subscriptions is not specified.

You can have multiple subscribers for the same specification::

  >>> registry.subscribe([IR1], IP2, 'sub12 2')
  >>> subs = registry.subscriptions([IR1], IP2)
  >>> subs.sort()
  >>> subs
  ['sub12 1', 'sub12 2']

You can register subscribers for all specifications using None::

  >>> registry.subscribe([None], IP1, 'sub_1')
  >>> subs = registry.subscriptions([IR2], IP1)
  >>> subs.sort()
  >>> subs
  ['sub12 1', 'sub12 2', 'sub_1']

Subscriptions may be combined over multiple compatible specifications::

  >>> subs = registry.subscriptions([IR2], IP1)
  >>> subs.sort()
  >>> subs
  ['sub12 1', 'sub12 2', 'sub_1']
  >>> registry.subscribe([IR1], IP1, 'sub11')
  >>> subs = registry.subscriptions([IR2], IP1)
  >>> subs.sort()
  >>> subs
  ['sub11', 'sub12 1', 'sub12 2', 'sub_1']
  >>> registry.subscribe([IR2], IP2, 'sub22')
  >>> subs = registry.subscriptions([IR2], IP1)
  >>> subs.sort()
  >>> subs
  ['sub11', 'sub12 1', 'sub12 2', 'sub22', 'sub_1']
  >>> subs = registry.subscriptions([IR2], IP2)
  >>> subs.sort()
  >>> subs
  ['sub12 1', 'sub12 2', 'sub22']

Subscriptions can be on multiple specifications::

  >>> registry.subscribe([IR1, IQ], IP2, 'sub1q2')
  >>> registry.subscriptions([IR1, IQ], IP2)
  ['sub1q2']
  
As with single subscriptions and non-subscription adapters, you can
specify None for the first required interface, to specify a default::

  >>> registry.subscribe([None, IQ], IP2, 'sub_q2')
  >>> registry.subscriptions([IS, IQ], IP2)
  ['sub_q2']
  >>> subs = registry.subscriptions([IR1, IQ], IP2)
  >>> subs.sort()
  >>> subs
  ['sub1q2', 'sub_q2']

You can have subscriptions that are indepenent of any specifications::
  
  >>> registry.subscriptions([], IP1)
  []

  >>> registry.subscribe([], IP2, 'sub2')
  >>> registry.subscriptions([], IP1)
  ['sub2']
  >>> registry.subscribe([], IP1, 'sub1')
  >>> subs = registry.subscriptions([], IP1)
  >>> subs.sort()
  >>> subs
  ['sub1', 'sub2']
  >>> registry.subscriptions([], IP2)
  ['sub2']


=== Zope3/src/zope/interface/adapter.py 1.9 => 1.10 ===
--- Zope3/src/zope/interface/adapter.py:1.9	Mon Mar  8 12:26:56 2004
+++ Zope3/src/zope/interface/adapter.py	Mon Mar 15 15:41:55 2004
@@ -1,4 +1,4 @@
-##############################################################################
+############################################################################
 #
 # Copyright (c) 2002 Zope Corporation and Contributors.
 # All Rights Reserved.
@@ -10,7 +10,7 @@
 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
 # FOR A PARTICULAR PURPOSE.
 #
-##############################################################################
+############################################################################
 """Adapter-style interface registry
 
 This implementationb is based on a notion of "surrogate" interfaces.
@@ -59,19 +59,26 @@
 #   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:
+# dictionary are of several forms:
 
-#   (subscription, specification) -> factories
-
-#      for simple unnamed adapters
+# For single adapters:
+#
+# {specification -> {name -> object}
+#
+# where object is usually a sequence of factories
 
-#   (subscription, specification, name) -> factories
+# For multiple adapters:
+#
+# {(specification, order) -> {name -> {with -> object}}}
 
-#      for named adapters
+# For single subscription adapters:
+#
+# {('s', specification) -> tuple([object])}
 
-#   (subscription, specification, name, order) -> {with -> factories}
+# For multiple-subscription adapters:
+#
+# {('s', specification, order) -> {with -> tuple([object])}}
 
-#      for multi-adapters.  
 
 
 import weakref
@@ -129,26 +136,65 @@
         # override less-specific data.
         ancestors.reverse()
         for ancestor in ancestors:
-            for key, adapters in ancestor.selfImplied.iteritems():
-                subscription = key[0]
+            
+            for key, v in ancestor.selfImplied.iteritems():
+
+                # key is specification or ('s', specification)
+                subscription = isinstance(key, tuple) and key[0] == 's'
                 if subscription:
-                    adapters = tuple(map(tuple, adapters))
-                    implied[key] = tuple(Set(implied.get(key, ()) + adapters))
+                    # v is tuple of subs
+                    implied[key] = implied.get(key, ()) + v
                 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)
+                    oldbyname = implied.get(key)
+                    if not oldbyname:
+                        implied[key] = oldbyname = {}
+                    
+                    # v is name -> object
+                    oldbyname.update(v)
+
+            for key, v in ancestor.multImplied.iteritems():
+                # key is (specification, order)
+                #     or ('s', specification, order)
+                subscription = key[0] == 's'
+                if subscription:
+                    oldwithobs = implied.get(key)
+                    if not oldwithobs:
+                        oldwithobs = implied[key] = {}
+                        
+                    # v is {with -> tuple([object])}
+                    for with, objects in v.iteritems():
+                        oldwithobs[with] = oldwithobs.get(with, ()) + objects
+                    
                 else:
-                    implied[k] = ancestor_adapters.copy()
+                    oldbyname = implied.get(key)
+                    if not oldbyname:
+                        implied[key] = oldbyname = {}
+
+                    # v is {name -> {with -> ?}}
+                    for name, withobs in v.iteritems():
+                        oldwithobs = oldbyname.get(name)
+                        if not oldwithobs:
+                            oldwithobs = oldbyname[name] = {}
+
+                        # withobs is {with -> object}
+                        oldwithobs.update(withobs)
+
+        # Now flatten with mappings to tuples
+        withcmp_ = withcmp
+        for key, v in implied.iteritems():
+            if isinstance(key, tuple) and key[0] == 's':
+                # subscriptions
+                if isinstance(v, dict):
+                    implied[key] = v.items()
+            else:
+                byname = v
+                for name, value in byname.iteritems():
+                    if isinstance(value, dict):
+                        # We have {with -> value}
+                        # convert it to sorted [(with, value]
+                        value = value.items()
+                        value.sort(withcmp_)
+                        byname[name] = value
 
 
         self.get = implied.get
@@ -181,28 +227,25 @@
     def unsubscribe(self, dependent):
         del self.dependents[dependent]
 
-    def _adaptTo(self, specification, factories, name='', with=()):
-        if factories is None:
+    def _adaptTo(self, specification, object, name='', with=()):
+        if object is None:
             try:
                 del self.adapters[False, tuple(with), name, specification]
             except KeyError:
                 pass
         else:
-            self.adapters[False, tuple(with), name, specification] = factories
+            self.adapters[False, tuple(with), name, specification
+                          ] = object
 
         self.dirty()
 
-    def _subscriptionAdaptTo(self, specification, factories, name='', with=()):
-        if factories is None:
+    def _subscriptionAdaptTo(self, specification, object, with=()):
+        if object 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
-
+        key = (True, tuple(with), '', specification)
+        self.adapters[key] = self.adapters.get(key, ()) + (object, )
         self.dirty()
 
     def changed(self, which=None):
@@ -211,11 +254,20 @@
     def __repr__(self):
         return '<%s(%s)>' % (self.__class__.__name__, self.spec())
 
+def withcmp((with1, v1), (with2, v2)):
+    for spec1, spec2 in zip(with1, with2):
+        if spec1.extends(spec2):
+            return -1
+        if spec2.extends(spec1):
+            return 1
+    return 0
+
+
 class AdapterRegistry(object):
-    """Surrogate registry
+    """Adapter registry
     """
 
-    # Implementation node:
+    # Implementation note:
     # 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.
@@ -225,6 +277,8 @@
     def __init__(self):
         default = self._surrogateClass(Default, self)
         self._default = default
+        null = self._surrogateClass(Default, self)
+        self._null = null
         surrogates = {Default.weakref(): default}
         self._surrogates = surrogates
 
@@ -247,630 +301,143 @@
             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)
+    def register(self, required, provided, name, value):
+        if required:
+            required, with = self.get(required[0]), tuple(required[1:])
         else:
+            required = self._null
             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
+        required._adaptTo(provided, value, unicode(name), with)
 
-           >>> 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]
+    def lookup(self, required, provided, name='', default=None):
+        order = len(required)
+        if order == 1:
+            # Simple adapter:
+            s = self.get(required[0])
+            byname = s.get(provided)
+            if byname:
+                value = byname.get(name)
+            else:
+                value = None
+
+            if value is None:
+                byname = self._default.get(provided)
+                if byname:
+                    value = byname.get(name, default)
+                else:
+                    return default
+                
+            return value
+
+        elif order == 0:
+            # null adapter
+            byname = self._null.get(provided)
+            if byname:
+                return byname.get(name, default)
+            else:
+                return default
+
+        # Multi adapter
+
+        with = required[1:]
+        key = provided, order
+
+        for surrogate in self.get(required[0]), self._default:
+            byname = surrogate.get(key)
+            if not byname:
+                continue
+
+            bywith = byname.get(name)
+            if not bywith:
+                continue
+
+            for rwith, value in bywith:
+                for rspec, spec in zip(rwith, with):
+                    if not spec.isOrExtends(rspec):
+                        break # This one is no good
+                else:
+                    return value
 
         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)
+    def lookup1(self, required, provided, name='', default=None):
+        s = self.get(required)
+        byname = s.get(provided)
+        if byname:
+            value = byname.get(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
+            value = None
 
-        return default
+        if value is None:
+            byname = self._default.get(provided)
+            if byname:
+                value = byname.get(name, default)
+            else:
+                return default
 
-    def queryMultiAdapter(self, objects, interface, name=u'',
-                          default=None, raw=False, _subscription=False):
-        """
+        return value
 
-        >>> 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
+    def subscribe(self, required, provided, value):
+        if required:
+            required, with = self.get(required[0]), tuple(required[1:])
         else:
-            return default
-
-    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()
+            required = self._null
+            with = ()
         
+        required._subscriptionAdaptTo(provided, value, with)
 
-        >>> 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:
+    def subscriptions(self, required, provided):
+        order = len(required)
         
-        >>> 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
+        if order == 1:
+            # Simple subscriptions:
+            s = self.get(required[0])
+            result = s.get(('s', provided))
+            if result:
+                result = list(result)
+            else:
+                result = []
+
+            default = self._default.get(('s', provided))
+            if default:
+                result.extend(default)
+                
+            return result
 
-        But using chicken, we can use poultry *and* chicken recipes:
+        elif order == 0:
+            result = self._null.get(('s', provided))
+            if result:
+                return list(result)
+            else:
+                return []
         
-        >>> 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
+        # Multi
+        key = 's', provided, order
+        with = required[1:]
+        result = []
         
-        >>> provide(IDrink, IRecipe, [Meal], with=[IPoultry],name='Joes Diner')
-        >>> adapters = list(query((drink,poultry), IRecipe, name='Joes Diner'))
-        >>> len(adapters)
-        1
+        for surrogate in self.get(required[0]), self._default:
+            bywith = surrogate.get(key)
+            if not bywith:
+                continue
+
+            for rwith, values in bywith:
+                for rspec, spec in zip(rwith, with):
+                    if not spec.isOrExtends(rspec):
+                        break # This one is no good
+                else:
+                    # we didn't break, so we have a match
+                    result.extend(values)
 
-        """
-        return self.queryMultiAdapter(objects, interface, name, default, raw,
-                                      _subscription=True)
+        return result
 
     def getRegisteredMatching(self,
                               required=None,
@@ -919,10 +486,10 @@
 
 
            >>> 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')
+           >>> registry.register([None], P3, '', 'default P3')
+           >>> registry.register([Interface], P3, '', 'any P3')
+           >>> registry.register([R2], P3, '', 'R2 P3')
+           >>> registry.register([R2], P3, 'bob', "bobs R2 P3")
 
            >>> from pprint import PrettyPrinter
            >>> pprint = PrettyPrinter(width=60).pprint
@@ -1054,8 +621,7 @@
                                 continue
 
                         yield (ancestor, target, rwith, aname, factories)
-
-
+        
 
 def mextends(with, rwith):
     if len(with) == len(rwith):
@@ -1065,15 +631,19 @@
         else:
             return True
     return False
-        
 
 def adapterImplied(adapters):
     implied = {}
     multi = {}
+
+    # This dictionary is used to catch situations specific adapters
+    # override less specific adapters.
+    # Because subscriptions are cummulative, registered doesn't apply.
     registered = {}
+
     # Add adapters and interfaces directly implied by same:
 
-    for key, factories in adapters.iteritems():
+    for key, value in adapters.iteritems():
 
         # XXX Backward compatability
         # Don't need to handle 3-tuples some day
@@ -1082,62 +652,86 @@
         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)
+
+        if subscription:
+            if with:
+                _add_multi_sub_adapter(with, target, multi, value)
+            else:
+                _add_named_sub_adapter(target, implied, value)
         else:
-            _add_adapter(target, target, implied, registered, factories,
-                         subscription)
+            if with:
+                _add_multi_adapter(with, name, target, target, multi,
+                                   registered, value)
+            else:
+                _add_named_adapter(target, target, name, implied,
+                                   registered, value)
 
     return implied, multi
 
-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, value):
+    
+    ikey = target
+    rkey = target, name
+
+    byname = implied.get(ikey)
+    if not byname:
+        byname = implied[ikey] = {}
 
-def _add_named_adapter(target, provided, name,
-                        implied, registered, factories, subscription):
-    key = subscription, target, name
-    if (key not in implied
+    if (name not in byname
         or
-        (key in registered and registered[key].extends(provided))
+        (rkey in registered and registered[rkey].extends(provided))
         ):
-        registered[key] = provided
-        implied[key] = factories
+
+        registered[rkey] = provided
+        byname[name] = value
+
         for b in target.__bases__:
-            _add_named_adapter(b, provided, name,
-                               implied, registered, factories, subscription)
+            _add_named_adapter(b, provided, name, implied,
+                               registered, value)
 
-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
+def _add_multi_adapter(with, name, target, provided, implied,
+                       registered, object):
 
-    key = key, interfaces # The full key has all 5
-    if key not in registered or registered[key].extends(provided):
+    ikey = target, (len(with) + 1)
+    byname = implied.get(ikey)
+    if not byname:
+        byname = implied[ikey] = {}
+
+    bywith = byname.get(name)
+    if not bywith:
+        bywith = byname[name] = {}
+
+    
+    rkey = ikey, name, with # The full key has all 4
+    if (with not in bywith
+        or
+        (rkey not in registered or registered[rkey].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
+        registered[rkey] = provided
+        bywith[with] = object
+
+    for b in target.__bases__:
+        _add_multi_adapter(with, name, b, provided, implied,
+                           registered, object)
 
+def _add_named_sub_adapter(target, implied, objects):
+    key = ('s', target)
+    implied[key] = implied.get(key, ()) + objects
+    
     for b in target.__bases__:
-        _add_multi_adapter(interfaces, name, b, provided, implied,
-                           registered, factories, subscription)
+        _add_named_sub_adapter(b, implied, objects)
+
+def _add_multi_sub_adapter(with, target, implied, objects):
+    key = 's', target, (len(with) + 1)
+    bywith = implied.get(key)
+    if not bywith:
+        bywith = implied[key] = {}
 
+    bywith[with] = bywith.get(with, ()) + objects
+
+    for b in target.__bases__:
+        _add_multi_sub_adapter(with, b, implied, objects)


=== Zope3/src/zope/interface/interfaces.py 1.22 => 1.23 ===
--- Zope3/src/zope/interface/interfaces.py:1.22	Mon Mar  8 12:26:56 2004
+++ Zope3/src/zope/interface/interfaces.py	Mon Mar 15 15:41:55 2004
@@ -687,69 +687,51 @@
         the given interfaces is returned.
         """
 
-class IInterfaceDeclarationYAGNI(Interface):
-    """YAGNI interface declaration API
+class IAdapterRegistry(Interface):
+    """Provide an interface-based registry for adapters
 
-    The functions in this interface are functions that might be
-    provided later, but that introduce difficulties that we choose to
-    avoid now.
-    """
-
-    def unimplements(*interfaces):
-        """Declare interfaces not 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 removed from 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 classUnimplements. For example::
-
-          unimplements(I1)
+    This registry registers objects that are in some sense "from" a
+    sequence of specification to an interface and a name.
 
-        is equivalent to calling::
-
-          classUnimplements(I1)
-
-        after the class has been created.
-
-        Consider the following example::
-
-          class C(A, B):
-            unimplements(I1, I2)
+    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
 
-        Instances of ``C`` don't implement ``I1``, ``I2``, even if
-        instances of ``A`` and ``B`` do.
+        A value is registered for a *sequence* of required specifications, a
+        provided interface, and a name.
         """
 
-    def classUnimplements(class_, *interfaces):
-        """Declare the interfaces not implemented for instances of a class
+    def lookup(required, provided, name, default=None):
+        """Lookup a value
 
-        The arguments after the class are one or more interfaces or
-        interface specifications (IDeclaration objects).
+        A value is looked up based on a *sequence* of required
+        specifications, a provided interface, and a name.
+        """
 
-        The interfaces given (including the interfaces in the
-        specifications) cancel previous declarations for the same
-        interfaces, including declarations made in base classes.
+    def names(required, provided):
+        """Return the names for which there are registered objects
+        """
 
-        Consider the following example::
+    def subscribe(required, provided, subscriber):
+        """Register a subscriber
 
-          class C(A, B):
-             ...
+        A subscriber is registered for a *sequence* of required
+        specifications, a provided interface, and a name.
 
-          classImplements(C, I1)
-          classUnimplements(C, I1, I2)
+        Multiple subscribers may be registered for the same (or
+        equivalent) interfaces.
+        """
 
+    def subscriptions(required, provided):
+        """Get a sequence of subscribers
 
-        Instances of ``C`` don't provide ``I1`` and ``I2`` even if
-        instances of ``A`` or ``B`` do.
+        Subscribers for a *sequence* of required interfaces, and a provided
+        interface are returned.
         """
+    




More information about the Zope3-Checkins mailing list