[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