[Zope3-checkins] SVN: Zope3/branches/srichter-blow-services/src/zope/app/component/ Made local adapter registries work. Added a text documentation file to

Stephan Richter srichter at cosmos.phy.tufts.edu
Wed Jan 5 11:27:54 EST 2005


Log message for revision 28731:
  Made local adapter registries work. Added a text documentation file to 
  this effect. I also updated the original adapter registry tests, which 
  are very comprehensive. They also pass.
  
  

Changed:
  U   Zope3/branches/srichter-blow-services/src/zope/app/component/__init__.py
  U   Zope3/branches/srichter-blow-services/src/zope/app/component/adapter.py
  A   Zope3/branches/srichter-blow-services/src/zope/app/component/adapterregistry.txt
  U   Zope3/branches/srichter-blow-services/src/zope/app/component/configure.zcml
  U   Zope3/branches/srichter-blow-services/src/zope/app/component/interfaces/__init__.py
  U   Zope3/branches/srichter-blow-services/src/zope/app/component/interfaces/registration.py
  U   Zope3/branches/srichter-blow-services/src/zope/app/component/site.py
  A   Zope3/branches/srichter-blow-services/src/zope/app/component/tests/test_adapter.py

-=-
Modified: Zope3/branches/srichter-blow-services/src/zope/app/component/__init__.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/component/__init__.py	2005-01-05 16:23:49 UTC (rev 28730)
+++ Zope3/branches/srichter-blow-services/src/zope/app/component/__init__.py	2005-01-05 16:27:53 UTC (rev 28731)
@@ -22,17 +22,17 @@
 
 import sys
 import zope.app
-from zope.app.component.bbb import registration
-sys.modules['zope.app.registration'] = registration
-zope.app.registration = registration
+from zope.app.component.bbb import registration as bbb_registration
+sys.modules['zope.app.registration'] = bbb_registration
+zope.app.registration = bbb_registration
 from zope.app.component.bbb import localservice
 sys.modules['zope.app.component.localservice'] = localservice
 from zope.app.component.bbb import site
 sys.modules['zope.app.site'] = site
 zope.app.site = site
-from zope.app.component.bbb import adapter
-sys.modules['zope.app.adapter'] = adapter
-zope.app.adapter = adapter
+from zope.app.component.bbb import adapter as bbb_adapter
+sys.modules['zope.app.adapter'] = bbb_adapter
+zope.app.adapter = bbb_adapter
 from zope.app.component.bbb import utility
 sys.modules['zope.app.utility'] = utility
 zope.app.utility = utility

Modified: Zope3/branches/srichter-blow-services/src/zope/app/component/adapter.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/component/adapter.py	2005-01-05 16:23:49 UTC (rev 28730)
+++ Zope3/branches/srichter-blow-services/src/zope/app/component/adapter.py	2005-01-05 16:27:53 UTC (rev 28731)
@@ -11,41 +11,34 @@
 # FOR A PARTICULAR PURPOSE.
 #
 ##############################################################################
-"""Adapter Service
+"""Local/Persistent Adapter Registry
 
 $Id$
 """
 __docformat__ = 'restructuredtext' 
-import sys
-from persistent.dict import PersistentDict
-from persistent import Persistent
+import persistent
 
 import zope.component.adapter
 import zope.interface
 import zope.schema
-from zope.interface.adapter import adapterImplied, Default
-from zope.interface.adapter import Surrogate, AdapterRegistry
 from zope.security.proxy import removeSecurityProxy
 
 import zope.app.component.localservice
 import zope.app.container.contained
 import zope.app.site.interfaces
-import zope.component.interfaces
-from zope.app import zapi
-from zope.app.component.interfaces.registration import IRegistry
+from zope.app.component import registration
+from zope.app.component import interfaces
 from zope.app.i18n import ZopeMessageIDFactory as _
 
 
-class LocalSurrogate(Surrogate):
-    """Local surrogates
+class LocalSurrogate(zope.interface.adapter.Surrogate):
+    """Local Surrogate
 
-    Local surrogates are transient, rather than persistent.
-
-    Their adapter data are stored in their registry objects.
+    Local surrogates are transient, rather than persistent. Their adapter
+    data are stored in their registry objects.
     """
-
     def __init__(self, spec, registry):
-        Surrogate.__init__(self, spec, registry)
+        super(LocalSurrogate, self).__init__(spec, registry)
         self.registry = registry
         registry.baseFor(spec).subscribe(self)
 
@@ -59,32 +52,40 @@
         else:
             adapters = base.adapters
         self.adapters = adapters
-        Surrogate.clean(self)
+        super(LocalSurrogate, self).clean()
 
 
-class LocalAdapterRegistry(AdapterRegistry, Persistent):
+class LocalAdapterRegistry(zope.interface.adapter.AdapterRegistry,
+                           persistent.Persistent):
     """Local/persistent surrogate registry"""
-
-    zope.interface.implements(IRegistry)
+    zope.interface.implements(interfaces.ILocalAdapterRegistry)
     
     _surrogateClass = LocalSurrogate
 
-    # Next local registry, may be None
-    nextRegistry = None
+    # See interfaces.registration.ILocatedRegistry
+    next = None
+    subs = ()
 
-    subRegistries = ()
-
     def __init__(self, base, next=None):
-
         # Base registry. This is always a global registry
         self.base = base
-
+        # `adapters` is simple dict, since it is populated during every load
         self.adapters = {}
-        self.registrations = PersistentDict()
-        AdapterRegistry.__init__(self)
-        self.setNextRegistry(next)
+        self._registrations = ()
+        super(LocalAdapterRegistry, self).__init__()
+        self.setNext(next)
 
-    def setNextRegistry(self, next, base=None):
+    def addSub(self, sub):
+        """See interfaces.registration.ILocatedRegistry"""
+        self.subs += (sub, )
+
+    def removeSub(self, sub):
+        """See interfaces.registration.ILocatedRegistry"""
+        self.subs = tuple(
+            [s for s in self.subs if s is not sub] )
+
+    def setNext(self, next, base=None):
+        """See interfaces.registration.ILocatedRegistry"""
         if base is not None:
             self.base = base
         if self.next is not None:
@@ -94,14 +95,32 @@
         self.next = next
         self.adaptersChanged()
 
-    def addSubRegistry(self, sub):
-        self.subs += (sub, )
+    def _getKey(self, registration):
+        """Return the key of a registration."""
+        return (False, registration.with,
+                registration.name, registration.provided)
 
-    def removeSubRegistry(self, sub):
-        self.subs = tuple([s for s in self.subs if s is not sub])
+    def register(self, registration):
+        """See zope.app.component.interfaces.registration.IRegistry"""
+        self._registrations += (registration,)
+        self.adaptersChanged()
 
+    def unregister(self, registration):
+        """See zope.app.component.interfaces.registration.IRegistry"""
+        self._registrations = tuple([reg for reg in self._registrations
+                                     if reg is not registration])
+        self.adaptersChanged()
+
+    def registered(self, registration):
+        """See zope.app.component.interfaces.registration.IRegistry"""
+        return registration in self._registrations
+
+    def registrations(self):
+        """See zope.app.component.interfaces.registration.IRegistry"""
+        return self._registrations
+
     def __getstate__(self):
