[Zope-CMF] back-porting a GenericSetup fix to the 1.4 branch

Rob Miller ra at burningman.com
Tue Sep 16 17:28:58 EDT 2008


Rob Miller wrote:
> hi all,
> 
> currently, on the 1.4 branch of GenericSetup, there is what i consider to be a 
> bug in the component registry import code.  the issue is that when you re-run 
> the import step, even if you are not in purge mode, then any utilities that 
> are registered with the given site manager will be replaced with brand new 
> instances.  since local utilities are persistent objects, and often store 
> important information within themselves, this seems to me a big enough issue 
> to warrant a bug-fix on the 1.4 branch.
> 
> this appears to have been resolved on the trunk, as part of a bigger set of 
> component registry improvements.  i've backported the changes (with minor 
> adjustments to account for other version-related differences) to a local 1.4 
> checkout, and have added a regression test that demonstrates the problem and 
> verifies the fix.
> 
> if somebody feels strongly that this is too much of a functionality change for 
> a maintenance branch, please respond here on-list... otherwise, i'll commit 
> this to the 1.4 branch some time in the next day or so.

whoops, forgot the patch:

Index: Products/GenericSetup/tests/test_components.py
===================================================================
--- Products/GenericSetup/tests/test_components.py	(revision 91193)
+++ Products/GenericSetup/tests/test_components.py	(working copy)
@@ -26,13 +26,17 @@
  from Products.Five.component import enableSite
  from Products.Five.component.interfaces import IObjectManagerSite
  from zope.app.component.hooks import setSite, clearSite, setHooks
+from zope.app.component.hooks import getSite
+from zope.component import getMultiAdapter
  from zope.component import getSiteManager
  from zope.component import queryUtility
  from zope.component.globalregistry import base
  from zope.interface import implements
  from zope.interface import Interface

+from Products.GenericSetup.interfaces import IBody
  from Products.GenericSetup.testing import BodyAdapterTestCase
+from Products.GenericSetup.testing import DummySetupEnviron
  from Products.GenericSetup.testing import ExportImportZCMLLayer

  try:
@@ -180,6 +184,30 @@
          self._obj = sm
          self._BODY = _COMPONENTS_BODY

+    def test_no_overwrite(self):
+        # make sure we don't overwrite an existing tool when it
+        # already exists and has the same factory
+        context = DummySetupEnviron()
+        context._should_purge = False
+        importer = getMultiAdapter((self._obj, context), IBody)
+        importer.body = self._BODY # <-- triggers the import :(
+
+        util = queryUtility(IDummyInterface)
+        value = 'bar'
+        util.foo = value
+
+        # re-retrieve to make sure it's the same object
+        util = queryUtility(IDummyInterface)
+        self.assertEquals(getattr(util, 'foo', None), value)
+
+        context = DummySetupEnviron()
+        context._should_purge = False
+        importer = getMultiAdapter((self._obj, context), IBody)
+        importer.body = self._BODY # <-- triggers the import :(
+
+        util = queryUtility(IDummyInterface)
+        self.assertEquals(getattr(util, 'foo', None), value)
+
      def tearDown(self):
          clearSite()

Index: Products/GenericSetup/components.py
===================================================================
--- Products/GenericSetup/components.py	(revision 91193)
+++ Products/GenericSetup/components.py	(working copy)
@@ -127,6 +127,8 @@

      def _initUtilities(self, node):
          site = self._getSite()
+        current_utilities = self.context.registeredUtilities()
+
          for child in node.childNodes:
              if child.nodeName != 'utility':
                  continue
@@ -163,7 +165,16 @@
              elif component:
                  self.context.registerUtility(component, provided, name)
              elif factory is not None:
-                self.context.registerUtility(factory(), provided, name)
+                current = [ utility for utility in current_utilities
+                            if utility.provided==provided and
+                            utility.name==name ]
+                assert len(current) <=1
+
+                new_utility = factory()
+                if current and type(current[0].component) == type(new_utility):
+                    continue
+
+                self.context.registerUtility(new_utility, provided, name)
              else:
                  self._logger.warning("Invalid utility registration for "
                                       "interface %s" % provided)



More information about the Zope-CMF mailing list