[Checkins] SVN: GenericSetup/trunk/ Merge the wichert-zcml-steps
branch
Wichert Akkerman
wichert at wiggy.net
Sun Nov 4 09:34:45 EST 2007
Log message for revision 81471:
Merge the wichert-zcml-steps branch
Changed:
U GenericSetup/trunk/CHANGES.txt
U GenericSetup/trunk/meta.zcml
U GenericSetup/trunk/registry.py
A GenericSetup/trunk/tests/test_stepzcml.py
U GenericSetup/trunk/tests/test_zcml.py
U GenericSetup/trunk/tool.py
U GenericSetup/trunk/utils.py
U GenericSetup/trunk/www/sutExportSteps.zpt
U GenericSetup/trunk/www/sutImportSteps.zpt
U GenericSetup/trunk/zcml.py
-=-
Modified: GenericSetup/trunk/CHANGES.txt
===================================================================
--- GenericSetup/trunk/CHANGES.txt 2007-11-04 07:07:19 UTC (rev 81470)
+++ GenericSetup/trunk/CHANGES.txt 2007-11-04 14:34:45 UTC (rev 81471)
@@ -1,6 +1,8 @@
GenericSetup Product Changelog
- GenericSetup 1.3.3 (unreleased)
+ GenericSetup 1.4 (unreleased)
+
+ - Use zcml to register import and export steps.
- tool: Fixed toolset import handler not to initialize tools again, when
they already exist in the site.
Modified: GenericSetup/trunk/meta.zcml
===================================================================
--- GenericSetup/trunk/meta.zcml 2007-11-04 07:07:19 UTC (rev 81470)
+++ GenericSetup/trunk/meta.zcml 2007-11-04 14:34:45 UTC (rev 81471)
@@ -11,6 +11,24 @@
/>
<meta:directive
+ name="exportStep"
+ schema=".zcml.IExportStepDirective"
+ handler=".zcml.exportStep"
+ />
+ <meta:complexDirective
+ name="importStep"
+ schema=".zcml.IImportStepDirective"
+ handler=".zcml.importStep"
+ >
+
+ <meta:subdirective
+ name="depends"
+ schema=".zcml.IImportStepDependsDirective"
+ />
+
+ </meta:complexDirective>
+
+ <meta:directive
name="upgradeStep"
schema=".zcml.IUpgradeStepDirective"
handler=".zcml.upgradeStep"
Modified: GenericSetup/trunk/registry.py
===================================================================
--- GenericSetup/trunk/registry.py 2007-11-04 07:07:19 UTC (rev 81470)
+++ GenericSetup/trunk/registry.py 2007-11-04 14:34:45 UTC (rev 81471)
@@ -36,6 +36,7 @@
from utils import _getDottedName
from utils import _resolveDottedName
from utils import _extractDocstring
+from utils import _computeTopologicalSort
class BaseStepRegistry( Implicit ):
@@ -109,6 +110,10 @@
return _resolveDottedName( info[ 'handler' ] )
+ security.declarePrivate( 'unregisterStep' )
+ def unregisterStep( self, id ):
+ del self._registered[id]
+
security.declarePrivate( 'clear' )
def clear( self ):
@@ -249,50 +254,15 @@
#
security.declarePrivate( '_computeTopologicalSort' )
def _computeTopologicalSort( self ):
+ return _computeTopologicalSort(self._registered.values())
- result = []
- graph = [ ( x[ 'id' ], x[ 'dependencies' ] )
- for x in self._registered.values() ]
-
- unresolved = []
-
- while 1:
- for node, edges in graph:
-
- after = -1
- resolved = 0
-
- for edge in edges:
-
- if edge in result:
- resolved += 1
- after = max( after, result.index( edge ) )
-
- if len(edges) > resolved:
- unresolved.append((node, edges))
- else:
- result.insert( after + 1, node )
-
- if not unresolved:
- break
- if len(unresolved) == len(graph):
- # Nothing was resolved in this loop. There must be circular or
- # missing dependencies. Just add them to the end. We can't
- # raise an error, because checkComplete relies on this method.
- for node, edges in unresolved:
- result.append(node)
- break
- graph = unresolved
- unresolved = []
-
- return result
-
security.declarePrivate( '_exportTemplate' )
_exportTemplate = PageTemplateFile( 'isrExport.xml', _xmldir )
InitializeClass( ImportStepRegistry )
+_import_step_registry = ImportStepRegistry()
class ExportStepRegistry( BaseStepRegistry ):
@@ -379,6 +349,9 @@
InitializeClass( ExportStepRegistry )
+_export_step_registry = ExportStepRegistry()
+
+
class ToolsetRegistry( Implicit ):
""" Track required / forbidden tools.
Copied: GenericSetup/trunk/tests/test_stepzcml.py (from rev 81470, GenericSetup/branches/wichert-zcml-steps/tests/test_stepzcml.py)
===================================================================
--- GenericSetup/trunk/tests/test_stepzcml.py (rev 0)
+++ GenericSetup/trunk/tests/test_stepzcml.py 2007-11-04 14:34:45 UTC (rev 81471)
@@ -0,0 +1,72 @@
+##############################################################################
+#
+# Copyright (c) 2007 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.
+#
+##############################################################################
+"""Unit tests for import/export step zcml module.
+"""
+import unittest
+from Products.GenericSetup.zcml import cleanUpImportSteps
+import Products.GenericSetup
+from Products.GenericSetup.registry import _import_step_registry
+from Products.GenericSetup.testing import ExportImportZCMLLayer
+from Products.Five import zcml
+
+EMPTY_ZCML = '''<configure xmlns:genericsetup="http://namespaces.zope.org/genericsetup">
+</configure>'''
+
+ONE_STEP_ZCML = '''<configure xmlns:genericsetup="http://namespaces.zope.org/genericsetup" i18n_domain="genericsetup">
+<genericsetup:importStep
+ name="Products.GenericSetup.teststep"
+ title="step title"
+ description="step description"
+ handler="Products.GenericSetup.initialize"
+ version="1.0"
+ />
+</configure>'''
+
+class ImportStepTests(unittest.TestCase):
+ layer = ExportImportZCMLLayer
+
+ def setUp(self):
+ zcml.load_config('meta.zcml', Products.GenericSetup)
+
+ def tearDown(self):
+ cleanUpImportSteps()
+
+ def testEmptyImport(self):
+ zcml.load_string(EMPTY_ZCML)
+ self.assertEqual(_import_step_registry._registered, {})
+
+ def testOneStepImport(self):
+ zcml.load_string(ONE_STEP_ZCML)
+ self.assertEqual(_import_step_registry._registered.keys(),
+ [ u'Products.GenericSetup.teststep' ])
+ info = _import_step_registry._registered[ u'Products.GenericSetup.teststep' ]
+ self.assertEqual( info['description'],
+ u'step description' )
+ self.assertEqual( info['title'],
+ u'step title' )
+ self.assertEqual( info['handler'],
+ 'Products.GenericSetup.initialize')
+ self.assertEqual( info['version'],
+ u'1.0' )
+ self.assertEqual( info['id'],
+ u'Products.GenericSetup.teststep' )
+
+
+def test_suite():
+ return unittest.TestSuite((
+ unittest.makeSuite(ImportStepTests),
+ ))
+
+if __name__ == '__main__':
+ from Products.GenericSetup.testing import run
+ run(test_suite())
Modified: GenericSetup/trunk/tests/test_zcml.py
===================================================================
--- GenericSetup/trunk/tests/test_zcml.py 2007-11-04 07:07:19 UTC (rev 81470)
+++ GenericSetup/trunk/tests/test_zcml.py 2007-11-04 14:34:45 UTC (rev 81471)
@@ -1,6 +1,6 @@
##############################################################################
#
-# Copyright (c) 2006 Zope Corporation and Contributors. All Rights Reserved.
+# Copyright (c) 2006-2007 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.
@@ -20,6 +20,19 @@
from zope.testing import doctest
from zope.testing.doctest import ELLIPSIS
+from Products.GenericSetup.testing import ExportImportZCMLLayer
+from Products.GenericSetup.zcml import cleanUpImportSteps
+from Products.GenericSetup.zcml import cleanUpExportSteps
+from Products.GenericSetup.registry import _import_step_registry
+from Products.GenericSetup.registry import _export_step_registry
+from Products.Five import zcml
+
+def dummy_importstep_handler(context):
+ pass
+
+def dummy_exportstep_handler(context):
+ pass
+
def dummy_upgrade_handler(context):
pass
@@ -230,10 +243,93 @@
>>> cleanUp()
"""
+
+class ImportStepTests(unittest.TestCase):
+ layer = ExportImportZCMLLayer
+
+ def tearDown(self):
+ cleanUpImportSteps()
+
+ def testNoDependencies(self):
+ zcml.load_string("""<configure
+ xmlns:genericsetup="http://namespaces.zope.org/genericsetup"
+ i18n_domain="genericsetup">
+ <genericsetup:importStep
+ name="name"
+ title="title"
+ description="description"
+ handler="Products.GenericSetup.tests.test_zcml.dummy_importstep_handler"
+ version="version">
+ </genericsetup:importStep>
+ </configure>""")
+ from Products.GenericSetup.zcml import _import_step_regs
+ self.assertEqual(_import_step_regs, [u'name'])
+ self.assertEqual( _import_step_registry.listSteps(), [u'name'])
+ data=_import_step_registry.getStepMetadata(u'name')
+ self.assertEqual(data["handler"],
+ 'Products.GenericSetup.tests.test_zcml.dummy_importstep_handler')
+ self.assertEqual(data["description"], u"description")
+ self.assertEqual(data["version"], u"version")
+ self.assertEqual(data["title"], u"title")
+ self.assertEqual(data["dependencies"], ())
+ self.assertEqual(data["id"], u"name")
+
+
+ def testWithDependency(self):
+ zcml.load_string("""<configure
+ xmlns:genericsetup="http://namespaces.zope.org/genericsetup"
+ i18n_domain="genericsetup">
+ <genericsetup:importStep
+ name="name"
+ title="title"
+ description="description"
+ handler="Products.GenericSetup.tests.test_zcml.dummy_importstep_handler"
+ version="version">
+ <depends name="something.else"/>
+ </genericsetup:importStep>
+ </configure>""")
+ data=_import_step_registry.getStepMetadata(u'name')
+ self.assertEqual(data["dependencies"], (u"something.else",))
+
+
+
+class ExportStepTests(unittest.TestCase):
+ layer = ExportImportZCMLLayer
+
+ def tearDown(self):
+ cleanUpExportSteps()
+
+ def testRegistration(self):
+ zcml.load_string("""<configure
+ xmlns:genericsetup="http://namespaces.zope.org/genericsetup"
+ i18n_domain="genericsetup">
+ <genericsetup:exportStep
+ name="name"
+ title="title"
+ description="description"
+ handler="Products.GenericSetup.tests.test_zcml.dummy_exportstep_handler"
+ />
+ </configure>
+ """)
+ from Products.GenericSetup.zcml import _export_step_regs
+ self.assertEqual(_export_step_regs, [u'name'])
+ self.assertEqual( _export_step_registry.listSteps(), [u'name'])
+ data=_export_step_registry.getStepMetadata(u'name')
+ self.assertEqual(data["handler"],
+ 'Products.GenericSetup.tests.test_zcml.dummy_exportstep_handler')
+ self.assertEqual(data["description"], u"description")
+ self.assertEqual(data["title"], u"title")
+ self.assertEqual(data["id"], u"name")
+
+
def test_suite():
- return unittest.TestSuite((
- doctest.DocTestSuite(optionflags=ELLIPSIS),
- ))
+ suite = unittest.TestSuite()
+ suite.addTest(doctest.DocTestSuite(optionflags=ELLIPSIS))
+ suite.addTest(unittest.makeSuite(ImportStepTests))
+ suite.addTest(unittest.makeSuite(ExportStepTests))
+ return suite
+ return suite
+
if __name__ == '__main__':
unittest.main(defaultTest='test_suite')
Modified: GenericSetup/trunk/tool.py
===================================================================
--- GenericSetup/trunk/tool.py 2007-11-04 07:07:19 UTC (rev 81470)
+++ GenericSetup/trunk/tool.py 2007-11-04 14:34:45 UTC (rev 81471)
@@ -46,6 +46,8 @@
from registry import ExportStepRegistry
from registry import ToolsetRegistry
from registry import _profile_registry
+from registry import _import_step_registry
+from registry import _export_step_registry
from upgrade import listUpgradeSteps
from upgrade import listProfilesWithUpgrades
@@ -54,6 +56,7 @@
from utils import _getDottedName
from utils import _resolveDottedName
from utils import _wwwdir
+from utils import _computeTopologicalSort
IMPORT_STEPS_XML = 'import_steps.xml'
EXPORT_STEPS_XML = 'export_steps.xml'
@@ -250,6 +253,47 @@
"""
return self._export_registry
+
+ security.declareProtected(ManagePortal, 'getExportStep')
+ def getExportStep(self, step, default=None):
+ """Simple wrapper to query both the global and local step registry."""
+ res=_export_step_registry.getStep(step, default)
+ if res is not default:
+ return res
+ return self._export_registry.getStep(step, default)
+
+
+ security.declareProtected(ManagePortal, 'listExportSteps')
+ def listExportSteps(self):
+ steps = _export_step_registry._registered.keys() + \
+ self._export_registry._registered.keys()
+ return steps
+
+
+ security.declareProtected(ManagePortal, 'getImportStep')
+ def getImportStep(self, step, default=None):
+ """Simple wrapper to query both the global and local step registry."""
+ res=_import_step_registry.getStep(step, default)
+ if res is not default:
+ return res
+ return self._import_registry.getStep(step, default)
+
+
+ security.declareProtected(ManagePortal, 'getSortedImportSteps')
+ def getSortedImportSteps(self):
+ steps = _import_step_registry._registered.values() + \
+ self._import_registry._registered.values()
+ return _computeTopologicalSort(steps)
+
+ security.declareProtected(ManagePortal, 'getImportStep')
+ def getImportStepMetadata(self, step, default=None):
+ """Simple wrapper to query both the global and local step registry."""
+ res=_import_step_registry.getStepMetadata(step, default)
+ if res is not default:
+ return res
+ return self._import_registry.getStepMetadata(step, default)
+
+
security.declareProtected(ManagePortal, 'getToolsetRegistry')
def getToolsetRegistry(self):
@@ -267,7 +311,7 @@
self.applyContext(context)
- info = self._import_registry.getStepMetadata(step_id)
+ info = self.getImportStepMetadata(step_id)
if info is None:
self._import_context_id = old_context
@@ -352,7 +396,7 @@
""" See ISetupTool.
"""
- return self._doRunExportSteps(self._export_registry.listSteps())
+ return self._doRunExportSteps(self.listExportSteps())
security.declareProtected(ManagePortal, 'createSnapshot')
def createSnapshot(self, snapshot_id):
@@ -361,7 +405,7 @@
"""
context = SnapshotExportContext(self, snapshot_id)
messages = {}
- steps = self._export_registry.listSteps()
+ steps = self.listExportSteps()
for step_id in steps:
@@ -971,7 +1015,7 @@
__traceback_info__ = step_id
marker = object()
- handler = self._import_registry.getStep(step_id)
+ handler = self.getImportStep(step_id)
if handler is marker:
raise ValueError('Invalid import step: %s' % step_id)
@@ -1019,7 +1063,7 @@
self.applyContext(context)
if steps is None:
- steps = self._import_registry.sortSteps()
+ steps = self.getSortedImportSteps()
messages = {}
for step in steps:
Modified: GenericSetup/trunk/utils.py
===================================================================
--- GenericSetup/trunk/utils.py 2007-11-04 07:07:19 UTC (rev 81470)
+++ GenericSetup/trunk/utils.py 2007-11-04 14:34:45 UTC (rev 81471)
@@ -788,3 +788,41 @@
if getattr(obj, 'objectValues', False):
for sub in obj.objectValues():
importObjects(sub, path+'/', context)
+
+
+def _computeTopologicalSort( steps ):
+ result = []
+ graph = [ ( x[ 'id' ], x[ 'dependencies' ] ) for x in steps ]
+
+ unresolved = []
+
+ while 1:
+ for node, edges in graph:
+
+ after = -1
+ resolved = 0
+
+ for edge in edges:
+
+ if edge in result:
+ resolved += 1
+ after = max( after, result.index( edge ) )
+
+ if len(edges) > resolved:
+ unresolved.append((node, edges))
+ else:
+ result.insert( after + 1, node )
+
+ if not unresolved:
+ break
+ if len(unresolved) == len(graph):
+ # Nothing was resolved in this loop. There must be circular or
+ # missing dependencies. Just add them to the end. We can't
+ # raise an error, because checkComplete relies on this method.
+ for node, edges in unresolved:
+ result.append(node)
+ break
+ graph = unresolved
+ unresolved = []
+
+ return result
Modified: GenericSetup/trunk/www/sutExportSteps.zpt
===================================================================
--- GenericSetup/trunk/www/sutExportSteps.zpt 2007-11-04 07:07:19 UTC (rev 81470)
+++ GenericSetup/trunk/www/sutExportSteps.zpt 2007-11-04 14:34:45 UTC (rev 81471)
@@ -25,12 +25,11 @@
</tr>
</thead>
- <tbody tal:define="registry here/getExportStepRegistry;
- step_ids registry/listSteps;
+ <tbody tal:define="step_ids here/listExportSteps;
">
<tal:loop tal:repeat="step_id step_ids">
<tr valign="top"
- tal:define="info python: registry.getStepMetadata( step_id );"
+ tal:define="info python: here.getExportStepMetadata( step_id );"
tal:attributes="class python:
repeat[ 'step_id' ].odd and 'row-normal' or 'row-hilite'" >
<td class="list-item" width="16">
Modified: GenericSetup/trunk/www/sutImportSteps.zpt
===================================================================
--- GenericSetup/trunk/www/sutImportSteps.zpt 2007-11-04 07:07:19 UTC (rev 81470)
+++ GenericSetup/trunk/www/sutImportSteps.zpt 2007-11-04 14:34:45 UTC (rev 81471)
@@ -55,12 +55,11 @@
</tr>
</thead>
- <tbody tal:define="registry context/getImportStepRegistry;
- step_ids registry/sortSteps;
+ <tbody tal:define="step_ids context/getSortedImportSteps;
">
<tal:loop tal:repeat="step_id step_ids">
<tr valign="top"
- tal:define="info python: registry.getStepMetadata( step_id );"
+ tal:define="info python: context.getImportStepMetadata( step_id );"
tal:attributes="class python:
repeat[ 'step_id' ].odd and 'row-normal' or 'row-hilite';
style python:info['invalid'] and 'background: red' or None" >
Modified: GenericSetup/trunk/zcml.py
===================================================================
--- GenericSetup/trunk/zcml.py 2007-11-04 07:07:19 UTC (rev 81470)
+++ GenericSetup/trunk/zcml.py 2007-11-04 14:34:45 UTC (rev 81471)
@@ -1,6 +1,6 @@
-##############################################################################
+
#
-# Copyright (c) 2006 Zope Corporation and Contributors. All Rights Reserved.
+# Copyright (c) 2006-2007 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.
@@ -22,6 +22,8 @@
from zope.interface import Interface
from interfaces import BASE
+from registry import _import_step_registry
+from registry import _export_step_registry
from registry import _profile_registry
from upgrade import _upgrade_registry
@@ -83,6 +85,110 @@
)
+#### genericsetup:exportStep
+
+class IExportStepDirective(Interface):
+ name = PythonIdentifier(
+ title=u'Name',
+ description=u'',
+ required=True)
+
+ title = MessageID(
+ title=u'Title',
+ description=u'',
+ required=True)
+
+ description = MessageID(
+ title=u'Description',
+ description=u'',
+ required=True)
+
+ handler = GlobalObject(
+ title=u'Handler',
+ description=u'',
+ required=True)
+
+
+_export_step_regs = []
+
+def exportStep(context, name, handler, title=None, description=None):
+ global _export_step_regs
+ _export_step_regs.append(name)
+
+ context.action(
+ discriminator = ('exportStep', name),
+ callable = _export_step_registry.registerStep,
+ args = (name, handler, title, description),
+ )
+#### genericsetup:importStep
+
+class IImportStepDirective(Interface):
+
+ """Register import steps with the global registry.
+ """
+
+ name = PythonIdentifier(
+ title=u'Name',
+ description=u'',
+ required=True)
+
+ title = MessageID(
+ title=u'Title',
+ description=u'',
+ required=True)
+
+ description = MessageID(
+ title=u'Description',
+ description=u'',
+ required=True)
+
+ handler = GlobalObject(
+ title=u'Handler',
+ description=u'',
+ required=True)
+
+ version = MessageID(
+ title=u'Version',
+ description=u'',
+ required=True)
+
+class IImportStepDependsDirective(Interface):
+ name = PythonIdentifier(
+ title=u'Name',
+ description=u'Name of another import step that has to be run first',
+ required=True)
+
+_import_step_regs = []
+
+class importStep:
+ def __init__(self, context, name, version, title, description, handler):
+ """ Add a new import step to the registry.
+ """
+ self.context=context
+ self.discriminator = ('importStep', name),
+ self.name=name
+ self.version=version
+ self.handler=handler
+ self.title=title
+ self.description=description
+ self.dependencies=()
+
+
+ def depends(self, context, name):
+ self.dependencies+=(name,)
+
+
+ def __call__(self):
+ global _import_step_regs
+ _import_step_regs.append(self.name)
+
+ self.context.action(
+ discriminator = self.discriminator,
+ callable = _import_step_registry.registerStep,
+ args = (self.name, self.version, self.handler, self.dependencies,
+ self.title, self.description),
+ )
+
#### genericsetup:upgradeStep
import zope.schema
@@ -176,7 +282,7 @@
#### cleanup
-def cleanUp():
+def cleanUpProfiles():
global _profile_regs
for profile_id in _profile_regs:
del _profile_registry._profile_info[profile_id]
@@ -186,6 +292,28 @@
_upgrade_registry.clear()
+def cleanUpImportSteps():
+ global _import_step_regs
+ for name in _import_step_regs:
+ try:
+ _import_step_registry.unregisterStep( name )
+ except KeyError:
+ pass
+
+ _import_step_regs=[]
+
+def cleanUpExportSteps():
+ global _export_step_regs
+ for name in _export_step_regs:
+ try:
+ _export_step_registry.unregisterStep( name )
+ except KeyError:
+ pass
+
+ _export_step_regs=[]
+
from zope.testing.cleanup import addCleanUp
-addCleanUp(cleanUp)
+addCleanUp(cleanUpProfiles)
+addCleanUp(cleanUpImportSteps)
+addCleanUp(cleanUpExportSteps)
del addCleanUp
More information about the Checkins
mailing list