-        state = Persistent.__getstate__(self).copy()
+        state = super(LocalAdapterRegistry, self).__getstate__().copy()
         
         for name in ('_default', '_null', 'adapter_hook',
                      'lookup', 'lookup1', 'queryAdapter', 'get',
@@ -111,35 +130,35 @@
         return state
 
     def __setstate__(self, state):
-        Persistent.__setstate__(self, state)
-        AdapterRegistry.__init__(self)
+        super(LocalAdapterRegistry, self).__setstate__(state)
+        super(LocalAdapterRegistry, self).__init__()
     
     def baseFor(self, spec):
+        """Used by LocalSurrogate"""
         return self.base.get(spec)
 
     def _updateAdaptersFromLocalData(self, adapters):
-        for required, stacks in self.stacks.iteritems():
+        """Update all adapter surrogates locally."""
+        for registration in self._registrations:
+            required = registration.required
             if required is None:
-                required = Default
+                required = zope.interface.adapter.Default
             radapters = adapters.get(required)
             if not radapters:
                 radapters = {}
                 adapters[required] = radapters
 
-            for key, stack in stacks.iteritems():
-                registration = stack.active()
-                if registration is not None:
+            # Needs more thought:
+            # We have to remove the proxy because we're
+            # storing the value amd we can't store proxies.
+            # (Why can't we?)  we need to think more about
+            # why/if this is truly safe
+            key = self._getKey(registration)
+            radapters[key] = removeSecurityProxy(registration.component)
 
-                    # Needs more thought:
-                    # We have to remove the proxy because we're
-                    # storing the value amd we can't store proxies.
-                    # (Why can't we?)  we need to think more about
-                    # why/if this is truly safe
-                    
-                    radapters[key] = removeSecurityProxy(registration.factory)
 
     def adaptersChanged(self):
-
+        """See interfaces.registration.ILocalAdapterRegistry"""
         adapters = {}
         if self.next is not None:
             for required, radapters in self.next.adapters.iteritems():
@@ -152,74 +171,45 @@
 
             # Throw away all of our surrogates, rather than dirtrying
             # them individually
-            AdapterRegistry.__init__(self)
+            super(LocalAdapterRegistry, self).__init__()
 
             for sub in self.subs:
                 sub.adaptersChanged()
 
     def baseChanged(self):
-        """Someone changed the base service
-
-        This should only happen during testing
-        """
-        AdapterRegistry.__init__(self)
+        """See interfaces.registration.ILocalAdapterRegistry"""
+        super(LocalAdapterRegistry, self).__init__()
         for sub in self.subs:
             sub.baseChanged()
-                
 
-    def registrations(self):
-        for stacks in self.stacks.itervalues():
-            for stack in stacks.itervalues():
-                for info in stack.info():
-                    yield info['registration']
 
+class AdapterRegistration(registration.ComponentRegistration):
+    """A simple implementation of the adapter registration interface."""
+    zope.interface.implements(interfaces.IAdapterRegistration)
 
-class LocalAdapterBasedService(
-    zope.app.container.contained.Contained,
-    Persistent,
-    ):
-    """A service that uses local surrogate registries
+    def __init__(self, required, provided, factory,
+                 name='', permission=None, registry=None):
+        if not isinstance(required, (tuple, list)):
+            self.required = required
+            self.with = ()
+        else:
+            self.required = required[0]
+            self.with = tuple(required[1:])
+        self.provided = provided
+        self.name = name
+        self.component = factory
+        self.permission = permission
+        self.registry = registry
 
-    A local surrogate-based service needs to maintain connections
-    between it's surrogate registries and those of containing ans
-    sub-services.
+    def getRegistry(self):
+        return self.registry
 
-    The service must implement a `setNext` method that will be called
-    with the next local service, which may be ``None``, and the global
-    service. This method will be called when a service is bound.
-    
-    """
-
-    zope.interface.implements(
-        zope.app.site.interfaces.IBindingAware,
-        )
-
-    def __updateNext(self, servicename):
-        next = zope.app.component.localservice.getNextService(
-            self, servicename)
-        global_ = zapi.getGlobalServices().getService(servicename)
-        if next == global_:
-            next = None
-        self.setNext(next, global_)
-        
-    def bound(self, servicename):
-        self.__updateNext(servicename)
-        
-        # Now, we need to notify any sub-site services. This is
-        # a bit complicated because our immediate subsites might not have
-        # the same service. Sigh
-        sm = zapi.getServices(self)
-        self.__notifySubs(sm.subSites, servicename)
-
-    def unbound(self, servicename):
-        sm = zapi.getServices(self)
-        self.__notifySubs(sm.subSites, servicename)
-
-    def __notifySubs(self, subs, servicename):
-        for sub in subs:
-            s = sub.queryLocalService(servicename)
-            if s is not None:
-                s.__updateNext(servicename)
-            else:
-                self.__notifySubs(sub.subSites, servicename)
-
+    def __repr__(self):
+        return ('<%s: ' %self.__class__.__name__ +
+                'required=%r, ' %self.required +
+                'with=' + `self.with` + ', ' +
+                'provided=%r, ' %self.provided +
+                'name=%r, ' %self.name +
+                'component=%r, ' %self.component +
+                'permission=%r' %self.permission +
+                '>')

Added: Zope3/branches/srichter-blow-services/src/zope/app/component/adapterregistry.txt
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/component/adapterregistry.txt	2005-01-05 16:23:49 UTC (rev 28730)
+++ Zope3/branches/srichter-blow-services/src/zope/app/component/adapterregistry.txt	2005-01-05 16:27:53 UTC (rev 28731)
@@ -0,0 +1,213 @@
+==========================
+The Local Adapter Registry
+==========================
+
+The local adapter registry, like its global parent at
+`zope.interface.adapter`, is responsible for managing registered
+adapters. However, local adapter registries must always be instantiated by
+providing their base (or global registry), which will always be a global
+adapter registry isntance:
+
+  >>> import zope.interface
+  >>> gar = zope.interface.adapter.AdapterRegistry()
+
+  >>> from zope.app.component import adapter
+  >>> lar = adapter.LocalAdapterRegistry(gar)
+  >>> lar.base is gar
+  True
+
+The local adapter registry uses the registration framework (introduced in
+`registration.txt`) to manage the adapter registrations. To demonstrate we
+need to create an adapter first. Let's say we have a file
+
+  >>> class IFile(zope.interface.Interface):
+  ...     content = zope.interface.Attribute('File Content')
+
+and we want to know the size of the file, which can be gotten by having a
+component providing the `ISized` interface:
+
+  >>> class ISized(zope.interface.Interface):
+  ...     def getSize():
+  ...         """Return the size of the component."""
+
+  >>> FileSize = '<FileSize>'
+
+As you can see, the adapter itself has no meaning in the adapter registry,
+which is merely responsible for looking up the component, but asserts no
+interpretation. Thus the adapter component can be any Python object.
+
+Instead of registering the adapter with the registry directly, we use an
+adapter registration to register the adapter with the local adapter registry:
+
+  >>> reg = adapter.AdapterRegistration(
+  ...         required = (IFile,), 
+  ...         provided = ISized,
+  ...         factory = FileSize,
+  ...         registry = lar)
+  >>> reg.status
+  u'Inactive'
+
+The adapter registration is an extended `ComponentRegistration`. Here the
+factory is the component. We can register the registration with the adapter
+registry using
+ 
+  >>> lar.register(reg)
+
+Note that the registration's status is automatically set to active, when you
+register a registration:
+
+  >>> reg.status
+  u'Active'
+
+What really happens behind the scene is that the registry keeps track of a
+list of registrations and the registration's status property calls the
+registry's `registered()` method to determine whether the registration is
+activated. 
+
+  >>> lar.registered(reg)
+  True
+
+You can also ask the registry for all of its registrations:
+
+  >>> lar.registrations() #doctest: +NORMALIZE_WHITESPACE
+  (<AdapterRegistration: 
+        required=<InterfaceClass __builtin__.IFile>, 
+        with=(), 
+        provided=<InterfaceClass __builtin__.ISized>, 
+        name='', 
+        component='<FileSize>', 
+        permission=None>,)
+
+Later you can unregister the adapter registration:
+
+  >>> lar.unregister(reg)
+  >>> lar.registered(reg)
+  False
+  >>> lar.registrations()
+  ()
+  >>> reg.status
+  u'Inactive'
+
+Of course, the same can be achieved by setting the registration's status:
+
+  >>> from zope.app.component import interfaces
+  >>> reg.status = interfaces.registration.ActiveStatus
+  >>> lar.registered(reg)
+  True
+
+  >>> reg.status = interfaces.registration.InactiveStatus
+  >>> lar.registered(reg)
+  False
+
+But the true flexibility of the local adapter registry is that it can be
+located in an adapter registry tree. Each node of the tree is a location and
+can override existing and register new adapters. The parent of a node can be
+accessed using
+
+  >>> lar.next is None
+  True
+
+In our case there is no nect registry, since the `lar` instance is the root
+node. The base registry, which is always a global one, can be accessed using
+
+  >>> lar.base is gar
+  True
+
+The node also knows about its children via
+
+  >>> lar.subs
+  ()
+
+Thus this is a double-linked tree. If I now create a second local adapter
+registry in which `lar` is the parent
+
+  >>> lar2 = adapter.LocalAdapterRegistry(gar, lar)
+
+then we have
+
+  >>> lar2.next is lar
+  True
+  >>> lar.subs == (lar2,)
+  True
+  >>> lar2.base is lar.base is gar
+  True
+
+Let's now register our adapter with `lar` again:
+
+  >>> reg.status = interfaces.registration.ActiveStatus
+
+On the second level, however, the size should be a word count instead of a
+character count:
+
+  >>> FileWordCount = '<FileWordCount>'
+
+
+  >>> reg2 = adapter.AdapterRegistration(
+  ...          required = (IFile,), 
+  ...          provided = ISized,
+  ...          factory = FileWordCount,
+  ...          registry = lar2)
+  >>> reg2.status = interfaces.registration.ActiveStatus
+
+If we now lookup the adapter in `lar`, we get the original `ISized` adapter:
+
+  >>> lar.lookup((IFile,), ISized)
+  '<FileSize>'
+
+but from the second registry we get
+
+  >>> lar2.lookup((IFile,), ISized)
+  '<FileWordCount>'
+
+If we now unregister the word counting size adapter
+
+  >>> reg2.status = interfaces.registration.InactiveStatus
+
+then `lar2` will get the adapter from its parent:
+
+  >>> lar2.lookup((IFile,), ISized)
+  '<FileSize>'
+
+We can also change the location of a local adapter registry using
+
+  >>> lar2.setNext(None)
+  >>> lar2.next
+  >>> lar.subs
+  ()
+
+In this case we made `lar2` a root node itself. Clearly, now the `FileSize`
+adapter should not be available anymore:
+
+  >>> lar2.lookup((IFile,), ISized)
+
+Now we make `lar` a sub registry of `lar2`:
+
+  >>> lar.setNext(lar2)
+  >>> lar.next is lar2
+  True
+  >>> lar2.subs == (lar,)
+  True
+
+Note that you should never set the next attribute directly, but always use the
+`setNext()` method, since it ensures the integrety of all the links in the
+tree and updates the adapter lookup caches. 
+
+The global adapter registry is taken into account during the lookup too, of
+course. Let's say we want to have an adapter that determines the type of the
+file and register it globally:
+
+  >>> class IFileType(zope.interface.Interface):
+  ...     def getType():
+  ...         """Return the type of the file."""
+
+  >>> FileType = '<FileType>'
+
+  >>> gar.register((IFile,), IFileType, '', FileType)
+
+Then this adapter will be available in any local adapter registry:
+
+  >>> lar.lookup((IFile,), IFileType, '')
+  '<FileType>'
+  >>> lar2.lookup((IFile,), IFileType, '')
+  '<FileType>'
+

Modified: Zope3/branches/srichter-blow-services/src/zope/app/component/configure.zcml
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/component/configure.zcml	2005-01-05 16:23:49 UTC (rev 28730)
+++ Zope3/branches/srichter-blow-services/src/zope/app/component/configure.zcml	2005-01-05 16:27:53 UTC (rev 28731)
@@ -1,23 +1,5 @@
 <configure xmlns="http://namespaces.zope.org/zope">
 
-  <serviceType
-      id="Utilities"
-      interface="zope.component.interfaces.IUtilityService" />
-
-  <service
-      serviceType="Utilities"
-      permission="zope.Public"
-      factory="zope.component.utility.GlobalUtilityService" />
-
-  <serviceType
-      id="Adapters"
-      interface="zope.component.interfaces.IAdapterService" />
-
-  <service
-      serviceType="Adapters"
-      permission="zope.Public"
-      factory="zope.component.adapter.GlobalAdapterService" />
-
   <vocabulary
       name="Interfaces"
       factory="zope.app.utility.vocabulary.UtilityVocabulary"

Modified: Zope3/branches/srichter-blow-services/src/zope/app/component/interfaces/__init__.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/component/interfaces/__init__.py	2005-01-05 16:23:49 UTC (rev 28730)
+++ Zope3/branches/srichter-blow-services/src/zope/app/component/interfaces/__init__.py	2005-01-05 16:27:53 UTC (rev 28731)
@@ -24,19 +24,40 @@
 from zope.app.i18n import ZopeMessageIDFactory as _
 import registration
 
+class ILocalAdapterRegistry(registration.IRegistry,
+                            registration.ILocatedRegistry):
+
+    def adaptersChanged():
+        """Update the adapter surrogates, since the registrations changed."""
+
+    def baseChanged():
+        """Someone changed the base registry
+
+        This should only happen during testing
+        """
+
 class IComponentManager(zope.interface.Interface):
 
-    def queryComponent(type=None, filter=None, all=0):
+    def queryComponent(type=None, filter=None, all=True):
         """Return all components that match the given type and filter
 
-        The objects are returned a sequence of mapping objects with keys:
+        The arguments are:
 
-        path -- The component path
+        type -- An argument is the interface a returned component must
+                provide.
 
-        component -- The component
+        filter -- A Python expression that must evaluate to `True` for any
+                  returned component; `None` means that no filter has been
+                  specified.
 
         all -- A flag indicating whether all component managers in
                this place should be queried, or just the local one.
+
+        The objects are returned a sequence of mapping objects with keys:
+
+        path -- The component path
+
+        component -- The component
         """
 
 class IBindingAware(zope.interface.Interface):
@@ -84,31 +105,6 @@
     services.
     """
 
-    def queryRegistrations(name, default=None):
-        """Return an IRegistrationRegistry for the registration name.
-
-        queryRegistrationsFor(cfg, default) is equivalent to
-        queryRegistrations(cfg.name, default)
-        """
-
-    def createRegistrationsFor(registration):
-        """Create and return an IRegistrationRegistry for the registration
-        name.
-
-        createRegistrationsFor(cfg, default) is equivalent to
-        createRegistrations(cfg.name, default)
-        """
-
-    def listRegistrationNames():
-        """Return a list of all registered registration names.
-        """
-
-    def queryActiveComponent(name, default=None):
-        """Finds the registration registry for a given name, checks if it has
-        an active registration, and if so, returns its component.  Otherwise
-        returns default.
-        """
-
     def addSubsite(subsite):
         """Add a subsite of the site
 
@@ -152,14 +148,6 @@
             ),
         )
 
