[Zope3-checkins]
SVN: Zope3/branches/srichter-blow-services/src/zope/app/component/
As promised on IRC yesterday,
here are the registration framework tests.
Stephan Richter
srichter at cosmos.phy.tufts.edu
Thu Dec 23 17:44:57 EST 2004
Log message for revision 28700:
As promised on IRC yesterday, here are the registration framework tests.
Now its time to get the local component architecture running again.
Changed:
U Zope3/branches/srichter-blow-services/src/zope/app/component/hooks.py
U Zope3/branches/srichter-blow-services/src/zope/app/component/interfaces/registration.py
U Zope3/branches/srichter-blow-services/src/zope/app/component/registration.py
U Zope3/branches/srichter-blow-services/src/zope/app/component/registration.txt
U Zope3/branches/srichter-blow-services/src/zope/app/component/statusproperty.txt
U Zope3/branches/srichter-blow-services/src/zope/app/component/tests/test_registration.py
-=-
Modified: Zope3/branches/srichter-blow-services/src/zope/app/component/hooks.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/component/hooks.py 2004-12-23 22:43:11 UTC (rev 28699)
+++ Zope3/branches/srichter-blow-services/src/zope/app/component/hooks.py 2004-12-23 22:44:57 UTC (rev 28700)
@@ -41,7 +41,7 @@
class SiteInfo(zope.thread.local):
site = None
- #services = serviceManager
+ sm = zope.component.getGlobalSiteManager()
def adapter_hook(self):
services = self.services
@@ -56,7 +56,7 @@
def setSite(site=None):
if site is None:
- services = serviceManager
+ sm = zope.component.getGlobalSiteManager()
else:
# We remove the security proxy because there's no way for
@@ -67,10 +67,10 @@
# they can't be proxied. Well, except maybe for performance.
site = removeSecurityProxy(site)
- services = site.getSiteManager()
+ sm = site.getSiteManager()
siteinfo.site = site
- siteinfo.services = services
+ siteinfo.services = sm
try:
del siteinfo.adapter_hook
except AttributeError:
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 2004-12-23 22:43:11 UTC (rev 28699)
+++ Zope3/branches/srichter-blow-services/src/zope/app/component/interfaces/registration.py 2004-12-23 22:44:57 UTC (rev 28700)
@@ -79,30 +79,33 @@
class IComponentRegistration(IRegistration):
- """Registration object that uses a component path and a permission."""
+ """Registration object that uses a component.
- permission = Choice(
- title=_("The permission needed to use the component"),
- vocabulary="Permissions",
- required=False,
- )
-
+ An interface can optionally be specified that describes the interface the
+ component provides for the registry.
+
+ The interface will be used to produce a proxy for the component, if
+ the permission is also specified.
+ """
component = Component(
title=_("Registration Component"),
description=_("The component the registration is for."),
required=True)
- def getInterface(self):
- """Return the interface the component provides through this
- registration.
+ interface = Field(
+ title=_("Component Interface"),
+ description=_("The interface the component provides through this "
+ "registration."),
+ required=False,
+ default=None)
- If no interface was specified, return `None`.
+ permission = Choice(
+ title=_("The permission needed to use the component"),
+ vocabulary="Permissions",
+ required=False
+ )
- The interface will be used to produce a proxy for the component, if
- the `permission` is specified.
- """
-
class IRegistry(zope.component.interfaces.IRegistry):
"""A component that can be configured using a registration manager."""
Modified: Zope3/branches/srichter-blow-services/src/zope/app/component/registration.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/component/registration.py 2004-12-23 22:43:11 UTC (rev 28699)
+++ Zope3/branches/srichter-blow-services/src/zope/app/component/registration.py 2004-12-23 22:44:57 UTC (rev 28700)
@@ -19,8 +19,8 @@
import zope.event
from zope.interface import implements
-from zope.security.checker import CheckerPublic
-from zope.security.proxy import removeSecurityProxy
+from zope.security.checker import InterfaceChecker, CheckerPublic
+from zope.security.proxy import Proxy, removeSecurityProxy
from zope.app import zapi
from zope.app.container.btree import BTreeContainer
@@ -66,9 +66,12 @@
if value == interfaces.ActiveStatus:
if not registry.registered(registration):
registry.register(registration)
+ zope.event.notify(RegistrationActivatedEvent(registration))
+
elif value == interfaces.InactiveStatus:
if registry.registered(registration):
registry.unregister(registration)
+ zope.event.notify(RegistrationDeactivatedEvent(registration))
else:
raise ValueError, value
@@ -101,8 +104,8 @@
self.permission = permission
def _getComponent(self):
- if self.permission and self.getInterface():
- checker = InterfaceChecker(self.getInterface(), self.permission)
+ if self.permission and self.interface:
+ checker = InterfaceChecker(self.interface, self.permission)
return Proxy(self._component, checker)
return self._component
@@ -111,10 +114,11 @@
# get back a proxied component anyways.
self._component = removeSecurityProxy(component)
+ # See zope.app.component.interfaces.registration.IComponentRegistration
component = property(_getComponent, _setComponent)
- def getInterface(self):
- return None
+ # See zope.app.component.interfaces.registration.IComponentRegistration
+ interface = None
def SimpleRegistrationRemoveSubscriber(registration, event):
@@ -137,19 +141,19 @@
% objectpath)
-def ComponentRegistrationRemoveSubscriber(component_registration, event):
+def ComponentRegistrationRemoveSubscriber(componentRegistration, event):
"""Receive notification of remove event."""
- component = component_registration.component
+ component = componentRegistration.component
dependents = IDependable(component)
- objectpath = zapi.getPath(component_registration)
+ objectpath = zapi.getPath(componentRegistration)
dependents.removeDependent(objectpath)
-def ComponentRegistrationAddSubscriber(component_registration, event):
+def ComponentRegistrationAddSubscriber(componentRegistration, event):
"""Receive notification of add event."""
- component = component_registration.component
+ component = componentRegistration.component
dependents = IDependable(component)
- objectpath = zapi.getPath(component_registration)
+ objectpath = zapi.getPath(componentRegistration)
dependents.addDependent(objectpath)
@@ -176,8 +180,8 @@
def registrations(self):
rm = zapi.getParent(self.registerable).registrationManager
- return [reg for reg in rm
- if (IComponentRegistration.providedBy(reg) and
+ return [reg for reg in rm.values()
+ if (interfaces.IComponentRegistration.providedBy(reg) and
reg.component is self.registerable)]
Modified: Zope3/branches/srichter-blow-services/src/zope/app/component/registration.txt
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/component/registration.txt 2004-12-23 22:43:11 UTC (rev 28699)
+++ Zope3/branches/srichter-blow-services/src/zope/app/component/registration.txt 2004-12-23 22:44:57 UTC (rev 28700)
@@ -248,21 +248,180 @@
The Component Registration
--------------------------
-provided interface
-permission
-Registered
+Until now we have only discussed the most primitive usage of the
+`ComponentRegistration`. Usually, a registry is not just interested in a
+component, but a set of methods which are specified by a particular
+interface. Thus the component registration supports the `interface`
+attribute. By default it is `None`:
+ >>> regFoo.interface is None
+ True
-Registration Events
--------------------
+We can now write another `IComponentRegistration` implementation that knows
+about the interface; in fact, it will pick the most specific one of the
+component:
-RegistrationActivatedEvent
-RegistrationDeactivatedEvent
+ >>> from zope.interface import providedBy
+ >>> class SomethingRegistration(Registration):
+ ...
+ ... def interface(self):
+ ... return list(providedBy(self._component))[0]
+ ... interface = property(interface)
+
+Next we create an interface and its implementation:
+ >>> class ISomething(zope.interface.Interface):
+ ... pass
+ >>> class Something(Component):
+ ... zope.interface.implements(ISomething)
+
+Creating a "something registration", we can see that the interface attribute
+is now available:
+
+ >>> something = Something('Something')
+ >>> reg = SomethingRegistration(something)
+ >>> reg.interface
+ <InterfaceClass __builtin__.ISomething>
+
+But hold on, we are not done yet! The component registration also supports a
+`permission` attribute. When set and an interface is available, the component
+will always be proxied using an interface checker for the specified
+permission. By default the permission is `None`:
+
+ >>> reg.permission is None
+ True
+
+Now we set a permission for the registration and the component should be
+proxies when returned:
+
+ >>> from zope.security.checker import CheckerPublic
+ >>> reg.permission = CheckerPublic
+ >>> reg.component is something
+ False
+ >>> type(reg.component)
+ <type 'zope.security._proxy._Proxy'>
+
+You can also, specify a permission in the constructor:
+
+ >>> regNone = SomethingRegistration(None, 'zope.Public')
+ >>> regNone.permission is CheckerPublic
+ True
+
+If the interface is not available, the permission is ignored and the bare
+component is returned:
+
+ >>> regSomething2 = Registration(something, 'zope.Public')
+ >>> regSomething2.permission is CheckerPublic
+ True
+ >>> regSomething2.component is something
+ True
+
+
+The `Registered` Adapter
+------------------------
+
+Registerable components are able to get a list of all their
+registrations. However, the adapter only works for components and
+registrations that are stored in the registerable container and registration
+manager, respectively.
+
+ >>> from zope.app.component.registration import Registered
+ >>> registered = Registered(foo)
+ >>> registered.registrations() #doctest: +NORMALIZE_WHITESPACE
+ [<Registration for '<Component: 'Foo'>'>,
+ <Registration for '<Component: 'Foo'>'>]
+
+If the registerable component is not stored in a registrable container, a
+type error is raised, since no parent can be found:
+
+ >>> registered = Registered(something)
+ >>> registered.registrations() #doctest: +NORMALIZE_WHITESPACE
+ Traceback (most recent call last):
+ ...
+ TypeError: ('Not enough context information to get parent',
+ <Component: 'Something'>)
+
+
Registrations and Dependencies
------------------------------
-ComponentRegistrationRemoveSubscriber
-ComponentRegistrationAddSubscriber
-RegisterableMoveSubscriber
\ No newline at end of file
+Registerable objects and registrations have a very close dependence. For
+example, it makes no sense to delete a registerable component and leave its
+registrations around. Instead, registrations always are required to be removed
+before the component. Thus, it is also not allowed to simply copy a
+registerable component to another registerable container:
+
+ >>> orig = RegisterableManager()
+ >>> new = RegisterableManager()
+ >>> comp = Component('comp')
+
+ >>> from zope.app.container.contained import ObjectMovedEvent
+ >>> event = ObjectMovedEvent(comp, orig, 'comp', new, 'comp')
+
+ >>> from zope.app.component.registration import RegisterableMoveSubscriber
+ >>> RegisterableMoveSubscriber(comp, event)
+ Traceback (most recent call last):
+ ...
+ DependencyError: Can't move a registered component from its container.
+
+Hoever, renaming the component is no problem:
+
+ >>> event = ObjectMovedEvent(comp, orig, 'comp', orig, 'comp-new')
+ >>> RegisterableMoveSubscriber(comp, event)
+
+Whenever a registration is created it is added as a dependence to the
+registerable component, which is removed once the registration is removed. Two
+subscribers handle the management of the dependemcies.
+
+Since the default `IDependable` adapter uses annotations to store the
+dependents, our component has to provide `IAttrbuteAnnotatable`:
+
+ >>> from zope.app.annotation.interfaces import IAttributeAnnotatable
+ >>> from zope.interface import directlyProvides
+ >>> directlyProvides(comp, IAttributeAnnotatable)
+
+Make sure that we do not initially have any dependencies:
+
+ >>> from zope.app.dependable.interfaces import IDependable
+ >>> dependents = IDependable(comp)
+ >>> dependents.getPaths()
+ ()
+
+Since the path of the registration is used to store the dependency, we need to
+make sure that we have a containment root; so make the registration itself the
+root:
+
+ >>> reg = Registration(comp)
+ >>> from zope.app.traversing.interfaces import IContainmentRoot
+ >>> directlyProvides(reg, IContainmentRoot)
+
+The component registration add subscriber adds a dependent.
+
+ >>> from zope.app.container.contained import ObjectAddedEvent
+ >>> from zope.app.component.registration import RegistrationManager
+ >>> event = ObjectAddedEvent(reg, RegistrationManager(), 'reg1')
+
+ >>> from zope.app.component.registration import \
+ ... ComponentRegistrationAddSubscriber
+ >>> ComponentRegistrationAddSubscriber(reg, event)
+
+ >>> dependents = IDependable(comp)
+ >>> dependents.getPaths()
+ (u'/',)
+
+We simply got a slash here, since the registration is a root object. Now we
+remove the dependency again by calling the component registration remove
+subscriber:
+
+ >>> from zope.app.container.contained import ObjectRemovedEvent
+ >>> event = ObjectRemovedEvent(reg, RegistrationManager(), 'reg1')
+
+ >>> from zope.app.component.registration import \
+ ... ComponentRegistrationRemoveSubscriber
+ >>> ComponentRegistrationRemoveSubscriber(reg, event)
+
+ >>> dependents = IDependable(comp)
+ >>> dependents.getPaths()
+ ()
+
Modified: Zope3/branches/srichter-blow-services/src/zope/app/component/statusproperty.txt
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/component/statusproperty.txt 2004-12-23 22:43:11 UTC (rev 28699)
+++ Zope3/branches/srichter-blow-services/src/zope/app/component/statusproperty.txt 2004-12-23 22:44:57 UTC (rev 28700)
@@ -81,3 +81,44 @@
True
>>> reg in registry.registrations
False
+
+
+Registration Events
+-------------------
+
+When a registration is activated or deactivated, an
+`RegistrationActivatedEvent` or `RegistrationDeactivatedEvent` is created,
+respectively. Listening to these events can be useful for cases where you want
+to change the component based on registration status.
+
+To catch the events, we have to register a subscriber with the event
+framework:
+
+ >>> events = []
+ >>> def subscriber(event):
+ ... global events
+ ... events.append(event)
+
+ >>> import zope.event
+ >>> zope.event.subscribers.append(subscriber)
+
+Now we switch our registration to active:
+
+ >>> reg.status = ActiveStatus
+ >>> event = events.pop()
+ >>> event.__class__
+ <class 'zope.app.component.registration.RegistrationActivatedEvent'>
+ >>> event.object is reg
+ True
+
+and deactivate it again:
+
+ >>> reg.status = InactiveStatus
+ >>> events.pop().__class__
+ <class 'zope.app.component.registration.RegistrationDeactivatedEvent'>
+ >>> event.object is reg
+ True
+
+Now make sure that we remove the subscriber again:
+
+ >>> del zope.event.subscribers[zope.event.subscribers.index(subscriber)]
Modified: Zope3/branches/srichter-blow-services/src/zope/app/component/tests/test_registration.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/component/tests/test_registration.py 2004-12-23 22:43:11 UTC (rev 28699)
+++ Zope3/branches/srichter-blow-services/src/zope/app/component/tests/test_registration.py 2004-12-23 22:44:57 UTC (rev 28700)
@@ -18,13 +18,21 @@
__docformat__ = "reStructuredText"
import unittest
+import zope.component.testing as placelesssetup
from zope.testing import doctest
+from zope.app.testing import setup
+def setUp(test):
+ placelesssetup.setUp(test)
+ setup.setUpAnnotations()
+ setup.setUpDependable()
+ setup.setUpTraversal()
def test_suite():
return unittest.TestSuite((
doctest.DocFileSuite('../statusproperty.txt'),
- doctest.DocFileSuite('../registration.txt'),
+ doctest.DocFileSuite('../registration.txt',
+ setUp=setUp, tearDown=placelesssetup.tearDown),
))
if __name__ == "__main__":
More information about the Zope3-Checkins
mailing list