[Checkins] SVN: five.localsitemanager/branches/icemac-absolute-path-components/ * Added ability to register utilities with an absolute path. These

Michael Howitz mh at gocept.com
Tue Aug 26 05:03:50 EDT 2008


Log message for revision 90267:
  * Added ability to register utilities with an absolute path. These
    utilities are returned wrapped into their original context. This
    change is backward compatible to existing registries.
  
    But registering utilities having an acquisition context will behave
    different because these utilities will be returned in their original
    context. To restore the previous behavior, register utilities
    unwrapped (aq_base).
  

Changed:
  U   five.localsitemanager/branches/icemac-absolute-path-components/CHANGES.txt
  U   five.localsitemanager/branches/icemac-absolute-path-components/src/five/localsitemanager/localsitemanager.txt
  U   five.localsitemanager/branches/icemac-absolute-path-components/src/five/localsitemanager/registry.py

-=-
Modified: five.localsitemanager/branches/icemac-absolute-path-components/CHANGES.txt
===================================================================
--- five.localsitemanager/branches/icemac-absolute-path-components/CHANGES.txt	2008-08-26 08:57:37 UTC (rev 90266)
+++ five.localsitemanager/branches/icemac-absolute-path-components/CHANGES.txt	2008-08-26 09:03:49 UTC (rev 90267)
@@ -6,6 +6,16 @@
 
 * Added buildout for project, so testing can be done using ``bin/test``.
 
+* Added ability to register utilities with an absolute path. These
+  utilities are returned wrapped into their original context. This
+  change is backward compatible to existing registries.
+
+  But registering utilities having an acquisition context will behave
+  different because these utilities will be returned in their original
+  context. To restore the previous behavior, register utilities
+  unwrapped (aq_base).
+
+
 0.4 - 2008-07-23
 ----------------
 

Modified: five.localsitemanager/branches/icemac-absolute-path-components/src/five/localsitemanager/localsitemanager.txt
===================================================================
--- five.localsitemanager/branches/icemac-absolute-path-components/src/five/localsitemanager/localsitemanager.txt	2008-08-26 08:57:37 UTC (rev 90266)
+++ five.localsitemanager/branches/icemac-absolute-path-components/src/five/localsitemanager/localsitemanager.txt	2008-08-26 09:03:49 UTC (rev 90267)
@@ -108,7 +108,12 @@
 -----------
 
 Now to mix a little required Zope 2 confusion into everything, we must ensure
-that the aq chain is predictable. And based on consensus we decided that the
+that the aq chain is predictable. 
+
+Path relative to component registry
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+And based on consensus we decided that the
 acquired parent of a returned utility should be the ``ISite`` that owns the
 ``ISiteManager`` that returned the utility. We need to ensure all the ways of
 getting a utility have been covered. Of course this should only happen if the
@@ -203,6 +208,151 @@
     >>> Acquisition.aq_parent(comp) is site
     True
 
