[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