-class ISiteManagementFolders(IContainer, IComponentManager):
-    """A collection of ISiteManagementFolder objects.
-
-    An ISiteManagementFolders object supports simple containment as
-    well as package query and lookup.
-    
-    """
-
 class ILocalUtility(registration.IRegisterable):
     """Local utility marker.
 
@@ -173,20 +161,32 @@
     """
 
 
+class IAdapterRegistration(registration.IComponentRegistration):
+    """Local Adapter Registration for Local Adapter Registry
 
-
-
-class IAdapterRegistration(registration.IRegistration):
-
+    The adapter registration is used to provide local adapters via the
+    adapter registry. It is an extended component registration, whereby the
+    component is the adapter factory in this case.
+    """
     required = zope.schema.Choice(
-        title = _(u"For interface"),
-        description = _(u"The interface of the objects being adapted"),
+        title = _("For interface"),
+        description = _("The interface of the objects being adapted"),
         vocabulary="Interfaces",
-        readonly = True)
+        readonly = True,
+        required=False,
+        default=None)
 
+    with = zope.schema.Tuple(
+        title = _("With interfaces"),
+        description = _("Additionally required interfaces"),
+        readonly=True,
+        value_type = zope.schema.Choice(vocabulary='Interfaces'),
+        required=False,
+        default=())
+
     provided = zope.schema.Choice(
-        title = _(u"Provided interface"),
-        description = _(u"The interface provided"),
+        title = _("Provided interface"),
+        description = _("The interface provided"),
         vocabulary="Interfaces",
         readonly = True,
         required = True)
