[Checkins] SVN: five.localsitemanager/branches/1.0/ branching version 1.0 (last version to support zope.component < 3.5.0)

Michael Howitz mh at gocept.com
Tue Aug 26 11:25:35 EDT 2008


Log message for revision 90343:
  branching version 1.0 (last version to support zope.component < 3.5.0)
  

Changed:
  A   five.localsitemanager/branches/1.0/
  D   five.localsitemanager/branches/1.0/CHANGES.txt
  A   five.localsitemanager/branches/1.0/CHANGES.txt
  D   five.localsitemanager/branches/1.0/src/five/localsitemanager/localsitemanager.txt
  A   five.localsitemanager/branches/1.0/src/five/localsitemanager/localsitemanager.txt
  D   five.localsitemanager/branches/1.0/src/five/localsitemanager/registry.py
  A   five.localsitemanager/branches/1.0/src/five/localsitemanager/registry.py

-=-
Copied: five.localsitemanager/branches/1.0 (from rev 90340, five.localsitemanager/trunk)

Deleted: five.localsitemanager/branches/1.0/CHANGES.txt
===================================================================
--- five.localsitemanager/trunk/CHANGES.txt	2008-08-26 15:17:06 UTC (rev 90340)
+++ five.localsitemanager/branches/1.0/CHANGES.txt	2008-08-26 15:25:34 UTC (rev 90343)
@@ -1,50 +0,0 @@
-Changelog
-=========
-
-0.5 - Unreleased
-----------------
-
-* Added buildout for project, so testing can be done using ``bin/test``.
-
-0.4 - 2008-07-23
-----------------
-
-* Rewrite PersistentComponents.registeredUtilities to not use
-  internal methods. This makes it compatible with both zope.component <3.5.0dev
-  and >3.5.0dev.
-
-
-0.3 - 2007-12-24
-----------------
-
-* Fixed potential aq problem when assigning various values to the utilities
-  registry of the component registry.
-
-0.2 - 2007-06-30
-----------------
-
-* Refactored and fixed aq wrapping: Nested site managers now return utilities
-  wrapped in the right context. RequestContainers are removed and wrapped
-  utilities are cached. This requires a special LookupClass called
-  'FiveVerifyingAdapterLookup' in all 'utilities' registries used below a
-  five.localsitemanager site.
-
-
-0.1.2 - 2007-06-23
-------------------
-
-* Corrected the zip-safe flag to be False.
-
-
-0.1.1 - 2007-03-05
-------------------
-
-* Fixed aq wrapping when looking up a utility that is actually the component
-  registry's parent (the ISite).
-
-
-0.1 (2007-02-27)
-----------------
-
-* Initial version
-

Copied: five.localsitemanager/branches/1.0/CHANGES.txt (from rev 90341, five.localsitemanager/trunk/CHANGES.txt)
===================================================================
--- five.localsitemanager/branches/1.0/CHANGES.txt	                        (rev 0)
+++ five.localsitemanager/branches/1.0/CHANGES.txt	2008-08-26 15:25:34 UTC (rev 90343)
@@ -0,0 +1,66 @@
+Changelog
+=========
+
+0.5 - Unreleased
+----------------
+
+* 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).
+
+  For storing path information the component must implement
+  getPhysicalPath and have an absolute path.
+
+  When a component registered as utility is moved and registered again
+  the path stored in registry gets updated.
+
+
+0.4 - 2008-07-23
+----------------
+
+* Rewrite PersistentComponents.registeredUtilities to not use
+  internal methods. This makes it compatible with both zope.component <3.5.0dev
+  and >3.5.0dev.
+
+
+0.3 - 2007-12-24
+----------------
+
+* Fixed potential aq problem when assigning various values to the utilities
+  registry of the component registry.
+
+0.2 - 2007-06-30
+----------------
+
+* Refactored and fixed aq wrapping: Nested site managers now return utilities
+  wrapped in the right context. RequestContainers are removed and wrapped
+  utilities are cached. This requires a special LookupClass called
+  'FiveVerifyingAdapterLookup' in all 'utilities' registries used below a
+  five.localsitemanager site.
+
+
+0.1.2 - 2007-06-23
+------------------
+
+* Corrected the zip-safe flag to be False.
+
+
+0.1.1 - 2007-03-05
+------------------
+
+* Fixed aq wrapping when looking up a utility that is actually the component
+  registry's parent (the ISite).
+
+
+0.1 (2007-02-27)
+----------------
+
+* Initial version
+