+Absolute Path
+~~~~~~~~~~~~~
+
+The approach to return utilites with a acquisition chain relative to
+the component registry has the problem that it can return the wrong
+physical path for the utility so computed URLs for the utility are
+also wrong.
+
+This can be fixed when the parent object of a utility is a local
+component registry. But this is not like in Zope3 and has the problem
+that utility registration is only visible below its registry.
+
+Alternative solution: register the utility with its acquisition
+context and it will be returned wrapped into its original
+context. (Only the physical path is stored not the context itself.)
+
+We set up a hierarchy of folders to show the behavior:
+
+    >>> a = self.app._setObject('a', Folder('a'), set_owner=False)
+    >>> b = self.app.a._setObject('b', Folder('b'), set_owner=False)
+    >>> util = self.app.a.b._setObject(
+    ...     'util', AQTestUtility('util'), set_owner=False)
+
+Now we can make `a` a local component registry an register `util`
+there. We expect the utility to implement getPhysicalPath and raise an
+exception otherwise:
+
+    >>> make_objectmanager_site(self.app.a)
+    >>> setActiveSite(self.app.a)
+    >>> sitemanager_a = self.app.a.getSiteManager()
+    >>> sitemanager_a.registerUtility(self.app.a.b.util,
+    ...                               name=u'with_aq_chain',
+    ...                               provided=ITestUtility)
+    Traceback (most recent call last):
+    AttributeError: getPhysicalPath
+
+    >>> import OFS.SimpleItem
+    >>> class SITestUtility(OFS.SimpleItem.SimpleItem, TestUtility): pass
+    >>> si_util = self.app.a.b._setObject('si_util', SITestUtility('si_util'))
+    >>> sitemanager_a.registerUtility(self.app.a.b.si_util,
+    ...                               name=u'with_aq_chain',
+    ...                               provided=ITestUtility)
+
+When we query the utility, it is returned with its original context:
+
+    >>> si_util = sitemanager_a.getUtility(ITestUtility, name='with_aq_chain')
+    >>> si_util
+    <SITestUtility at /a/b/si_util>
+    >>> si_util.getPhysicalPath()
+    ('', 'a', 'b', 'si_util')
+    >>> si_util.aq_chain
+    [<SITestUtility at /a/b/si_util>, <Folder at /a/b>, <Folder at /a>, <Application at >, <ZPublisher.BaseRequest.RequestContainer object at 0x...>]
+    >>> si_util.absolute_url()
+    'http://nohost/a/b/si_util'
+
+    >>> zope.component.getUtility(ITestUtility, name='with_aq_chain')
+    <SITestUtility at /a/b/si_util>
+
+If we try to register a component with absolute path using another
+path, it does not get reregistered again, so getUtility returns the
+utility using the previously registered path:
+
+    >>> sitemanager_a.registerUtility(
+    ...     self.app.a.b.si_util.aq_base.__of__(self.app.a),
+    ...     name=u'with_aq_chain',
+    ...     provided=ITestUtility)
+    >>> zope.component.getUtility(ITestUtility, name='with_aq_chain')
+    <SITestUtility at /a/b/si_util>
+
+And just to mix things up a bit. Getting back multiple utilities
+should allow us to test aq, non-aq based components and components
+having an absolute aqusition path.
+
+First we have to register aq and non-aq based components in our
+registry (a component having an absolute aqusition path already exists):
+
+    >>> sitemanager_a.registerUtility(AQTestUtility('test'),
+    ...                               name=u'aq_wrapped',
+    ...                               provided=ITestUtility)
+    >>> sitemanager_a.registerUtility(TestUtility('test'),
+    ...                               name=u'hello_world',
+    ...                               provided=ITestUtility)
+
+We start with getUtilitiesFor():
+
+    >>> utils = [x for x in sitemanager_a.getUtilitiesFor(ITestUtility)]
+    >>> len(utils)
+    3
+
+    >>> nonaqutils = [(name, comp)
+    ...               for name, comp in utils if not IAcquirer.providedBy(comp)]
+    >>> len(nonaqutils)
+    1
+    >>> name, comp = nonaqutils[0]
+    >>> Acquisition.aq_parent(comp) is None
+    True
+
+    >>> aqutils = [(name, comp)
+    ...            for name, comp in utils if IAcquirer.providedBy(comp)]
+    >>> len(aqutils)
+    2
+    >>> aqutils
+    [(u'aq_wrapped', <Utility AQTestUtility "test">), 
+     (u'with_aq_chain', <SITestUtility at /a/b/si_util>)]
+
+And then getAllUtilitiesRegisteredFor():
+
+    >>> utils = [x for x in
+    ...          sitemanager_a.getAllUtilitiesRegisteredFor(ITestUtility)]
+    >>> len(utils)
+    3
+
+    >>> nonaqutils = [comp for comp in utils if not IAcquirer.providedBy(comp)]
+    >>> len(nonaqutils)
+    1
+    >>> comp = nonaqutils[0]
+    >>> Acquisition.aq_parent(comp) is None
+    True
+
+    >>> aqutils = [comp for comp in utils if IAcquirer.providedBy(comp)]
+    >>> len(aqutils)
+    2
+    >>> aqutils
+    [<SITestUtility at /a/b/si_util>, <Utility AQTestUtility "test">]
+
+And registeredUtilities():
+
+    >>> utils = [r.component for r in sitemanager_a.registeredUtilities()]
+    >>> len(utils)
+    3
+
+    >>> nonaqutils = [comp for comp in utils if not IAcquirer.providedBy(comp)]
+    >>> len(nonaqutils)
+    1
+    >>> comp = nonaqutils[0]
+    >>> Acquisition.aq_parent(comp) is None
+    True
+
+    >>> aqutils = [comp for comp in utils if IAcquirer.providedBy(comp)]
+    >>> len(aqutils)
+    2
+    >>> aqutils
+    [<SITestUtility at /a/b/si_util>, <Utility AQTestUtility "test">]
+
+
 Nested Sites
 ------------
 

