[Checkins] SVN: GenericSetup/trunk/ - Added support for importing/exporting Zope 3 component registries

Jens Vagelpohl jens at dataflake.org
Sun Nov 19 08:17:08 EST 2006


Log message for revision 71183:
  - Added support for importing/exporting Zope 3 component registries
    by folding in Hanno Schlichting's GSLocalAddons product.
  

Changed:
  U   GenericSetup/trunk/CHANGES.txt
  U   GenericSetup/trunk/DEPENDENCIES.txt
  A   GenericSetup/trunk/components.py
  U   GenericSetup/trunk/configure.zcml
  A   GenericSetup/trunk/tests/test_components.py

-=-
Modified: GenericSetup/trunk/CHANGES.txt
===================================================================
--- GenericSetup/trunk/CHANGES.txt	2006-11-19 12:03:52 UTC (rev 71182)
+++ GenericSetup/trunk/CHANGES.txt	2006-11-19 13:17:07 UTC (rev 71183)
@@ -1,5 +1,11 @@
 GenericSetup Product Changelog
 
+  GenericSetup 1.2 (unreleased)
+
+    - Added support for importing/exporting Zope 3 component registries
+      by folding in Hanno Schlichting's GSLocalAddons product.
+
+
   GenericSetup 1.2-beta (2006/09/20)
 
     - tool:  Added support for uploading a tarball on the "Import" tab

Modified: GenericSetup/trunk/DEPENDENCIES.txt
===================================================================
--- GenericSetup/trunk/DEPENDENCIES.txt	2006-11-19 12:03:52 UTC (rev 71182)
+++ GenericSetup/trunk/DEPENDENCIES.txt	2006-11-19 13:17:07 UTC (rev 71183)
@@ -1,2 +1 @@
-Zope >= 2.8.5
-Five >= 1.2
+Zope >= 2.10.0