Deleted: five.localsitemanager/branches/1.0/src/five/localsitemanager/localsitemanager.txt
===================================================================
--- five.localsitemanager/trunk/src/five/localsitemanager/localsitemanager.txt	2008-08-26 15:17:06 UTC (rev 90340)
+++ five.localsitemanager/branches/1.0/src/five/localsitemanager/localsitemanager.txt	2008-08-26 15:25:34 UTC (rev 90343)
@@ -1,380 +0,0 @@
-Local Site Manager
-==================
-
-We start from an empty folder. Since ``OFS.Folder``'s extend
-``ObjectSiteManager`` they all get to be ``IPossibleSite``'s.
-
-    >>> from OFS.Folder import Folder
-    >>> site = Folder('site')
-
-Of course we now need to transform that IPossibleSite into a real ISite.
-
-    >>> import zope.component
-    >>> from zope.app.component.hooks import setSite as setActiveSite
-    >>> from zope.app.component.hooks import clearSite
-
-    >>> from zope.app.component.hooks import setHooks
-    >>> setHooks()
-
-    >>> from five.localsitemanager import make_objectmanager_site
-    >>> make_objectmanager_site(site)
-    >>> sitemanager = site.getSiteManager()
-    >>> sitemanager
-    <PersistentComponents ...>
-
-Make sure this site has it's ``__bases__`` configure appropriately.
-
-    >>> sitemanager.__bases__
-    (<BaseGlobalComponents ...>,)
-
-Utilities
----------
-
-Utilities can now be registered with our site manager. We can confirm this by
-setting up a test utility.
-
-    >>> from OFS.SimpleItem import SimpleItem
-    >>> from zope import interface
-
-    >>> class ITestUtility(interface.Interface): pass
-    >>> class TestUtility(object):
-    ...     interface.implements(ITestUtility)
-    ...     def __init__(self, id):
-    ...         self.id = id
-    ...     def __repr__(self):
-    ...         return '<Utility %s "%s">' % (self.__class__.__name__, self.id)
-
-    >>> sitemanager.registerUtility(TestUtility('test'),
-    ...                             name=u'hello_world',
-    ...                             provided=ITestUtility)
-    >>> sitemanager.getUtility(ITestUtility, name=u'hello_world')
-    <Utility TestUtility "test">
-
-Make sure the utility lookup only works when the correct active site has
-been configured.
-
-    >>> setActiveSite(None)
-    >>> zope.component.queryUtility(ITestUtility, name=u'hello_world') is None
-    True
-
-    >>> setActiveSite(site)
-    >>> zope.component.queryUtility(ITestUtility, name=u'hello_world')
-    <Utility TestUtility "test">
-
-Adapters
----------
-
-Adapters can now be registered with our site manager. We can confirm this by
-setting up a test adapter.
-
-    >>> from OFS.SimpleItem import SimpleItem
-    >>> from zope import interface
-
-    >>> class IFoo(interface.Interface): pass
-    >>> class Foo(object):
-    ...     interface.implements(IFoo)
-    ...     def __init__(self, id):
-    ...         self.id = id
-
-    >>> class ITestAdapter(interface.Interface): pass
-    >>> class TestAdapter(object):
-    ...     interface.implements(ITestAdapter)
-    ...     def __init__(self, context):
-    ...         self.context = context
-    ...     def __repr__(self):
-    ...         return '<Adapter %s adapting "%s">' % (self.__class__.__name__,
-    ...                                                self.context.id)
-
-    >>> sitemanager.registerAdapter(TestAdapter,
-    ...                             required=(IFoo,),
-    ...                             provided=ITestAdapter)
-    >>> sitemanager.getAdapter(Foo('foo'), ITestAdapter)
-    <Adapter TestAdapter adapting "foo">
-
-Make sure the adapter lookup only works when the correct active site has
-been configured.
-
-    >>> setActiveSite(None)
-    >>> zope.component.queryAdapter(Foo('foo'), ITestAdapter) is None
-    True
-
-    >>> setActiveSite(site)
-    >>> zope.component.queryAdapter(Foo('foo'), ITestAdapter)
-    <Adapter TestAdapter adapting "foo">
-    >>> ITestAdapter(Foo('foo'))
-    <Adapter TestAdapter adapting "foo">
-
-Acquisition
------------
-
-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
-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
-utility is acquisition aware to begin with.
-
-    >>> import Acquisition
-    >>> from Acquisition.interfaces import IAcquirer
-
-First off, our utility isn't aq-wrapped so asking it what is aq_parent is
-should return None.
-
-    >>> comp = sitemanager.queryUtility(ITestUtility, name=u'hello_world')
-    >>> Acquisition.aq_parent(comp) is None
-    True
-
-So now we setup a utility that is aq-aware.
-
-    >>> class AQTestUtility(Acquisition.Explicit, TestUtility): pass
-    >>> sitemanager.registerUtility(AQTestUtility('test'),
-    ...                             name=u'aq_wrapped',
-    ...                             provided=ITestUtility)
-
-And of course the aq parent should be the site now.
-
-    >>> comp = sitemanager.getUtility(ITestUtility, name=u'aq_wrapped')
-    >>> Acquisition.aq_parent(comp) is site
-    True
-
-And just to mix things up a bit. Getting back multiple utilities should allow
-us to test both aq and non-aq based components.
-
-We start with getUtilitiesFor():
-
-    >>> utils = [x for x in sitemanager.getUtilitiesFor(ITestUtility)]
-    >>> len(utils)
-    2
-
-    >>> 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)
-    1
-    >>> name, comp = aqutils[0]
-    >>> Acquisition.aq_parent(comp) is site
-    True
-
-And then getAllUtilitiesRegisteredFor():
-
-    >>> utils = [x for x in
-    ...          sitemanager.getAllUtilitiesRegisteredFor(ITestUtility)]
-    >>> len(utils)
-    2
-
-    >>> 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)
-    1
-    >>> comp = aqutils[0]
-    >>> Acquisition.aq_parent(comp) is site
-    True
-
-And registeredUtilities():
-
-    >>> utils = [ r.component for r in sitemanager.registeredUtilities() ]
-    >>> len(utils)
-    2
-
-    >>> 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)
-    1
-    >>> comp = aqutils[0]
-    >>> Acquisition.aq_parent(comp) is site
-    True
-
-Nested Sites
-------------
-
-Whenever a component is queried using the component registry, the active
-component registry (ie site manager) needs to be smart enough to check all
-*parent* component registries as well.
-
-Implementation-wise this means that each component registry needs to have an
-appropriate ``__bases__`` attribute configured that is aware of containment
-and (in the case of Zope 2) acquisition to some respect.
-
-Start by creating some nested sites.
-
-    >>> from five.localsitemanager import update_sitemanager_bases
-
-    >>> folder1 = Folder('folder1')
-    >>> make_objectmanager_site(folder1)
-    >>> update_sitemanager_bases(folder1)
-
-    >>> folder1_1 = Folder('folder1_1')
-    >>> make_objectmanager_site(folder1_1)
-    >>> ignored = folder1._setObject('folder1_1', folder1_1)
-    >>> folder1_1 = folder1['folder1_1']
-    >>> update_sitemanager_bases(folder1_1)
-
-Now we check the actual next-site-lookup logic to make sure it's working.
-
-    >>> from five.localsitemanager import find_next_sitemanager, get_parent
-
-Needed to implement our own get_parent (zope3 has one) that is acquisition
-aware.
-
-    >>> get_parent(folder1)
-    Traceback (most recent call last):
-      ...
-    TypeError: ('Not enough context...
-
-    >>> get_parent(folder1_1)
-    <Folder at folder1>
-
-Any logic that sets up a site manager's ``__bases__`` will use the
-``find_next_sitemanager`` function to figure out the next closest place
-to look.
-
-    >>> find_next_sitemanager(folder1) is None
-    True
-
-    >>> find_next_sitemanager(folder1_1)
-    <PersistentComponents ...>
-
-Now we make sure that that the ``__bases__`` have been setup appropriately.
-
-    >>> folder1.getSiteManager().__bases__
-    (<BaseGlobalComponents base>,)
-
-    >>> folder1_1.getSiteManager().__bases__
-    (<PersistentComponents ...>,)
-
-
-Acquisition Context with Nested Sites
--------------------------------------
-
-    >>> from zope.component import queryUtility
-
-Register a utility with both of the nested site managers:
-
-    >>> sm1 = folder1.getSiteManager()
-    >>> sm1.registerUtility(AQTestUtility('util1'),
-    ...                     name=u'util1',
-    ...                     provided=ITestUtility)
-
-    >>> sm1_1 = folder1_1.getSiteManager()
-    >>> sm1_1.registerUtility(AQTestUtility('util1_1'),
-    ...                           name=u'util1_1',
-    ...                           provided=ITestUtility)
-
-    >>> folder1_1.getSiteManager().__bases__
-    (<PersistentComponents ...>,)
-
-Lookup both utilities in the context of the first site manager:
-
-    >>> setActiveSite(folder1)
-    >>> util1 = queryUtility(ITestUtility, name=u'util1')
-    >>> util1
-    <Utility AQTestUtility "util1">
-
-    >>> util1.aq_chain
-    [<Utility AQTestUtility "util1">, <Folder at folder1>]
-
-The second utility isn't available in the first site manager:
-
-    >>> queryUtility(ITestUtility, name=u'util_1') is None
-    True
-
-Lookup both utilities in the context of the second site manager:
-
-    >>> setActiveSite(folder1_1)
-    >>> util1 = queryUtility(ITestUtility, name=u'util1')
-    >>> util1
-    <Utility AQTestUtility "util1">
-
-We expect to get wrapped in the context of the site manager the utility is
-registered with:
-
-    >>> util1.aq_chain
-    [<Utility AQTestUtility "util1">, <Folder at folder1>]
-
-    >>> util1_1 = queryUtility(ITestUtility, name=u'util1_1')
-    >>> util1_1
-    <Utility AQTestUtility "util1_1">
-
-    >>> util1_1.aq_chain
-    [<Utility AQTestUtility "util1_1">, <Folder at folder1/folder1_1>, <Folder at folder1>]
-
-
-Acquisition Context of Global Utilities
----------------------------------------
-
-    >>> from zope.component import getGlobalSiteManager
-
-Register a utility with both of the nested site managers:
-
-    >>> gsm = getGlobalSiteManager()
-    >>> gsm.registerUtility(AQTestUtility('globalutil1'),
-    ...                     name=u'globalutil1',
-    ...                     provided=ITestUtility)
-
-    >>> sm1 = folder1.getSiteManager()
-    >>> sm1.registerUtility(AQTestUtility('localutil1'),
-    ...                     name=u'localutil1',
-    ...                     provided=ITestUtility)
-
-Lookup both utilities in the context of the global site manager:
-
-    >>> clearSite()
-    >>> globalutil1 = queryUtility(ITestUtility, name=u'globalutil1')
-    >>> globalutil1
-    <Utility AQTestUtility "globalutil1">
-
-    >>> getattr(globalutil1, 'aq_chain', None) is None
-    True
-
-The local utility isn't available in the global site manager:
-
-    >>> queryUtility(ITestUtility, name=u'localutil1') is None
-    True
-
-Lookup both utilities in the context of the local site manager:
-
-    >>> setActiveSite(folder1)
-    >>> globalutil1 = queryUtility(ITestUtility, name=u'globalutil1')
-    >>> globalutil1
-    <Utility AQTestUtility "globalutil1">
-
-We expect the global utility to get no Acquisition context:
-
-    >>> getattr(globalutil1, 'aq_chain', None) is None
-    True
-
-For the local utility we expect to get wrapped in the context of the local
-site manager:
-
-    >>> localutil1 = queryUtility(ITestUtility, name=u'localutil1')
-    >>> localutil1
-    <Utility AQTestUtility "localutil1">
-
-    >>> localutil1.aq_chain
-    [<Utility AQTestUtility "localutil1">, <Folder at folder1>]
-
-Clean up
---------
-
-    >>> from zope.testing.cleanup import cleanUp
-    >>> clearSite()
-    >>> cleanUp()

Copied: five.localsitemanager/branches/1.0/src/five/localsitemanager/localsitemanager.txt (from rev 90341, five.localsitemanager/trunk/src/five/localsitemanager/localsitemanager.txt)
===================================================================
--- five.localsitemanager/branches/1.0/src/five/localsitemanager/localsitemanager.txt	                        (rev 0)
+++ five.localsitemanager/branches/1.0/src/five/localsitemanager/localsitemanager.txt	2008-08-26 15:25:34 UTC (rev 90343)
@@ -0,0 +1,549 @@
+Local Site Manager
+==================
+
+We start from an empty folder. Since ``OFS.Folder``'s extend
+``ObjectSiteManager`` they all get to be ``IPossibleSite``'s.
+
+    >>> from OFS.Folder import Folder
+    >>> site = Folder('site')
+
+Of course we now need to transform that IPossibleSite into a real ISite.
+
+    >>> import zope.component
+    >>> from zope.app.component.hooks import setSite as setActiveSite
+    >>> from zope.app.component.hooks import clearSite
+
+    >>> from zope.app.component.hooks import setHooks
+    >>> setHooks()
+
+    >>> from five.localsitemanager import make_objectmanager_site
+    >>> make_objectmanager_site(site)
+    >>> sitemanager = site.getSiteManager()
+    >>> sitemanager
+    <PersistentComponents ...>
+
+Make sure this site has it's ``__bases__`` configure appropriately.
+
+    >>> sitemanager.__bases__
+    (<BaseGlobalComponents ...>,)
+
+Utilities
+---------
+
+Utilities can now be registered with our site manager. We can confirm this by
+setting up a test utility.
+
+    >>> from OFS.SimpleItem import SimpleItem
+    >>> from zope import interface
+
+    >>> class ITestUtility(interface.Interface): pass
+    >>> class TestUtility(object):
+    ...     interface.implements(ITestUtility)
+    ...     def __init__(self, id):
+    ...         self.id = id
+    ...     def __repr__(self):
+    ...         return '<Utility %s "%s">' % (self.__class__.__name__, self.id)
+
+    >>> sitemanager.registerUtility(TestUtility('test'),
+    ...                             name=u'hello_world',
+    ...                             provided=ITestUtility)
+    >>> sitemanager.getUtility(ITestUtility, name=u'hello_world')
+    <Utility TestUtility "test">
+
+Make sure the utility lookup only works when the correct active site has
+been configured.
+
+    >>> setActiveSite(None)
+    >>> zope.component.queryUtility(ITestUtility, name=u'hello_world') is None
+    True
+
+    >>> setActiveSite(site)
+    >>> zope.component.queryUtility(ITestUtility, name=u'hello_world')
+    <Utility TestUtility "test">
+
+Adapters
+---------
+
+Adapters can now be registered with our site manager. We can confirm this by
+setting up a test adapter.
+
+    >>> from OFS.SimpleItem import SimpleItem
+    >>> from zope import interface
+
+    >>> class IFoo(interface.Interface): pass
+    >>> class Foo(object):
+    ...     interface.implements(IFoo)
+    ...     def __init__(self, id):
+    ...         self.id = id
+
+    >>> class ITestAdapter(interface.Interface): pass
+    >>> class TestAdapter(object):
+    ...     interface.implements(ITestAdapter)
+    ...     def __init__(self, context):
+    ...         self.context = context
+    ...     def __repr__(self):
+    ...         return '<Adapter %s adapting "%s">' % (self.__class__.__name__,
+    ...                                                self.context.id)
+
+    >>> sitemanager.registerAdapter(TestAdapter,
+    ...                             required=(IFoo,),
+    ...                             provided=ITestAdapter)
+    >>> sitemanager.getAdapter(Foo('foo'), ITestAdapter)
+    <Adapter TestAdapter adapting "foo">
+
+Make sure the adapter lookup only works when the correct active site has
+been configured.
+
+    >>> setActiveSite(None)
+    >>> zope.component.queryAdapter(Foo('foo'), ITestAdapter) is None
+    True
+
+    >>> setActiveSite(site)
+    >>> zope.component.queryAdapter(Foo('foo'), ITestAdapter)
+    <Adapter TestAdapter adapting "foo">
+    >>> ITestAdapter(Foo('foo'))
+    <Adapter TestAdapter adapting "foo">
+
+Acquisition
+-----------
+
+Now to mix a little required Zope 2 confusion into everything, we must ensure
+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
+utility is acquisition aware to begin with.
+
+    >>> import Acquisition
+    >>> from Acquisition.interfaces import IAcquirer
+
+First off, our utility isn't aq-wrapped so asking it what is aq_parent is
+should return None.
+
+    >>> comp = sitemanager.queryUtility(ITestUtility, name=u'hello_world')
+    >>> Acquisition.aq_parent(comp) is None
+    True
+
+So now we setup a utility that is aq-aware.
+
+    >>> class AQTestUtility(Acquisition.Explicit, TestUtility): pass
+    >>> sitemanager.registerUtility(AQTestUtility('test'),
+    ...                             name=u'aq_wrapped',
+    ...                             provided=ITestUtility)
+
+And of course the aq parent should be the site now.
+
+    >>> comp = sitemanager.getUtility(ITestUtility, name=u'aq_wrapped')
+    >>> Acquisition.aq_parent(comp) is site
+    True
+
+And just to mix things up a bit. Getting back multiple utilities should allow
+us to test both aq and non-aq based components.
+
+We start with getUtilitiesFor():
+
+    >>> utils = [x for x in sitemanager.getUtilitiesFor(ITestUtility)]
+    >>> len(utils)
+    2
+
+    >>> 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)
+    1
+    >>> name, comp = aqutils[0]
+    >>> Acquisition.aq_parent(comp) is site
+    True
+
+And then getAllUtilitiesRegisteredFor():
+
+    >>> utils = [x for x in
+    ...          sitemanager.getAllUtilitiesRegisteredFor(ITestUtility)]
+    >>> len(utils)
+    2
+
+    >>> 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)
+    1
+    >>> comp = aqutils[0]
+    >>> Acquisition.aq_parent(comp) is site
+    True
+
+And registeredUtilities():
+
+    >>> utils = [ r.component for r in sitemanager.registeredUtilities() ]
+    >>> len(utils)
+    2
+
+    >>> 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)
+    1
+    >>> comp = aqutils[0]
+    >>> 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: Component <Utility AQTestUtility "util"> does not implement getPhysicalPath, so register it unwrapped or implement this method.
+    >>> 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 move a registered component (which has an absolute path) to new
+place, the registration gets updated after calling registerUtility
+again:
+
+    >>> ignored = self.app.a.b._setObject('c', Folder('c'))
+    >>> si_util = self.app.a.b.si_util.aq_base
+    >>> self.app.a.b._delObject('si_util')
+    >>> si_util.id = 'si_util_cped'
+    >>> ignored = self.app.a.b.c._setObject('si_util_cped', si_util)
+    >>> sitemanager_a.registerUtility(
+    ...     self.app.a.b.c.si_util_cped,
+    ...     name=u'with_aq_chain',
+    ...     provided=ITestUtility)
+    >>> zope.component.getUtility(ITestUtility, name='with_aq_chain')
+    <SITestUtility at /a/b/c/si_util_cped>
+
+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/c/si_util_cped>)]
+
+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/c/si_util_cped>, <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/c/si_util_cped>, <Utility AQTestUtility "test">]
+
+Nested Sites
+------------
+
+Whenever a component is queried using the component registry, the active
+component registry (ie site manager) needs to be smart enough to check all
+*parent* component registries as well.
+
+Implementation-wise this means that each component registry needs to have an
+appropriate ``__bases__`` attribute configured that is aware of containment
+and (in the case of Zope 2) acquisition to some respect.
+
+Start by creating some nested sites.
+
+    >>> from five.localsitemanager import update_sitemanager_bases
+
+    >>> folder1 = Folder('folder1')
+    >>> make_objectmanager_site(folder1)
+    >>> update_sitemanager_bases(folder1)
+
+    >>> folder1_1 = Folder('folder1_1')
+    >>> make_objectmanager_site(folder1_1)
+    >>> ignored = folder1._setObject('folder1_1', folder1_1)
+    >>> folder1_1 = folder1['folder1_1']
+    >>> update_sitemanager_bases(folder1_1)
+
+Now we check the actual next-site-lookup logic to make sure it's working.
+
+    >>> from five.localsitemanager import find_next_sitemanager, get_parent
+
+Needed to implement our own get_parent (zope3 has one) that is acquisition
+aware.
+
+    >>> get_parent(folder1)
+    Traceback (most recent call last):
+      ...
+    TypeError: ('Not enough context...
+
+    >>> get_parent(folder1_1)
+    <Folder at folder1>
+
+Any logic that sets up a site manager's ``__bases__`` will use the
+``find_next_sitemanager`` function to figure out the next closest place
+to look.
+
+    >>> find_next_sitemanager(folder1) is None
+    True
+
+    >>> find_next_sitemanager(folder1_1)
+    <PersistentComponents ...>
+
+Now we make sure that that the ``__bases__`` have been setup appropriately.
+
+    >>> folder1.getSiteManager().__bases__
+    (<BaseGlobalComponents base>,)
+
+    >>> folder1_1.getSiteManager().__bases__
+    (<PersistentComponents ...>,)
+
+
+Acquisition Context with Nested Sites
+-------------------------------------
+
+    >>> from zope.component import queryUtility
+
+Register a utility with both of the nested site managers:
+
+    >>> sm1 = folder1.getSiteManager()
+    >>> sm1.registerUtility(AQTestUtility('util1'),
+    ...                     name=u'util1',
+    ...                     provided=ITestUtility)
+
+    >>> sm1_1 = folder1_1.getSiteManager()
+    >>> sm1_1.registerUtility(AQTestUtility('util1_1'),
+    ...                           name=u'util1_1',
+    ...                           provided=ITestUtility)
+
+    >>> folder1_1.getSiteManager().__bases__
+    (<PersistentComponents ...>,)
+
+Lookup both utilities in the context of the first site manager:
+
+    >>> setActiveSite(folder1)
+    >>> util1 = queryUtility(ITestUtility, name=u'util1')
+    >>> util1
+    <Utility AQTestUtility "util1">
+
+    >>> util1.aq_chain
+    [<Utility AQTestUtility "util1">, <Folder at folder1>]
+
+The second utility isn't available in the first site manager:
+
+    >>> queryUtility(ITestUtility, name=u'util_1') is None
+    True
+
+Lookup both utilities in the context of the second site manager:
+
+    >>> setActiveSite(folder1_1)
+    >>> util1 = queryUtility(ITestUtility, name=u'util1')
+    >>> util1
+    <Utility AQTestUtility "util1">
+
+We expect to get wrapped in the context of the site manager the utility is
+registered with:
+
+    >>> util1.aq_chain
+    [<Utility AQTestUtility "util1">, <Folder at folder1>]
+
+    >>> util1_1 = queryUtility(ITestUtility, name=u'util1_1')
+    >>> util1_1
+    <Utility AQTestUtility "util1_1">
+
+    >>> util1_1.aq_chain
+    [<Utility AQTestUtility "util1_1">, <Folder at folder1/folder1_1>, <Folder at folder1>]
+
+Utilities stored with relative path
+-----------------------------------
+
+If we register a utility which has only a relative path, the path is
+_not_ stored and the utility is returned relative to the registry. (In
+the example we register folder_1/folder1_1/util in the registry of
+folder_1.):
+
+    >>> folder1_1._setObject('util', SITestUtility('util'), set_owner=False)
+    'util'
+    >>> sm1.registerUtility(folder1_1.util,
+    ...                     name=u'util2',
+    ...                     provided=ITestUtility)
+    >>> sm1.getUtility(ITestUtility, name='util2')
+    <SITestUtility at folder1/util>
+
+
+Acquisition Context of Global Utilities
+---------------------------------------
+
+    >>> from zope.component import getGlobalSiteManager
+
+Register a utility with both of the nested site managers:
+
+    >>> gsm = getGlobalSiteManager()
+    >>> gsm.registerUtility(AQTestUtility('globalutil1'),
+    ...                     name=u'globalutil1',
+    ...                     provided=ITestUtility)
+
+    >>> sm1 = folder1.getSiteManager()
+    >>> sm1.registerUtility(AQTestUtility('localutil1'),
+    ...                     name=u'localutil1',
+    ...                     provided=ITestUtility)
+
+Lookup both utilities in the context of the global site manager:
+
+    >>> clearSite()
+    >>> globalutil1 = queryUtility(ITestUtility, name=u'globalutil1')
+    >>> globalutil1
+    <Utility AQTestUtility "globalutil1">
+
+    >>> getattr(globalutil1, 'aq_chain', None) is None
+    True
+
+The local utility isn't available in the global site manager:
+
+    >>> queryUtility(ITestUtility, name=u'localutil1') is None
+    True
+
+Lookup both utilities in the context of the local site manager:
+
+    >>> setActiveSite(folder1)
+    >>> globalutil1 = queryUtility(ITestUtility, name=u'globalutil1')
+    >>> globalutil1
+    <Utility AQTestUtility "globalutil1">
+
+We expect the global utility to get no Acquisition context:
+
+    >>> getattr(globalutil1, 'aq_chain', None) is None
+    True
+
+For the local utility we expect to get wrapped in the context of the local
+site manager:
+
+    >>> localutil1 = queryUtility(ITestUtility, name=u'localutil1')
+    >>> localutil1
+    <Utility AQTestUtility "localutil1">
+
+    >>> localutil1.aq_chain
+    [<Utility AQTestUtility "localutil1">, <Folder at folder1>]
+
+Clean up
+--------
+
+    >>> from zope.testing.cleanup import cleanUp
+    >>> clearSite()
+    >>> cleanUp()

Deleted: five.localsitemanager/branches/1.0/src/five/localsitemanager/registry.py
===================================================================
--- five.localsitemanager/trunk/src/five/localsitemanager/registry.py	2008-08-26 15:17:06 UTC (rev 90340)
+++ five.localsitemanager/branches/1.0/src/five/localsitemanager/registry.py	2008-08-26 15:25:34 UTC (rev 90343)
@@ -1,179 +0,0 @@
-import Acquisition
-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.interface.adapter import VerifyingAdapterLookup
-from zope.interface.adapter import _lookup
-from zope.interface.adapter import _lookupAll
-from zope.interface.adapter import _subscriptions
-from ZPublisher.BaseRequest import RequestContainer
-
-from five.localsitemanager.utils import get_parent
-
-_marker = object()
-
-class FiveVerifyingAdapterLookup(VerifyingAdapterLookup):
-
-    # override some AdapterLookupBase methods for acquisition wrapping
-
-    def _uncached_lookup(self, required, provided, name=u''):
-        result = None
-        order = len(required)
-        for registry in self._registry.ro:
-            byorder = registry._adapters
-            if order >= len(byorder):
-                continue
-
-            extendors = registry._v_lookup._extendors.get(provided)
-            if not extendors:
-                continue
-
-            components = byorder[order]
-            result = _lookup(components, required, extendors, name, 0,
-                             order)
-            if result is not None:
-                result = _wrap(result, registry)
-                break
-
-        self._subscribe(*required)
-
-        return result
-
-    def _uncached_lookupAll(self, required, provided):
-        order = len(required)
-        result = {}
-        for registry in reversed(self._registry.ro):
-            byorder = registry._adapters
-            if order >= len(byorder):
-                continue
-            extendors = registry._v_lookup._extendors.get(provided)
-            if not extendors:
-                continue
-            components = byorder[order]
-            tmp_result = {}
-            _lookupAll(components, required, extendors, tmp_result, 0, order)
-            for k, v in tmp_result.iteritems():
-                tmp_result[k] = _wrap(v, registry)
-            result.update(tmp_result)
-
-        self._subscribe(*required)
-
-        return tuple(result.iteritems())
-
-    def _uncached_subscriptions(self, required, provided):
-        order = len(required)
-        result = []
-        for registry in reversed(self._registry.ro):
-            byorder = registry._subscribers
-            if order >= len(byorder):
-                continue
-
-            if provided is None:
-                extendors = (provided, )
-            else:
-                extendors = registry._v_lookup._extendors.get(provided)
-                if extendors is None:
-                    continue
-
-            tmp_result = []
-            _subscriptions(byorder[order], required, extendors, u'',
-                           result, 0, order)
-            result = [ _wrap(r, registry) for r in result ]
-
-        self._subscribe(*required)
-
-        return result
-
-
-def _recurse_to_site(current, wanted):
-    if not Acquisition.aq_base(current) == wanted:
-        current = _recurse_to_site(get_parent(current), wanted)
-    return current
-
-def _wrap(comp, registry):
-    """Return an aq wrapped component with the site as the parent but
-    only if the comp has an aq wrapper to begin with.
-    """
-
-    # 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
-    # (ie new utilities) should not rely on this predictability to
-    # get the portal object and should search out an alternate means
-    # (possibly retrieve the ISiteRoot utility). Although in most
-    # cases getting at the portal object shouldn't be the required pattern
-    # but instead looking up required functionality via other (possibly
-    # local) components.
-
-    if registry.__bases__ and IAcquirer.providedBy(comp):
-        current_site = getSite()
-        registry_site = Acquisition.aq_base(registry.__parent__)
-        if not ISite.providedBy(registry_site):
-            registry_site = registry_site.__parent__
-
-        if current_site is None:
-            # If no current site can be found, return utilities wrapped in
-            # the site they where registered in. We loose the whole aq chain
-            # here though
-            current_site = Acquisition.aq_base(registry_site)
-
-        parent = None
-
-        if current_site == registry_site:
-            parent = current_site
-        else:
-            parent = _recurse_to_site(current_site, registry_site)
-
-        if parent is None:
-            raise ValueError('Not enough context to acquire parent')
-
-        base = Acquisition.aq_base(comp)
-        # clean up aq_chain, removing REQUEST objects
-        parent = _rewrap(parent)
-
-        if base is not Acquisition.aq_base(parent):
-            # If the component is not the component registry container,
-            # wrap it in the parent
-            comp = base.__of__(parent)
-        else:
-            # If the component happens to be the component registry
-            # container we are looking up a ISiteRoot.
-            # We are not wrapping it in itself but in its own parent
-            comp = base.__of__(Acquisition.aq_parent(parent))
-
-    return comp
-
-def _rewrap(obj):
-    obj = Acquisition.aq_inner(obj)
-    base = Acquisition.aq_base(obj)
-    parent = Acquisition.aq_parent(obj)
-    if not parent or isinstance(parent, RequestContainer):
-        return base
-    return base.__of__(_rewrap(parent))
-
-
-class PersistentComponents \
-          (PersistentComponents,
-           OFS.ObjectManager.ObjectManager):
-    """An implementation of a component registry that can be persisted
-    and looks like a standard ObjectManager.  It also ensures that all
-    utilities have the the parent of this site manager (which should be
-    the ISite) as their acquired parent.
-    """
-
-    def _init_registries(self):
-        super(PersistentComponents, self)._init_registries()
-        utilities = Acquisition.aq_base(self.utilities)
-        utilities.LookupClass = FiveVerifyingAdapterLookup
-        utilities._createLookup()
-        utilities.__parent__ = self
-
-    def registeredUtilities(self):
-        for reg in super(PersistentComponents, self).registeredUtilities():
-            reg.component=_wrap(reg.component, self)
-            yield reg
-

Copied: five.localsitemanager/branches/1.0/src/five/localsitemanager/registry.py (from rev 90341, five.localsitemanager/trunk/src/five/localsitemanager/registry.py)
===================================================================
--- five.localsitemanager/branches/1.0/src/five/localsitemanager/registry.py	                        (rev 0)
+++ five.localsitemanager/branches/1.0/src/five/localsitemanager/registry.py	2008-08-26 15:25:34 UTC (rev 90343)
@@ -0,0 +1,245 @@
+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, _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
+
+_marker = object()
+
+class FiveVerifyingAdapterLookup(VerifyingAdapterLookup):
+
+    # override some AdapterLookupBase methods for acquisition wrapping
+
+    def _uncached_lookup(self, required, provided, name=u''):
+        result = None
+        order = len(required)
+        for registry in self._registry.ro:
+            byorder = registry._adapters
+            if order >= len(byorder):
+                continue
+
+            extendors = registry._v_lookup._extendors.get(provided)
+            if not extendors:
+                continue
+
+            components = byorder[order]
+            result = _lookup(components, required, extendors, name, 0,
+                             order)
+            if result is not None:
+                result = _wrap(result, registry)
+                break
+
+        self._subscribe(*required)
+
+        return result
+
+    def _uncached_lookupAll(self, required, provided):
+        order = len(required)
+        result = {}
+        for registry in reversed(self._registry.ro):
+            byorder = registry._adapters
+            if order >= len(byorder):
+                continue
+            extendors = registry._v_lookup._extendors.get(provided)
+            if not extendors:
+                continue
+            components = byorder[order]
+            tmp_result = {}
+            _lookupAll(components, required, extendors, tmp_result, 0, order)
+            for k, v in tmp_result.iteritems():
+                tmp_result[k] = _wrap(v, registry)
+            result.update(tmp_result)
+
+        self._subscribe(*required)
+
+        return tuple(result.iteritems())
+
+    def _uncached_subscriptions(self, required, provided):
+        order = len(required)
+        result = []
+        for registry in reversed(self._registry.ro):
+            byorder = registry._subscribers
+            if order >= len(byorder):
+                continue
+
+            if provided is None:
+                extendors = (provided, )
+            else:
+                extendors = registry._v_lookup._extendors.get(provided)
+                if extendors is None:
+                    continue
+
+            tmp_result = []
+            _subscriptions(byorder[order], required, extendors, u'',
+                           result, 0, order)
+            result = [ _wrap(r, registry) for r in result ]
+
+        self._subscribe(*required)
+
+        return result
+
+
+def _recurse_to_site(current, wanted):
+    if not Acquisition.aq_base(current) == wanted:
+        current = _recurse_to_site(get_parent(current), wanted)
+    return current
+
+def _wrap(comp, registry):
+    """Return an aq wrapped component with the site as the parent but
+    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 getSite().unrestrictedTraverse(comp.path)
+
+    # 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
+    # (ie new utilities) should not rely on this predictability to
+    # get the portal object and should search out an alternate means
+    # (possibly retrieve the ISiteRoot utility). Although in most
+    # cases getting at the portal object shouldn't be the required pattern
+    # but instead looking up required functionality via other (possibly
+    # local) components.
+
+    if registry.__bases__ and IAcquirer.providedBy(comp):
+        current_site = getSite()
+        registry_site = Acquisition.aq_base(registry.__parent__)
+        if not ISite.providedBy(registry_site):
+            registry_site = registry_site.__parent__
+
+        if current_site is None:
+            # If no current site can be found, return utilities wrapped in
+            # the site they where registered in. We loose the whole aq chain
+            # here though
+            current_site = Acquisition.aq_base(registry_site)
+
+        parent = None
+
+        if current_site == registry_site:
+            parent = current_site
+        else:
+            parent = _recurse_to_site(current_site, registry_site)
+
+        if parent is None:
+            raise ValueError('Not enough context to acquire parent')
+
+        base = Acquisition.aq_base(comp)
+        # clean up aq_chain, removing REQUEST objects
+        parent = _rewrap(parent)
+
+        if base is not Acquisition.aq_base(parent):
+            # If the component is not the component registry container,
+            # wrap it in the parent
+            comp = base.__of__(parent)
+        else:
+            # If the component happens to be the component registry
+            # container we are looking up a ISiteRoot.
+            # We are not wrapping it in itself but in its own parent
+            comp = base.__of__(Acquisition.aq_parent(parent))
+
+    return comp
+
+def _rewrap(obj):
+    obj = Acquisition.aq_inner(obj)
+    base = Acquisition.aq_base(obj)
+    parent = Acquisition.aq_parent(obj)
+    if not parent or isinstance(parent, RequestContainer):
+        return base
+    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):
+    """An implementation of a component registry that can be persisted
+    and looks like a standard ObjectManager.  It also ensures that all
+    utilities have the the parent of this site manager (which should be
+    the ISite) as their acquired parent.
+    """
+
+    def _init_registries(self):
+        super(PersistentComponents, self)._init_registries()
+        utilities = Acquisition.aq_base(self.utilities)
+        utilities.LookupClass = FiveVerifyingAdapterLookup
+        utilities._createLookup()
+        utilities.__parent__ = self
+
+    def registeredUtilities(self):
+        for reg in super(PersistentComponents, self).registeredUtilities():
+            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)
+
+        registration = self._utility_registrations.get((provided, name))
+        if (registration == (component, info)):
+            # already registered
+            if isinstance(registration[0], ComponentPathWrapper):
+                self.utilities.unsubscribe((), provided, registration[0])
+                # update path
+                registration[0].path = component.getPhysicalPath()
+                self.utilities.subscribe((), provided, registration[0])
+            return
+
+        subscribed = False
+        for ((p, _), data) in self._utility_registrations.iteritems():
+            if p == provided and data[0] == component:
+                subscribed = True
+                break
+
+        wrapped_component = component
+        if hasattr(component, 'aq_parent'):
+            # component is acquisition wrapped, so try to store path
+            if not hasattr(component, 'getPhysicalPath'):
+                raise AttributeError(
+                    'Component %r does not implement getPhysicalPath, '
+                    'so register it unwrapped or implement this method.' % 
+                    component)
+            path = component.getPhysicalPath()
+            # If the path is relative we can't store it because we
+            # have nearly no chance to use the path for traversal in
+            # getUtility.
+            if path[0] == '':
+                # We have an absolute path, so we can store it.
+                wrapped_component = ComponentPathWrapper(
+                    Acquisition.aq_base(component), path)
+        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