Modified: five.localsitemanager/branches/icemac-absolute-path-components/src/five/localsitemanager/registry.py
===================================================================
--- five.localsitemanager/branches/icemac-absolute-path-components/src/five/localsitemanager/registry.py	2008-08-26 08:57:37 UTC (rev 90266)
+++ five.localsitemanager/branches/icemac-absolute-path-components/src/five/localsitemanager/registry.py	2008-08-26 09:03:49 UTC (rev 90267)
@@ -1,15 +1,18 @@
 import Acquisition
+import persistent
 import OFS.ObjectManager
 from Acquisition.interfaces import IAcquirer
 from zope.app.component.hooks import getSite
 from zope.app.component.interfaces import ISite
 from zope.component.persistentregistry import PersistentAdapterRegistry
 from zope.component.persistentregistry import PersistentComponents
-from zope.component.registry import UtilityRegistration
+from zope.component.registry import UtilityRegistration, _getUtilityProvided
 from zope.interface.adapter import VerifyingAdapterLookup
 from zope.interface.adapter import _lookup
 from zope.interface.adapter import _lookupAll
 from zope.interface.adapter import _subscriptions
+import zope.event
+import zope.component.interfaces
 from ZPublisher.BaseRequest import RequestContainer
 
 from five.localsitemanager.utils import get_parent
@@ -99,6 +102,13 @@
     only if the comp has an aq wrapper to begin with.
     """
 
+    # If component is stored as a ComponentPathWrapper, we traverse to
+    # the component using the stored path:
+    if isinstance(comp, ComponentPathWrapper):
+        return comp.component.__of__(
+            getSite().unrestrictedTraverse(comp.path[:-1]))
+
+
     # BBB: The primary reason for doing this sort of wrapping of
     # returned utilities is to support CMF tool-like functionality where
     # a tool expects its aq_parent to be the portal object. New code
@@ -156,6 +166,16 @@
     return base.__of__(_rewrap(parent))
 
 
+class ComponentPathWrapper(persistent.Persistent):
+    
+    def __init__(self, component, path):
+        self.component = component
+        self.path = path
+
+    def __eq__(self, other):
+        return self.component == other
+    
+
 class PersistentComponents \
           (PersistentComponents,
            OFS.ObjectManager.ObjectManager):
@@ -177,3 +197,38 @@
             reg.component=_wrap(reg.component, self)
             yield reg
 
+    def registerUtility(self, component, provided=None, name=u'', info=u'',
+                        event=True):
+        if provided is None:
+            provided = _getUtilityProvided(component)
+
+        if (self._utility_registrations.get((provided, name))
+            == (component, info)):
+            # already registered
+            return
+
+        subscribed = False
+        for ((p, _), data) in self._utility_registrations.iteritems():
+            if p == provided and data[0] == component:
+                subscribed = True
+                break
+
+        # store path information if it exists
+        if hasattr(component, 'aq_parent'):
+            if Acquisition.aq_parent(component) != None:
+                path = component.getPhysicalPath()
+                wrapped_component = ComponentPathWrapper(
+                    Acquisition.aq_base(component), path)
+        else:
+            wrapped_component = component
+        self._utility_registrations[(provided, name)] = wrapped_component, info
+        self.utilities.register((), provided, name, wrapped_component)
+
+        if not subscribed:
+            self.utilities.subscribe((), provided, wrapped_component)
+
+        if event:
+            zope.event.notify(zope.component.interfaces.Registered(
+                UtilityRegistration(self, provided, name, component, info)
+                ))
+        



More information about the Checkins mailing list