Added: GenericSetup/trunk/components.py
===================================================================
--- GenericSetup/trunk/components.py	2006-11-19 12:03:52 UTC (rev 71182)
+++ GenericSetup/trunk/components.py	2006-11-19 13:17:07 UTC (rev 71183)
@@ -0,0 +1,269 @@
+##############################################################################
+#
+# Copyright (c) 2006 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.
+#
+##############################################################################
+"""Local component registry export / import handler.
+
+$Id$
+"""
+
+from zope.app.component.hooks import getSite
+from zope.component import adapts
+from zope.component import getSiteManager
+from zope.component.interfaces import IComponentRegistry
+from zope.interface import implements
+
+from Acquisition import aq_base
+
+from interfaces import IBody
+from interfaces import ISetupEnviron
+from utils import XMLAdapterBase
+from utils import exportObjects
+from utils import importObjects
+from utils import _getDottedName
+from utils import _resolveDottedName
+
+class ComponentRegistryXMLAdapter(XMLAdapterBase):
+    """In- and exporter for a local component registry.
+    """
+    adapts(IComponentRegistry, ISetupEnviron)
+    implements(IBody)
+    name = 'componentregistry'
+
+    def _exportNode(self):
+        node=self._doc.createElement('componentregistry')
+        fragment = self._doc.createDocumentFragment()
+
+        child=self._doc.createElement('adapters')
+        child.appendChild(self._extractAdapters())
+        self._logger.info('Adapters exported.')
+        fragment.appendChild(child)
+
+        child=self._doc.createElement('utilities')
+        child.appendChild(self._extractUtilities())
+        self._logger.info('Utilities exported.')
+        fragment.appendChild(child)
+
+        node.appendChild(fragment)
+
+        return node
+
+    def _importNode(self, node):
+        if self.environ.shouldPurge():
+            self._purgeAdapters()
+            self._purgeUtilities()
+
+        for child in node.childNodes:
+            if child.nodeName == 'adapters':
+                self._initAdapters(child)
+                self._logger.info('Adapters registered.')
+            if child.nodeName == 'utilities':
+                self._initUtilities(child)
+                self._logger.info('Utilities registered.')
+
+    def _purgeAdapters(self):
+        registrations = self.context.registeredAdapters()
+        
+        for registration in registrations:
+            factory = registration.factory
+            required = registration.required
+            provided = registration.provided
+            name = registration.name
+
+            self.context.unregisterAdapter(factory=factory,
+                                           required=required,
+                                           provided=provided,
+                                           name=name)
+
+    def _purgeUtilities(self):
+        registrations = self.context.registeredAdapters()
+        
+        for registration in registrations:
+            provided = registration.provided
+            name = registration.name
+            self.context.unregisterUtility(provided=provided, name=name)
+
+    def _initAdapters(self, node):
+        for child in node.childNodes:
+            if child.nodeName != 'adapter':
+                continue
+
+            factory = _resolveDottedName(child.getAttribute('factory'))
+            provided = _resolveDottedName(child.getAttribute('provides'))
+            name = unicode(str(child.getAttribute('name')))
+
+            for_ = child.getAttribute('for_')
+            required = []
+            for interface in for_.split(u' '):
+                if interface:
+                    required.append(_resolveDottedName(interface))
+
+            self.context.registerAdapter(factory,
+                                         required=required,
+                                         provided=provided,
+                                         name=name)
+
+    def _initUtilities(self, node):
+        for child in node.childNodes:
+            if child.nodeName != 'utility':
+                continue
+
+            provided = _resolveDottedName(child.getAttribute('interface'))
+            name = unicode(str(child.getAttribute('name')))
+
+            component = child.getAttribute('component')
+            component = component and _resolveDottedName(component) or None
+
+            factory = child.getAttribute('factory')
+            factory = factory and _resolveDottedName(factory) or None
+
+            obj_path = child.getAttribute('object')
+            if obj_path:
+                site = getSite()
+                # we support registering aq_wrapped objects only for now
+                if hasattr(site, 'aq_base'):
+                    # filter out empty path segments
+                    path = [f for f in obj_path.split('/') if f]
+                    # XXX add support for nested folder
+                    if len(path) > 1:
+                        return
+                    obj = getattr(site, path[0], None)
+                    if obj is not None:
+                        self.context.registerUtility(obj, provided, name)
+                else:
+                    # Log an error, not aq_wrapped
+                    self._logger.warning("The object %s was not acquisition "
+                                         "wrapped. Registering these is not "
+                                         "supported right now." % obj)
+            elif component:
+                self.context.registerUtility(component, provided, name)
+            else:
+                self.context.registerUtility(factory(), provided, name)
+
+    def _extractAdapters(self):
+        fragment = self._doc.createDocumentFragment()
+
+        # We get back a generator but in order to have a consistent order
+        # which is needed for the tests we convert to a sorted list
+        registrations = [reg for reg in self.context.registeredAdapters()]
+        registrations.sort()
+        
+        for registration in registrations:
+            child=self._doc.createElement('adapter')
+
+            factory = _getDottedName(registration.factory)
+            provided = _getDottedName(registration.provided)
+            name = _getDottedName(registration.name)
+
+            for_ = u''
+            for interface in registration.required:
+                for_ = for_ + _getDottedName(interface) + u'\n           '
+
+            child.setAttribute('factory', factory)
+            child.setAttribute('provides', provided)
+            child.setAttribute('for_', for_.strip())
+            if name:
+                child.setAttribute('name', name)
+
+            fragment.appendChild(child)
+
+        return fragment
+
+    def _extractUtilities(self):
+        fragment = self._doc.createDocumentFragment()
+
+        # We get back a generator but in order to have a consistent order
+        # which is needed for the tests we convert to a sorted list
+        registrations = [reg for reg in self.context.registeredUtilities()]
+        registrations.sort()
+
+        for registration in registrations:
+            child=self._doc.createElement('utility')
+
+            provided = _getDottedName(registration.provided)
+            child.setAttribute('interface', provided)
+
+            name = _getDottedName(registration.name)
+            if name:
+                child.setAttribute('name', name)
+
+            comp = registration.component
+            # check if the component is acquisition wrapped. If it is export
+            # an object reference instead of a component / factory reference
+            if hasattr(comp, 'aq_base'):
+                # if the site is acquistion wrapped as well, get the relative
+                # path to the site
+                path = '/'.join(comp.getPhysicalPath())
+                site = getSite()
+                if hasattr(site, 'aq_base'):
+                    site_path = '/'.join(site.getPhysicalPath())
+                    rel_path = path[len(site_path):]
+                child.setAttribute('object', rel_path)
+            else:
+                factory = _getDottedName(type(comp))
+                reference = self.resolveName(comp)
+                if reference:
+                    module = _getDottedName(comp.__module__)
+                    component = "%s.%s" % (module, reference)
+                    child.setAttribute('component', component)
+                else:
+                    child.setAttribute('factory', factory)
+
+            fragment.appendChild(child)
+
+        return fragment
+
+    def resolveName(self, component):
+        # extremly ugly hack to get to the reference name, this only
+        # works if the utility reference is defined in the namespace of the
+        # same module as the class itself
+        module = _resolveDottedName(component.__module__)
+        namespace = module.__dict__
+        name = ''
+        for ref in namespace:
+            if namespace[ref] is component:
+                name = ref
+        del module
+        del namespace
+        return name
+
+def dummyGetId():
+    return ''
+
+def importComponentRegistry(context):
+    """Import local components.
+    """
+    sm = getSiteManager(context.getSite())
+    if sm is None or not IComponentRegistry.providedBy(sm):
+        logger = context.getLogger('componentregistry')
+        logger.info("Can not register components, as no registry was found.")
+        return
+    # XXX GenericSetup.utils.importObjects expects the object to have a getId
+    # function. We provide a dummy one for now, but this should be fixed in GS
+    # itself
+    sm.getId = dummyGetId
+    importObjects(sm, '', context)
+    del(sm.getId)
+
+def exportComponentRegistry(context):
+    """Export local components.
+    """
+    sm = getSiteManager(context.getSite())
+    if sm is None or not IComponentRegistry.providedBy(sm):
+        logger = context.getLogger('sitemanager')
+        logger.info("Nothing to export.")
+        return
+    # XXX GenericSetup.utils.exportObjects expects the object to have a getId
+    # function. We provide a dummy one for now, but this should be fixed in GS
+    # itself
+    sm.getId = dummyGetId
+    exportObjects(sm, '', context)
+    del(sm.getId)