@@ -197,38 +197,31 @@
         required=False,
         )
 
-    factoryName = zope.schema.BytesLine(
-        title=_(u"The dotted name of a factory for creating the adapter"),
-        readonly = True,
-        required = True,
-        )
-
     permission = zope.schema.Choice(
-        title=_(u"The permission required for use"),
+        title=_("The permission required for use"),
         vocabulary="Permission Ids",
         readonly=False,
         required=False,
         )
-        
-    factory = zope.interface.Attribute(
-        _("Factory to be called to construct the component")
-        )
 
-class IUtilityRegistration(registration.IComponentRegistration):
+
+class IUtilityRegistration(IAdapterRegistration):
     """Utility registration object.
 
-    This is keyed off name (which may be empty) and interface. It inherits the
-    `component` property.
+    Adapter registries are also used to to manage utilities, since utilities
+    are adapters that are instantiated and have no required interfaces. Thus,
+    utility registrations must fulfill all requirements of an adapter
+    registration as well.
     """
 
     name = zope.schema.TextLine(
         title=_("Register As"),
-        description=_("The name that is registered"),
+        description=_("The name under which the utility will be known."),
         readonly=True,
         required=True,
         )
 
-    interface = zope.schema.Choice(
+    provided = zope.schema.Choice(
         title=_("Provided interface"),
         description=_("The interface provided by the utility"),
         vocabulary="Utility Component Interfaces",

Modified: Zope3/branches/srichter-blow-services/src/zope/app/component/interfaces/registration.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/component/interfaces/registration.py	2005-01-05 16:23:49 UTC (rev 28730)
+++ Zope3/branches/srichter-blow-services/src/zope/app/component/interfaces/registration.py	2005-01-05 16:27:53 UTC (rev 28731)
@@ -132,6 +132,50 @@
         """
 
 
+class ILocatedRegistry(zope.component.interfaces.IRegistry):
+    """A registry that is located in a tree of registries.
+
+    
+    """
+    next = Attribute("Set the next local registry in the tree. This attribute "
+                     "represents the parent of this registry node. If the "
+                     "value is `None`, then this registry represents the "
+                     "root of the tree")
+
+    subs = Attribute("A collection of registries that describe the next level "
+                     "of the registry tree. They are the children of this "
+                     "registry node. This attribute should never be "
+                     "manipulated manually. Use `addSub()` and `removeSub()` "
+                     "instead.")
+
+    base = Attribute("Outside of the local registry tree lies the global "
+                     "registry, which is known as the base to every local "
+                     "registry in the tree.")
+
+    def addSub(sub):
+        """Add a new sub-registry to the node.
+
+        Important: This method should *not* be used manually. It is
+        automatically called by `setNext()`. To add a new registry to the
+        tree, use `sub.setNext(self, self.base)` instead!
+        """
+
+    def removeSub(sub):
+        """Remove a sub-registry to the node.
+
+        Important: This method should *not* be used manually. It is
+        automatically called by `setNext()`. To remove a registry from the
+        tree, use `sub.setNext(None)` instead!
+        """
+
+    def setNext(next, base=None):
+        """Set the next/parent registry in the tree.
+
+        This method should ensure that all relevant registies are updated
+        correctly as well.
+        """
+
+
 class IRegistrationManager(IContainerNamesContainer):
     """Manage Registrations"""
 

Modified: Zope3/branches/srichter-blow-services/src/zope/app/component/site.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/component/site.py	2005-01-05 16:23:49 UTC (rev 28730)
+++ Zope3/branches/srichter-blow-services/src/zope/app/component/site.py	2005-01-05 16:27:53 UTC (rev 28731)
@@ -13,15 +13,14 @@
 ##############################################################################
 """Site and Local Site Manager implementation
 
-A local manager has a number of roles:
+A local site manager has a number of roles:
 
-  - A service service
+  - A local site manager, that provides a local adapter and utility registry. 
 
-  - A place to do TTW development or to manage database-based code
+  - A place to do TTW development and/or to manage database-based code.
 
-  - A registry for persistent modules.  The Zope import hook uses the
-    ServiceManager to search for modules.  (This functionality will
-    eventually be replaced by a separate module service.)
+  - A registry for persistent modules.  The Zope 3 import hook uses the
+    SiteManager to search for modules.
 
 $Id$
 """
@@ -31,13 +30,13 @@
 import zope.event
 import zope.interface
 from zope.component.exceptions import ComponentLookupError
+from zope.component.site import SiteManager
 
 from zope.app import zapi
+from zope.app.component import adapter
+from zope.app.component import interfaces
+from zope.app.component import registration
 from zope.app.component.hooks import setSite
-from zope.app.component.interfaces.registration import IRegistry
-from zope.app.component.interfaces.registration import IRegisterableContainer
-from zope.app.component.registration import ComponentRegistration
-from zope.app.component.registration import RegistrationStack
 from zope.app.container.btree import BTreeContainer
 from zope.app.container.constraints import ItemTypePrecondition
 from zope.app.container.contained import Contained
@@ -46,11 +45,9 @@
 from zope.app.location import inside
 from zope.app.traversing.interfaces import IContainmentRoot
 
-from zope.app.site.interfaces import IPossibleSite, ISite, ISiteManager
 
-
 class SiteManagementFolder(RegisterableContainer, BTreeContainer):
-    implements(ISiteManagementFolder)
+    implements(interfaces.ISiteManagementFolder)
 
 class SMFolderFactory(object):
     implements(IDirectoryFactory)
@@ -61,47 +58,53 @@
     def __call__(self, name):
         return SiteManagementFolder()
 
-class SiteManagementFolders(BTreeContainer):
-    pass 
 
+class LocalSiteManager(BTreeContainer, PersistentModuleRegistry, SiteManager):
+    """Local Site Manager implementation"""
+    zope.interface.implements(
+        interfaces.ILocalSiteManager,
+        interfaces.registrations.IRegisterableContainerContainer)
 
-class LocalSiteManager(BTreeContainer, PersistentModuleRegistry):
-
-    zope.interface.implements(ILocalSiteManager,
-                              IRegisterableContainerContainer,
-                              IRegistry)
-
     def __init__(self, site):
-        self._bindings = {}
+        # Locate the site manager
         self.__parent__ = site
         self.__name__ = '++etc++site'
+        # Make sure everything is setup correctly
         BTreeContainer.__init__(self)
         PersistentModuleRegistry.__init__(self)
+        # Set up adapter registries
+        self.adapters = adapter.LocalAdapterRegistry()
+        self.utilities = adapter.LocalAdapterRegistry()
+        # Initialize all links
         self.subSites = ()
         self._setNext(site)
+        # Setup default site management folder
         folder = SiteManagementFolder()
         zope.event.notify(objectevent.ObjectCreatedEvent(folder))
         self['default'] = folder
 
     def _setNext(self, site):
-        """Find set the next service manager
-        """
-        while True:
+        """Find and set the next site manager"""
+        next = None
+        while next is None:
             if IContainmentRoot.providedBy(site):
                 # we're the root site, use the global sm
-                self.next = zapi.getGlobalServices()
-                return
-            site = site.__parent__
-            if site is None:
-                raise TypeError("Not enough context information")
+                next = zapi.getGlobalServices()
+
+            site = zapi.getParent(site)
+
             if ISite.providedBy(site):
-                self.next = site.getSiteManager()
-                self.next.addSubsite(self)
+                next = site.getSiteManager()
+                next.addSubsite(self)
                 return
 
+        self.next = next
+        self.adapters.setNextRegistry(next.adapters)
+        self.utilities.setNextRegistry(next.utilities)
+
+
     def addSubsite(self, sub):
-        """See ISiteManager interface
-        """
+        """See ILocalSiteManager interface"""
         subsite = sub.__parent__
 
         # Update any sites that are now in the subsite:
@@ -115,42 +118,44 @@
 
         subsites.append(sub)
         self.subSites = tuple(subsites)
+        self.adapters.addSubRegistry(sub.adapters)
+        self.utilities.addSubRegistry(sub.utilities)
 
-    def queryRegistrationsFor(self, cfg, default=None):
-        """See IRegistry"""
-        return self.queryRegistrations(cfg.name, default)
+    def __getRegistry(registration):
+        """Determine the correct registry for the registration."""
+        if IAdapterRegistration.providedBy(registration):
+            return self.adapters
+        elif IUtilityRegistration.providedBy(registration):
+            return self.utilities
+        raise ValueError, \
+              ("Unable to detect registration type or registration "
+               "type is not supported. The registration object must provide "
+               "`IAdapterRegistration` or `IUtilityRegistration`.")
 
-    def queryRegistrations(self, name, default=None):
-        """See INameRegistry"""
-        return self._bindings.get(name, default)
+    def register(self, registration):
+        """See zope.app.component.interfaces.registration.IRegistry"""
+        registry = self.__getRegistry()
+        registry.register(registration)
 
-    def createRegistrationsFor(self, cfg):
-        """See IRegistry"""
-        return self.createRegistrations(cfg.name)
+    def unregister(self, registration):
+        """See zope.app.component.interfaces.registration.IRegistry"""
+        registry = self.__getRegistry()
+        registry.unregister(registration)        
 
-    def createRegistrations(self, name):
-        try:
-            registry = self._bindings[name]
-        except KeyError:
-            registry = RegistrationStack(self)
-            self._bindings[name] = registry
-            self._p_changed = 1
-        return registry
+    def registered(self, registration):
+        """See zope.app.component.interfaces.registration.IRegistry"""
+        return self.adapters.registered(registration) or \
+               self.utilities.registered(registration)
 
-    def listRegistrationNames(self):
-        return filter(self._bindings.get,
-                      self._bindings.keys())
+    def registrations(self):
+        """See zope.component.interfaces.IRegistry"""
+        for reg in self.adapters.registrations():
+            yield reg
+        for reg in self.utilities.registrations():
+            yield reg
 
-    def queryActiveComponent(self, name, default=None):
-        registry = self.queryRegistrations(name)
-        if registry:
-            registration = registry.active()
-            if registration is not None:
-                return registration.component
-        return default
-
-
-    def queryComponent(self, type=None, filter=None, all=0):
+    def queryComponent(self, type=None, filter=None, all=True):
+        """See zope.app.component.interfaces.IComponentManager"""
         local = []
         path = zapi.getPath(self)
         for pkg_name in self:
@@ -161,7 +166,7 @@
                     continue
                 if filter is not None and not filter(component):
                     continue
-                local.append({'path': "%s/%s/%s" % (path, pkg_name, name),
+                local.append({'path': "%s/%s/%s" %(path, pkg_name, name),
                               'component': component,
                               })
 
@@ -175,8 +180,9 @@
         return local
 
     def findModule(self, name):
+        """See zodbcode.interfaces.IPersistentModuleImportRegistry"""
         # override to pass call up to next service manager
-        mod = super(ServiceManager, self).findModule(name)
+        mod = super(SiteManager, self).findModule(name)
         if mod is not None:
             return mod
 
@@ -191,19 +197,7 @@
         return findModule(name)
 
 
-    def __import(self, module_name):
-        mod = self.findModule(module_name)
-        if mod is None:
-            mod = sys.modules.get(module_name)
-            if mod is None:
-                raise ImportError(module_name)
-
-        return mod
-
-
-    def findModule(self, name):
-        # Used by the persistent modules import hook
-
+    def __import(self, name):
         # Look for a .py file first:
         manager = self.get(name+'.py')
         if manager is not None:
@@ -218,17 +212,6 @@
             if IModuleManager.providedBy(manager):
                 return manager.getModule()
 
-
-        # See if out container is a RegisterableContainer:
-        c = self.__parent__
-        if interfaces.IRegisterableContainer.providedBy(c):
-            return c.findModule(name)
-
-        # Use sys.modules in lieu of module service:
-        module = sys.modules.get(name)
-        if module is not None:
-            return module
-
         raise ImportError(name)
 
 
@@ -238,60 +221,58 @@
         return getattr(mod, name[l+1:])
 
 
-class AdapterRegistration(
-    zope.app.registration.registration.SimpleRegistration):
+class AdapterRegistration(registration.ComponentRegistration):
+    """Adapter component registration for persistent components
 
-    with = () # Don't support multi-adapters yet
+    This registration configures persistent components in packages to
+    be adapters.
+    """
+    zope.interface.implements(interfaces.IAdapterRegistration)
 
-    # TODO: These should be positional arguments, except that required
-    #       isn't passed in if it is omitted. To fix this, we need a
-    #       required=False,explicitly_unrequired=True in the schema field
-    #       so None will get passed in.
-    def __init__(self, provided, factoryName,
-                 name='', required=None, permission=None):
-        self.required = required
+    def __init__(self, required, provided, factoryName,
+                 name='', permission=None):
+        if not isinstance(required, (tuple, list)):
+            self.required = required
+            self.with = ()
+        else:
+            self.required = required[0]
+            self.with = tuple(required[1:])
         self.provided = provided
         self.name = name
         self.factoryName = factoryName
         self.permission = permission
 
-    def factory(self):
+    def component(self):
         folder = self.__parent__.__parent__
         factory = folder.resolve(self.factoryName)
         return factory
-    factory = property(factory)
+    component = property(component)
 
     def getRegistry(self):
-        sm = self.getSiteManager()
-        return sm.adapters
+        return zapi.getSiteManager(self)
 
 
-class UtilityRegistration(ComponentRegistration):
+class UtilityRegistration(registration.ComponentRegistration):
     """Utility component registration for persistent components
 
     This registration configures persistent components in packages to
     be utilities.
     """
-    zope.interface.implements(IUtilityRegistration)
+    zope.interface.implements(interfaces.IUtilityRegistration)
 
     ############################################################
     # To make adapter code happy. Are we going too far?
-    #
     required = zope.interface.adapter.Null
     with = ()
-    provided = property(lambda self: self.interface)
-    factory = property(lambda self: self.component)
-    #
     ############################################################
 
-    def __init__(self, name, interface, component, permission=None):
+    def __init__(self, name, provided, component, permission=None):
         super(UtilityRegistration, self).__init__(component, permission)
         self.name = name
-        self.interface = interface
+        self.provided = provided
 
     def getRegistry(self):
-        sm = self.getSiteManager()
-        return sm.utilities
+        return self.getSiteManager()
 
 
 def threadSiteSubscriber(event):
@@ -299,7 +280,7 @@
 
     Sets the 'site' thread global if the object traversed is a site.
     """
-    if ISite.providedBy(event.object):
+    if interfaces.ISite.providedBy(event.object):
         setSite(event.object)
 
 

Added: Zope3/branches/srichter-blow-services/src/zope/app/component/tests/test_adapter.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/component/tests/test_adapter.py	2005-01-05 16:23:49 UTC (rev 28730)
+++ Zope3/branches/srichter-blow-services/src/zope/app/component/tests/test_adapter.py	2005-01-05 16:27:53 UTC (rev 28731)
@@ -0,0 +1,713 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Registration Tests
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import unittest
+from transaction import get_transaction
+from ZODB.tests.util import DB
+
+import zope.component.testing as placelesssetup
+import zope.interface
+from zope.interface.adapter import AdapterRegistry
+from zope.testing import doctest
+
+from zope.app import zapi
+from zope.app.component import interfaces
+from zope.app.component.adapter import LocalAdapterRegistry, AdapterRegistration
+from zope.app.testing import setup
+
+class IF0(zope.interface.Interface):
+    pass
+
+class IF1(IF0):
+    pass
+
+class IF2(IF1):
+    pass
+
+class IB0(zope.interface.Interface):
+    pass
+
+class IB1(IB0):
+    pass
+
+class IR0(zope.interface.Interface):
+    pass
+
+class IR1(IR0):
+    pass
+
+class R1(object):
+    zope.interface.implements(IR1)
+
+class F0(object):
+    zope.interface.implements(IF0)
+
+class F2(object):
+    zope.interface.implements(IF2)
+
+# Create a picklable global registry. The pickleability of other
+# global adapter registries is beyond the scope of these tests:
+class GlobalAdapterRegistry(AdapterRegistry):
+    def __reduce__(self):
+        return 'globalAdapterRegistry'
+
+globalAdapterRegistry = GlobalAdapterRegistry()
+
+def test_local_adapter():
+    """Local Adapter Tests
+
+   Local surrogates and adapter registries share declarations with
+   those "above" them.
+
+   Suppose we have a global AdapterRegistry:
+
+   >>> G = AdapterRegistry()
+
+   we also have a local adapter registry, with G as it's base:
+
+   >>> L1 = LocalAdapterRegistry(G)
+
+   and so on:
+
+   >>> L2 = LocalAdapterRegistry(G, L1)
+
+   Now, if we declare an adapter globally:
+
+   >>> G.register([IF1], IB1, '', 'A11G')
+
+   we can query it locally:
+
+   >>> L1.lookup([IF2], IB1)
+   'A11G'
+   
+   >>> L2.lookup([IF2], IB1)
+   'A11G'
+
+   We can add local definitions:
+
+   >>> ra011 = AdapterRegistration(required=IF0, provided=IB1, factory='A011',
+   ...                             registry=L1)
+   >>> ra011.status = interfaces.registration.ActiveStatus
+
+   and use it:
+
+   >>> L1.lookup([IF0], IB1)
+   'A011'
+   
+   >>> L2.lookup([IF0], IB1)
+   'A011'
+   
+   but not outside L1:
+
+   >>> G.lookup([IF0], IB1)
+
+   Note that it doesn't override the non-local adapter:
+
+   >>> L1.lookup([IF2], IB1)
+   'A11G'
+   
+   >>> L2.lookup([IF2], IB1)
+   'A11G'
+   
+   because it was more specific.
+
+   Let's override the adapter in L2:
+
+   >>> ra112 = AdapterRegistration(required=IF1, provided=IB1, factory='A112',
+   ...                             registry=L2)
+   >>> ra112.status = interfaces.registration.ActiveStatus
+
+   Now, in L2, we get the new adapter, because it's as specific and more
+   local than the one from G:
+
+   >>> L2.lookup([IF2], IB1)
+   'A112'
+   
+   But we still get the old one in L1
+
+   >>> L1.lookup([IF2], IB1)
+   'A11G'
+   
+   Note that we can ask for less specific interfaces and still get the adapter:
+
+   >>> L2.lookup([IF2], IB0)
+   'A112'
+
+   >>> L1.lookup([IF2], IB0)
+   'A11G'
+
+   We get the more specific adapter even if there is a less-specific
+   adapter to B0:
+
+   >>> G.register([IF1], IB1, '', 'A10G')
+
+   >>> L2.lookup([IF2], IB0)
+   'A112'
+
+   But if we have an equally specific and equally local adapter to B0, it
+   will win:
+
+   >>> ra102 = AdapterRegistration(required=IF1, provided=IB0, factory='A102',
+   ...                             registry=L2)
+   >>> ra102.status = interfaces.registration.ActiveStatus
+
+   >>> L2.lookup([IF2], IB0)
+   'A102'
+
+   We can deactivate registrations, which has the effect of deleting adapters:
+
+
+   >>> ra112.status = interfaces.registration.InactiveStatus
+
+   >>> L2.lookup([IF2], IB0)
+   'A102'
+
+   >>> L2.lookup([IF2], IB1)
+   'A10G'
+
+   >>> ra102.status = interfaces.registration.InactiveStatus
+
+   >>> L2.lookup([IF2], IB0)
+   'A10G'
+
+   We can ask for all of the registrations :
+
+   >>> L1.registrations() #doctest: +NORMALIZE_WHITESPACE
+   (<AdapterRegistration:
+        required=<InterfaceClass zope.app.component.tests.test_adapter.IF0>,
+        with=(),
+        provided=<InterfaceClass zope.app.component.tests.test_adapter.IB1>,
+        name='',
+        component='A011',
+        permission=None>,)
+
+   This shows only the local registrations in L1.
+   """
+
+def test_named_adapters():
+    """
+    Suppose we have a global AdapterRegistry:
+
+    >>> G = AdapterRegistry()
+
+    we also have a local adapter registry, with G as it's base:
+
+    >>> L1 = LocalAdapterRegistry(G)
+
+    and so on:
+
+    >>> L2 = LocalAdapterRegistry(G, L1)
+
+    Now, if we declare an adapter globally:
+
+    >>> G.register([IF1], IB1, 'bob', 'A11G')
+
+    we can query it locally:
+
+    >>> L1.lookup([IF2], IB1)
+    >>> L1.lookup([IF2], IB1, 'bob')
+    'A11G'
+
+    >>> L2.lookup([IF2], IB1)
+    >>> L2.lookup([IF2], IB1, 'bob')
+    'A11G'
+
+    We can add local definitions:
+
+    >>> ra011 = AdapterRegistration(required = IF0, provided=IB1,
+    ...                             factory='A011', name='bob',
+    ...                             registry=L1)
+    >>> ra011.status = interfaces.registration.ActiveStatus
+
+    and use it:
+
+    >>> L1.lookup([IF0], IB1)
+    >>> L1.lookup([IF0], IB1, 'bob')
+    'A011'
+
+    >>> L2.lookup([IF0], IB1)
+    >>> L2.lookup([IF0], IB1, 'bob')
+    'A011'
+
+    but not outside L1:
+
+    >>> G.lookup([IF0], IB1, 'bob')
+
+    Note that it doesn't override the non-local adapter:
+
+    >>> L1.lookup([IF2], IB1)
+    >>> L1.lookup([IF2], IB1, 'bob')
+    'A11G'
+
+    >>> L2.lookup([IF2], IB1)
+    >>> L2.lookup([IF2], IB1, 'bob')
+    'A11G'
+
+    because it was more specific.
+
+    Let's override the adapter in L2:
+
+    >>> ra112 = AdapterRegistration(required=IF1, provided=IB1,
+    ...                             factory='A112', name='bob',
+    ...                             registry=L2)
+    >>> ra112.status = interfaces.registration.ActiveStatus
+
+    Now, in L2, we get the new adapter, because it's as specific and more
+    local than the one from G:
+
+    >>> L2.lookup([IF2], IB1)
+    >>> L2.lookup([IF2], IB1, 'bob')
+    'A112'
+
+    But we still get thye old one in L1
+
+    >>> L1.lookup([IF2], IB1)
+    >>> L1.lookup([IF2], IB1, 'bob')
+    'A11G'
+
+    Note that we can ask for less specific interfaces and still get the adapter:
+
+    >>> L2.lookup([IF2], IB0)
+    >>> L2.lookup([IF2], IB0, 'bob')
+    'A112'
+
+    >>> L1.lookup([IF2], IB0)
+    >>> L1.lookup([IF2], IB0, 'bob')
+    'A11G'
+
+    We get the more specific adapter even if there is a less-specific
+    adapter to B0:
+
+    >>> G.register([IF1], IB1, 'bob', 'A10G')
+
+    >>> L2.lookup([IF2], IB0)
+    >>> L2.lookup([IF2], IB0, 'bob')
+    'A112'
+
+    But if we have an equally specific and equally local adapter to B0, it
+    will win:
+
+    >>> ra102 = AdapterRegistration(required = IF1, provided=IB0,
+    ...                             factory='A102', name='bob',
+    ...                             registry=L2)
+    >>> ra102.status = interfaces.registration.ActiveStatus
+    
+    >>> L2.lookup([IF2], IB0)
+    >>> L2.lookup([IF2], IB0, 'bob')
+    'A102'
+
+    We can deactivate registrations, which has the effect of deleting adapters:
+
+
+    >>> ra112.status = interfaces.registration.InactiveStatus
+
+    >>> L2.lookup([IF2], IB0)
+    >>> L2.lookup([IF2], IB0, 'bob')
+    'A102'
+
+    >>> L2.lookup([IF2], IB1)
+    >>> L2.lookup([IF2], IB1, 'bob')
+    'A10G'
+
+    >>> ra102.status = interfaces.registration.InactiveStatus
+
+    >>> L2.lookup([IF2], IB0)
+    >>> L2.lookup([IF2], IB0, 'bob')
+    'A10G'
+    """
+
+def test_multi_adapters():
+    """
+    Suppose we have a global AdapterRegistry:
+
+    >>> G = AdapterRegistry()
+
+    we also have a local adapter registry, with G as it's base:
+
+    >>> L1 = LocalAdapterRegistry(G)
+
+    and so on:
+
+    >>> L2 = LocalAdapterRegistry(G, L1)
+
+    Now, if we declare an adapter globally:
+
+    >>> G.register([IF1, IR0], IB1, 'bob', 'A11G')
+
+    we can query it locally:
+
+    >>> L1.lookup([IF2, IR1], IB1, 'bob')
+    'A11G'
+
+    >>> L2.lookup([IF2, IR1], IB1, 'bob')
+    'A11G'
+
+    We can add local definitions:
+
+    >>> ra011 = AdapterRegistration(required=(IF0, IR0), provided=IB1,
+    ...                             factory='A011', name='bob',
+    ...                             registry=L1)
+    >>> ra011.status = interfaces.registration.ActiveStatus
+    
+    and use it:
+
+    >>> L1.lookup([IF0, IR1], IB1, 'bob')
+    'A011'
+
+    >>> L2.lookup([IF0, IR1], IB1, 'bob')
+    'A011'
+
+    but not outside L1:
+
+    >>> G.lookup((IF0, IR1), IB1, 'bob')
+
+    Note that it doesn't override the non-local adapter:
+
+    >>> L1.lookup([IF2, IR1], IB1, 'bob')
+    'A11G'
+
+    >>> L2.lookup((IF2, IR1), IB1, 'bob')
+    'A11G'
+
+    because it was more specific.
+
+    Let's override the adapter in L2:
+
+    >>> ra112 = AdapterRegistration(required=(IF1, IR0), provided=IB1,
+    ...                             factory='A112', name='bob',
+    ...                             registry=L2)
+    >>> ra112.status = interfaces.registration.ActiveStatus
+
+    Now, in L2, we get the new adapter, because it's as specific and more
+    local than the one from G:
+
+    >>> L2.lookup((IF2, IR1), IB1, 'bob')
+    'A112'
+
+    But we still get the old one in L1
+
+    >>> L1.lookup((IF2, IR1), IB1, 'bob')
+    'A11G'
+
+    Note that we can ask for less specific interfaces and still get
+    the adapter:
+
+    >>> L2.lookup((IF2, IR1), IB0, 'bob')
+    'A112'
+
+    >>> L1.lookup((IF2, IR1), IB0, 'bob')
+    'A11G'
+
+    We get the more specific adapter even if there is a less-specific
+    adapter to B0:
+
+    >>> G.register([IF1, IR0], IB1, 'bob', 'A10G')
+
+    >>> L2.lookup((IF2, IR1), IB0, 'bob')
+    'A112'
+
+    But if we have an equally specific and equally local adapter to B0, it
+    will win:
+
+    >>> ra102 = AdapterRegistration(required=(IF1, IR0), provided=IB0,
+    ...                             factory='A102', name='bob',
+    ...                             registry=L2)
+    >>> ra102.status = interfaces.registration.ActiveStatus
+    
+    >>> L2.lookup((IF2, IR1), IB0, 'bob')
+    'A102'
+
+    We can deactivate registrations, which has the effect of deleting adapters:
+
+    >>> ra112.status = interfaces.registration.InactiveStatus
+
+    >>> L2.lookup((IF2, IR1), IB0, 'bob')
+    'A102'
+
+    >>> L2.lookup((IF2, IR1), IB1, 'bob')
+    'A10G'
+
+    >>> ra102.status = interfaces.registration.InactiveStatus
+
+    >>> L2.lookup([IF2], IB0)
+    >>> L2.lookup((IF2, IR1), IB0, 'bob')
+    'A10G'
+    """
+
+def test_persistence():
+    """
+    >>> db = DB()
+    >>> conn1 = db.open()
+
+    >>> G = globalAdapterRegistry
+    >>> L1 = LocalAdapterRegistry(G)
+    >>> L2 = LocalAdapterRegistry(G, L1)
+
+    >>> conn1.root()['L1'] = L1
+    >>> conn1.root()['L2'] = L2
+    
+    >>> G.register([IF1], IB1, 'bob', 'A11G')
+    >>> L1.lookup([IF2], IB1)
+    >>> L1.lookup([IF2], IB1, 'bob')
+    'A11G'
+
+    >>> L2.lookup([IF2], IB1)
+    >>> L2.lookup([IF2], IB1, 'bob')
+    'A11G'
+
+    We can add local definitions:
+
+    >>> ra011 = AdapterRegistration(required=IF0, provided=IB1,
+    ...                             factory='A011', name='bob',
+    ...                             registry=L1)
+    >>> ra011.status = interfaces.registration.ActiveStatus
+
+    and use it:
+
+    >>> L1.lookup([IF0], IB1)
+    >>> L1.lookup([IF0], IB1, 'bob')
+    'A011'
+
+    >>> L2.lookup([IF0], IB1)
+    >>> L2.lookup([IF0], IB1, 'bob')
+    'A011'
+
+    but not outside L1:
+
+    >>> G.lookup([IF0], IB1)
+
+    Note that it doesn't override the non-local adapter:
+
+    >>> L1.lookup([IF2], IB1)
+    >>> L1.lookup([IF2], IB1, 'bob')
+    'A11G'
+
+    >>> L2.lookup([IF2], IB1)
+    >>> L2.lookup([IF2], IB1, 'bob')
+    'A11G'
+
+    because it was more specific.
+
+    Let's override the adapter in L2:
+
+    >>> ra112 = AdapterRegistration(required=IF1, provided=IB1,
+    ...                             factory='A112', name='bob',
+    ...                             registry=L2)
+    >>> ra112.status = interfaces.registration.ActiveStatus
+
+    Now, in L2, we get the new adapter, because it's as specific and more
+    local than the one from G:
+
+    >>> L2.lookup([IF2], IB1)
+    >>> L2.lookup([IF2], IB1, 'bob')
+    'A112'
+
+    But we still get the old one in L1
+
+    >>> L1.lookup([IF2], IB1)
+    >>> L1.lookup([IF2], IB1, 'bob')
+    'A11G'
+
+    Note that we can ask for less specific interfaces and still get
+    the adapter:
+
+    >>> L2.lookup([IF2], IB0)
+    >>> L2.lookup([IF2], IB0, 'bob')
+    'A112'
+
+    >>> L1.lookup([IF2], IB0)
+    >>> L1.lookup([IF2], IB0, 'bob')
+    'A11G'
+
+    We get the more specific adapter even if there is a less-specific
+    adapter to B0:
+
+    >>> G.register([IF0], IB0, 'bob', 'A00G')
+
+    >>> L2.lookup([IF2], IB0)
+    >>> L2.lookup([IF2], IB0, 'bob')
+    'A112'
+
+    But if we have an equally specific and equally local adapter to B0, it
+    will win:
+
+    >>> ra102 = AdapterRegistration(required=IF1, provided=IB0,
+    ...                             factory='A102', name='bob',
+    ...                             registry=L2)
+    >>> ra102.status = interfaces.registration.ActiveStatus
+
+    >>> L2.lookup([IF2], IB0)
+    >>> L2.lookup([IF2], IB0, 'bob')
+    'A102'
+
+    >>> L1.lookup([IF2], IB0, 'bob')
+    'A11G'
+    >>> L1.lookup([IF2], IB1, 'bob')
+    'A11G'
+    >>> L2.lookup([IF2], IB0, 'bob')
+    'A102'
+    >>> L2.lookup([IF2], IB1, 'bob')
+    'A112'
+
+    >>> get_transaction().commit()
+
+    Now, let's open another transaction:
+
+    >>> conn2 = db.open()
+
+    >>> L1 = conn2.root()['L1']
+    >>> L2 = conn2.root()['L2']
+    >>> ra112 = L2._registrations[0]
+    >>> ra102 = L2._registrations[1]
+
+    We should get the same outputs:
+
+    >>> L1.lookup([IF2], IB0, 'bob')
+    'A11G'
+    >>> L1.lookup([IF2], IB1, 'bob')
+    'A11G'
+    >>> L2.lookup([IF2], IB0, 'bob')
+    'A102'
+    >>> L2.lookup([IF2], IB1, 'bob')
+    'A112'
+    
+    We can deactivate registrations, which has the effect of deleting
+    adapters:
+
+    >>> ra112.status = interfaces.registration.InactiveStatus
+    >>> ra102.status = interfaces.registration.InactiveStatus
+
+    >>> L1.lookup([IF2], IB0, 'bob')
+    'A11G'
+    >>> L1.lookup([IF2], IB1, 'bob')
+    'A11G'
+    >>> L2.lookup([IF2], IB0, 'bob')
+    'A11G'
+    >>> L2.lookup([IF2], IB1, 'bob')
+    'A11G'
+
+    >>> get_transaction().commit()
+
+    If we look back at the first connection, we should get the same data:
+
+    >>> conn1.sync()
+    >>> L1 = conn1.root()['L1']
+    >>> L2 = conn1.root()['L2']
+
+    We should see the result of the deactivations:
+    
+    >>> L1.lookup([IF2], IB0, 'bob')
+    'A11G'
+    >>> L1.lookup([IF2], IB1, 'bob')
+    'A11G'
+    >>> L2.lookup([IF2], IB0, 'bob')
+    'A11G'
+    >>> L2.lookup([IF2], IB1, 'bob')
+    'A11G'
+
+    Cleanup:
+    >>> G.__init__()
+    >>> db.close()
+    """
+
+
+def test_local_default():
+    """
+    >>> G = AdapterRegistry()
+    >>> L1 = LocalAdapterRegistry(G)
+
+    >>> r = AdapterRegistration(required=None, provided=IB1,
+    ...                         factory='Adapter', registry=L1)
+    >>> r.status = interfaces.registration.ActiveStatus
+    >>> L1.lookup([IF2], IB1)
+    'Adapter'
+    """
+
+
+def test_changing_next():
+    """
+    >>> G = AdapterRegistry()
+    >>> L1 = LocalAdapterRegistry(G)
+    >>> L2 = LocalAdapterRegistry(G, L1)
+    >>> f2 = F2()
+
+    >>> L2.lookup([IF2], IB1)
+
+    >>> G.register([IF1], IB1, '', 'A11G')
+    >>> L2.lookup([IF2], IB1)
+    'A11G'
+
+
+    >>> ra111 = AdapterRegistration(required=IF1, provided=IB1,
+    ...                             factory='A111', registry=L1)
+    >>> ra111.status = interfaces.registration.ActiveStatus
+    >>> L2.lookup([IF2], IB1)
+    'A111'
+
+    >>> L1.next
+    >>> L2.next == L1
+    True
+    >>> L1.subs == (L2,)
+    True
+    >>> L3 = LocalAdapterRegistry(G, L1)
+    >>> L2.setNext(L3)
+    >>> L2.next == L3
+    True
+    >>> L3.next == L1
+    True
+    >>> L1.subs == (L3,)
+    True
+    >>> L3.subs == (L2,)
+    True
+
+    >>> ra113 = AdapterRegistration(required=IF1, provided=IB1,
+    ...                             factory='A113', registry=L3)
+    >>> ra113.status = interfaces.registration.ActiveStatus
+
+    >>> L2.lookup([IF2], IB1)
+    'A113'
+    >>> L2.setNext(L1)
+    >>> L2.next == L1
+    True
+    >>> L3.next == L1
+    True
+    >>> L1.subs == (L3, L2)
+    True
+    >>> L3.subs == ()
+    True
+    >>> L2.lookup([IF2], IB1)
+    'A111'
+
+    """
+
+def setUp(test):
+    placelesssetup.setUp(test)
+    setup.setUpAnnotations()
+    setup.setUpDependable()
+    setup.setUpTraversal()
+
+def test_suite():
+    return unittest.TestSuite((
+        doctest.DocTestSuite(),
+        doctest.DocFileSuite('../adapterregistry.txt',
+                             setUp=setUp, tearDown=placelesssetup.tearDown),
+        ))
+
+if __name__ == "__main__":
+    unittest.main(defaultTest='test_suite')
+    



More information about the Zope3-Checkins mailing list