Property changes on: GenericSetup/trunk/components.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Modified: GenericSetup/trunk/configure.zcml
===================================================================
--- GenericSetup/trunk/configure.zcml	2006-11-19 12:03:52 UTC (rev 71182)
+++ GenericSetup/trunk/configure.zcml	2006-11-19 13:17:07 UTC (rev 71183)
@@ -52,4 +52,8 @@
       for="Products.GenericSetup.interfaces.IDAVAware"
       />
 
+  <adapter
+      factory=".components.ComponentRegistryXMLAdapter"
+      />
+
 </configure>

Added: GenericSetup/trunk/tests/test_components.py
===================================================================
--- GenericSetup/trunk/tests/test_components.py	2006-11-19 12:03:52 UTC (rev 71182)
+++ GenericSetup/trunk/tests/test_components.py	2006-11-19 13:17:07 UTC (rev 71183)
@@ -0,0 +1,195 @@
+##############################################################################
+#
+# Copyright (c) 2006 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.
+#
+##############################################################################
+"""Component registry export / import support unit tests.
+
+$Id$
+"""
+
+import unittest
+from Testing.ZopeTestCase import ZopeTestCase
+
+from AccessControl import ClassSecurityInfo
+from Acquisition import aq_base
+from Acquisition import aq_get
+from Globals import InitializeClass
+from OFS.Folder import Folder
+from OFS.SimpleItem import SimpleItem
+
+from Products.Five import zcml
+from Products.Five.component import enableSite
+from Products.Five.component.interfaces import IObjectManagerSite
+from Products.GenericSetup.testing import BodyAdapterTestCase
+
+from zope.app.component.hooks import setSite, clearSite, setHooks
+from zope.component import getSiteManager
+from zope.component import queryUtility
+from zope.component.globalregistry import base
+from zope.component.persistentregistry import PersistentComponents
+from zope.interface import implements
+from zope.interface import Interface
+
+_marker = []
+
+def createComponentRegistry(context):
+    enableSite(context, iface=IObjectManagerSite)
+
+    components = PersistentComponents()
+    components.__bases__ = (base,)
+    context.setSiteManager(components)
+
+class IDummyInterface(Interface):
+    """A dummy interface."""
+
+    def verify():
+        """Returns True."""
+
+class IDummyInterface2(Interface):
+    """A second dummy interface."""
+
+    def verify():
+        """Returns True."""
+
+class DummyUtility(object):
+    """A dummy utility."""
+
+    implements(IDummyInterface)
+
+    def verify(self):
+        return True
+
+class DummyUtility2(object):
+    """A second dummy utility."""
+
+    implements(IDummyInterface2)
+
+    def verify(self):
+        return True
+
+dummy2 = DummyUtility2()
+
+class DummyTool(SimpleItem):
+    """A dummy tool."""
+    implements(IDummyInterface)
+
+    id = 'dummy_tool'
+    meta_type = 'dummy tool'
+    security = ClassSecurityInfo()
+
+    security.declarePublic('verify')
+    def verify(self):
+        return True
+
+InitializeClass(DummyTool)
+
+_COMPONENTS_BODY = """\
+<?xml version="1.0"?>
+<componentregistry>
+ <adapters/>
+ <utilities>
+  <utility factory="Products.GenericSetup.tests.test_components.DummyUtility"
+     interface="Products.GenericSetup.tests.test_components.IDummyInterface"/>
+  <utility name="dummy tool name"
+     interface="Products.GenericSetup.tests.test_components.IDummyInterface"
+     object="/dummy_tool"/>
+  <utility name="dummy tool name2"
+     interface="Products.GenericSetup.tests.test_components.IDummyInterface"
+     object="/test_folder_1_/dummy_tool"/>
+  <utility name="foo"
+     factory="Products.GenericSetup.tests.test_components.DummyUtility"
+     interface="Products.GenericSetup.tests.test_components.IDummyInterface"/>
+  <utility component="Products.GenericSetup.tests.test_components.dummy2"
+     interface="Products.GenericSetup.tests.test_components.IDummyInterface2"/>
+ </utilities>
+</componentregistry>
+"""
+
+class ComponentRegistryXMLAdapterTests(ZopeTestCase, BodyAdapterTestCase):
+
+    def _getTargetClass(self):
+        from Products.GenericSetup.components import \
+            ComponentRegistryXMLAdapter
+        return ComponentRegistryXMLAdapter
+
+    def _verifyImport(self, obj):
+        util = queryUtility(IDummyInterface, name=u'foo')
+        self.failUnless(IDummyInterface.providedBy(util))
+        self.failUnless(util.verify())
+
+        util = queryUtility(IDummyInterface)
+        self.failUnless(IDummyInterface.providedBy(util))
+        self.failUnless(util.verify())
+
+        util = queryUtility(IDummyInterface, name='dummy tool name')
+        self.failUnless(IDummyInterface.providedBy(util))
+        self.failUnless(util.verify())
+        self.assertEqual(util.meta_type, 'dummy tool')
+
+        # make sure we can get the tool by normal means
+        tool = getattr(self.app, 'dummy_tool')
+        self.assertEqual(tool.meta_type, 'dummy tool')
+        self.assertEquals(repr(util), repr(tool))
+
+        util = queryUtility(IDummyInterface2)
+        self.failUnless(IDummyInterface2.providedBy(util))
+        self.failUnless(util.verify())
+
+        util = queryUtility(IDummyInterface, name='dummy tool name2')
+        self.failUnless(IDummyInterface.providedBy(util))
+        self.failUnless(util.verify())
+        self.assertEqual(util.meta_type, 'dummy tool')
+
+        # make sure we can get the tool by normal means
+        tool = getattr(self.app.test_folder_1_, 'dummy_tool2')
+        self.assertEqual(tool.meta_type, 'dummy tool')
+        self.assertEquals(repr(util), repr(tool))
+
+    def afterSetUp(self):
+        import Products.GenericSetup
+        
+        BodyAdapterTestCase.setUp(self)
+        zcml.load_config('configure.zcml', Products.GenericSetup)
+
+        # Create and enable a local component registry
+        createComponentRegistry(self.app)
+        setHooks()
+        setSite(self.app)
+        sm = getSiteManager()
+
+        sm.registerUtility(DummyUtility(), IDummyInterface)
+        sm.registerUtility(DummyUtility(), IDummyInterface, name=u'foo')
+        sm.registerUtility(dummy2, IDummyInterface2)
+
+        tool = DummyTool()
+        self.app._setObject(tool.id, tool)
+        obj = self.app[tool.id]
+        sm.registerUtility(obj, IDummyInterface, name=u'dummy tool name')
+
+        folder = self.app.test_folder_1_
+        tool2 = DummyTool()
+        folder._setObject('dummy_tool2', tool2)
+        obj = folder['dummy_tool2']
+        sm.registerUtility(obj, IDummyInterface, name=u'dummy tool name2')
+
+        self._obj = sm
+        self._BODY = _COMPONENTS_BODY
+
+    def beforeTearDown(self):
+        clearSite()
+
+def test_suite():
+    return unittest.TestSuite((
+        unittest.makeSuite(ComponentRegistryXMLAdapterTests),
+        ))
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')


Property changes on: GenericSetup/trunk/tests/test_components.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native



More information about the Checkins mailing list