[CMF-checkins] CVS: CMF/CMFSetup - tool.py:1.1 interfaces.py:1.3
registry.py:1.8
Tres Seaver
tseaver at zope.com
Sun May 23 14:58:09 EDT 2004
Update of /cvs-repository/CMF/CMFSetup
In directory cvs.zope.org:/tmp/cvs-serv26424
Modified Files:
interfaces.py registry.py
Added Files:
tool.py
Log Message:
o interfaces.py:
- Centralize interface declarations.
- Remove notion of 'profile', for the nonce.
- Declare interfaces for registries.
- Declare interface for tool.
o registry.py:
- Rationalize names ( s/SetupStep/ImportStep/, s/ExportScript/ExportStep/).
- Add interface assertions, and test.
- Document common pseudo-interfaces for callables.
o tool.py:
- Skeleton implementation of tool interface.
=== Added File CMF/CMFSetup/tool.py ===
""" Classes: SetupTool
$Id: tool.py,v 1.1 2004/05/23 18:57:38 tseaver Exp $
"""
from AccessControl import ClassSecurityInfo
from Globals import InitializeClass
from OFS.Folder import Folder
from Products.CMFCore.utils import UniqueObject
from interfaces import ISetupTool
from permissions import ManagePortal
class SetupTool( UniqueObject, Folder ):
""" Profile-based site configuration manager.
"""
__implements__ = ( ISetupTool, ) + Folder.__implements__
id = 'portal_setup'
meta_type = 'Portal Setup Tool'
security = ClassSecurityInfo()
security.declareProtected( ManagePortal, 'executeStep' )
def runSetupStep( self, step_id, run_dependencies=True, purge_old=True ):
""" See ISetupTool.
"""
security.declareProtected( ManagePortal, 'runAllSetupSteps')
def runAllSetupSteps( self, purge_old=True ):
""" See ISetupTool.
"""
security.declareProtected( ManagePortal, 'runExportStep')
def runExportStep( self, step_id ):
""" See ISetupTool.
"""
security.declareProtected(ManagePortal, 'runAllExportSteps')
def runAllExportSteps( self ):
""" See ISetupTool.
"""
security.declareProtected( ManagePortal, 'createSnapshot')
def createSnapshot( self, snapshot_id ):
""" See ISetupTool.
"""
security.declareProtected(ManagePortal, 'compareConfigurations')
def compareConfigurations( self
, source1
, source2
, missing_as_empty=False
, ignore_whitespace=False
):
""" See ISetupTool.
"""
security.declareProtected( ManagePortal, 'markupComparison')
def markupComparison(self, lines):
""" See ISetupTool.
"""
InitializeClass( SetupTool )
=== CMF/CMFSetup/interfaces.py 1.2 => 1.3 ===
--- CMF/CMFSetup/interfaces.py:1.2 Fri May 21 17:12:39 2004
+++ CMF/CMFSetup/interfaces.py Sun May 23 14:57:38 2004
@@ -5,6 +5,11 @@
from Interface import Interface
+class IPseudoInterface( Interface ):
+
+ """ API documentation; not testable / enforceable.
+ """
+
class ISetupContext( Interface ):
""" Context used for export / import plugins.
@@ -20,12 +25,10 @@
""" Search the current configuration for the requested file.
- o Search each profile in the configuration in order.
-
o 'filename' is the name (without path elements) of the file.
o 'subdir' is an optional subdirectory; if not supplied, search
- only the "root" directory of each configured profile.
+ only the "root" directory.
o Return the file contents as a string, or None if the
file cannot be found.
@@ -37,24 +40,24 @@
o Search profiles in the configuration in order.
- o If the profile is filesystem based, return the 'stat' timestamp
+ o If the context is filesystem based, return the 'stat' timestamp
of the file / directory to which 'path' points.
- o If the profile is ZODB-based, return the Zope modification time
+ o If the context is ZODB-based, return the Zope modification time
of the object to which 'path' points.
- o Return None if 'path' does not point to any object in any profile.
+ o Return None if 'path' does not point to any object.
"""
def isDirectory( path ):
- """ Test whether path points to a directory / folder in a profile
+ """ Test whether path points to a directory / folder.
- o If the profile is filesystem based, check that 'path' points to
- a subdirectory within the "root" directory of the profile.
+ o If the context is filesystem based, check that 'path' points to
+ a subdirectory within the "root" directory.
- o If the profile is ZODB-based, check that 'path' points to a
- "container" under the profile.
+ o If the context is ZODB-based, check that 'path' points to a
+ "container" under the context's tool.
o Return None if 'path' does not resolve; otherwise, return a
bool.
@@ -62,7 +65,7 @@
def listDirectory( path, skip=('CVS',) ):
- """ List IDs of the contents of a profile directory / folder.
+ """ List IDs of the contents of a directory / folder.
o Omit names in 'skip'.
@@ -74,6 +77,19 @@
""" When installing, should the existing setup be purged?
"""
+class IImportPlugin( IPseudoInterface ):
+
+ """ Signature for callables used to import portions of site configuration.
+ """
+ def __call__( context ):
+
+ """ Perform the setup step.
+
+ o Return a message describing the work done.
+
+ o 'context' must implement IImportContext.
+ """
+
class IExportContext( ISetupContext ):
def writeDataFile( filename, text, content_type, subdir=None ):
@@ -91,31 +107,210 @@
"root" of the target.
"""
+class IExportPlugin( IPseudoInterface ):
-class IPseudoInterface( Interface ):
+ """ Signature for callables used to export portions of site configuration.
+ """
+ def __call__( context ):
- """ API documentation; not testable / enforceable.
+ """ Write export data for the site wrapped by context.
+
+ o Return a message describing the work done.
+
+ o 'context' must implement IExportContext. The plugin will use
+ its 'writeDataFile' method for each file to be exported.
+ """
+
+class IStepRegistry( Interface ):
+
+ """ Base interface for step registries.
"""
+ def listSteps():
+ """ Return a sequence of IDs of registered steps.
-class IImportPlugin( IPseudoInterface ):
+ o Order is not significant.
+ """
+
+ def listStepMetadata():
+
+ """ Return a sequence of mappings describing registered steps.
+
+ o Mappings will be ordered alphabetically.
+ """
+
+ def getStepMetadata( key, default=None ):
+
+ """ Return a mapping of metadata for the step identified by 'key'.
+
+ o Return 'default' if no such step is registered.
- """ API for initializing / configuring a site from a profile.
+ o The 'handler' metadata is available via 'getStep'.
+ """
+
+ def exportAsXML():
+
+ """ Return a round-trippable XML representation of the registry.
+
+ o 'handler' values are serialized using their dotted names.
+ """
+
+ def importFromXML( text ):
- o 'context' must implement IImportContext.
+ """ Parse 'text' into a clean registry.
+ """
+
+class IImportStepRegistry( IStepRegistry ):
+
+ """ API for import step registry.
"""
- def __call__( context ):
+ def sortSteps():
- """ Use data from 'context' to do initialization / configuration.
+ """ Return a sequence of registered step IDs
+
+ o Sequence is sorted topologically by dependency, with the dependent
+ steps *after* the steps they depend on.
"""
-class IExportPlugin( IPseudoInterface ):
+ def checkComplete():
- """ API for exporting a site to a serialization.
+ """ Return a sequence of ( node, edge ) tuples for unsatisifed deps.
+ """
+
+ def getStep( key, default=None ):
+
+ """ Return the IImportPlugin registered for 'key'.
+
+ o Return 'default' if no such step is registered.
+ """
+
+ def registerStep( id
+ , version
+ , handler
+ , dependencies=()
+ , title=None
+ , description=None
+ ):
+ """ Register a setup step.
+
+ o 'id' is a unique name for this step,
+
+ o 'version' is a string for comparing versions, it is preferred to
+ be a yyyy/mm/dd-ii formatted string (date plus two-digit
+ ordinal). when comparing two version strings, the version with
+ the lower sort order is considered the older version.
+
+ - Newer versions of a step supplant older ones.
+
+ - Attempting to register an older one after a newer one results
+ in a KeyError.
+
+ o 'handler' should implement IImportPlugin.
+
+ o 'dependencies' is a tuple of step ids which have to run before
+ this step in order to be able to run at all. Registration of
+ steps that have unmet dependencies are deferred until the
+ dependencies have been registered.
+
+ o 'title' is a one-line UI description for this step.
+ If None, the first line of the documentation string of the handler
+ is used, or the id if no docstring can be found.
+
+ o 'description' is a one-line UI description for this step.
+ If None, the remaining line of the documentation string of
+ the handler is used, or default to ''.
+ """
+
+class IExportStepRegistry( IStepRegistry ):
+
+ """ API for export step registry.
"""
- def __call__( context ):
+ def getStep( key, default=None ):
+
+ """ Return the IExportPlugin registered for 'key'.
+
+ o Return 'default' if no such step is registered.
+ """
+
+ def registerStep( id, handler, title=None, description=None ):
+
+ """ Register an export step.
+
+ o 'id' is the unique identifier for this step
+
+ o 'step' should implement IExportPlugin.
+
+ o 'title' is a one-line UI description for this step.
+ If None, the first line of the documentation string of the step
+ is used, or the id if no docstring can be found.
+
+ o 'description' is a one-line UI description for this step.
+ If None, the remaining line of the documentation string of
+ the step is used, or default to ''.
+ """
+
+class ISetupTool( Interface ):
+
+ """ API for SetupTool.
+ """
+
+ def runSetupStep( step_id, purge_old=True, run_dependencies=True ):
+
+ """ Execute a given setup step
+
+ o 'step_id' is the ID of the step to run.
+
+ o If 'purge_old' is True, then run the step after purging any
+ "old" setup first (this is the responsibility of the step,
+ which must check the context we supply).
+
+ o If 'run_dependencies' is True, then run any out-of-date
+ dependency steps first.
+ """
+
+ def runAllSetupSteps( purge_old=True ):
+
+ """ Run all setup steps in dependency order.
+
+ o If 'purge_old' is True, then run each step after purging any
+ "old" setup first (this is the responsibility of the step,
+ which must check the context we supply).
+ """
+
+ def runExportStep( step_id ):
+
+ """ Return a tarball containing artifacts from one export step.
+
+ o 'step_id' identifies the export step.
+ """
+
+ def runAllExportSteps():
+
+ """ Return a tarball created using all export steps.
+ """
+
+ def createSnapshot( snapshot_id ):
+
+ """ Create a snapshot folder using all steps.
+
+ o 'snapshot_id' is the ID of the new folder.
+ """
+
+ def compareConfigurations( lhs_id
+ , rhs_id
+ , missing_as_empty=False
+ , ignore_whitespace=False
+ ):
+ """ Compare two configurations.
+
+ o 'lhs_id' and 'rhs_id', if None, refer to the "default" filesystem
+ configuration.
+
+ o Otherwise, 'lhs_id' and 'rhs_id' refer to snapshots.
- """ Use data from 'context' to do initialization / configuration.
+ o If 'missing_as_empty', then compare files not present as though
+ they were zero-length; otherwise, omit such files.
- o 'context' must implement IExportContext.
+ o If 'ignore_whitespace', then suppress diffs due only to whitespace
+ (c.f: 'diff -wbB')
"""
=== CMF/CMFSetup/registry.py 1.7 => 1.8 ===
--- CMF/CMFSetup/registry.py:1.7 Fri May 14 09:28:52 2004
+++ CMF/CMFSetup/registry.py Sun May 23 14:57:38 2004
@@ -1,4 +1,4 @@
-""" Classes: SetupStepRegistry, ExportScriptRegistry
+""" Classes: ImportStepRegistry, ExportStepRegistry
$Id$
"""
@@ -9,35 +9,24 @@
from AccessControl import ClassSecurityInfo
from Acquisition import Implicit
from Globals import InitializeClass
-from Interface import Interface
from Products.PageTemplates.PageTemplateFile import PageTemplateFile
+from interfaces import IImportStepRegistry
+from interfaces import IExportStepRegistry
from permissions import ManagePortal
from utils import _xmldir
from utils import _getDottedName
from utils import _resolveDottedName
from utils import _extractDocstring
-class ISetupStep( Interface ):
-
- """ The executable object which performs a step to configure a site.
- """
- def __call__( context ):
-
- """ Perform the setup step.
-
- o Return a message describing the work done.
-
- o 'context' is a wrapper for the site object to be configured,
- along with the data files used by the step.
- """
-
-class SetupStepRegistry( Implicit ):
+class ImportStepRegistry( Implicit ):
""" Manage knowledge about steps to create / configure site.
o Steps are composed together to define a site profile.
"""
+ __implements__ = ( IImportStepRegistry, )
+
security = ClassSecurityInfo()
def __init__( self ):
@@ -127,7 +116,7 @@
security.declarePrivate( 'getStep' )
def getStep( self, key, default=None ):
- """ Return the ISetupStep registered for 'key'.
+ """ Return the IImportPlugin registered for 'key'.
o Return 'default' if no such step is registered.
"""
@@ -162,7 +151,7 @@
- Attempting to register an older one after a newer one results
in a KeyError.
- o 'handler' should implement ISetupStep.
+ o 'handler' should implement IImportPlugin.
o 'dependencies' is a tuple of step ids which have to run before
this step in order to be able to run at all. Registration of
@@ -212,7 +201,7 @@
if reader is not None:
text = reader()
- parseString( text, _SetupStepRegistryParser( self ) )
+ parseString( text, _ImportStepRegistryParser( self ) )
#
# Helper methods
@@ -244,11 +233,11 @@
return result
security.declarePrivate( '_exportTemplate' )
- _exportTemplate = PageTemplateFile( 'ssrExport.xml', _xmldir )
+ _exportTemplate = PageTemplateFile( 'isrExport.xml', _xmldir )
-InitializeClass( SetupStepRegistry )
+InitializeClass( ImportStepRegistry )
-class _SetupStepRegistryParser( ContentHandler ):
+class _ImportStepRegistryParser( ContentHandler ):
security = ClassSecurityInfo()
security.declareObjectPrivate()
@@ -263,14 +252,14 @@
def startElement( self, name, attrs ):
- if name == 'setup-steps':
+ if name == 'import-steps':
if self._started:
raise ValueError, 'Duplicated setup-steps element: %s' % name
self._started = True
- elif name == 'setup-step':
+ elif name == 'import-step':
if self._pending is not None:
raise ValueError, 'Cannot nest setup-step elements'
@@ -297,10 +286,10 @@
def endElement(self, name):
- if name == 'setup-steps':
+ if name == 'import-steps':
pass
- elif name == 'setup-step':
+ elif name == 'import-step':
if self._pending is None:
raise ValueError, 'No pending step!'
@@ -322,37 +311,19 @@
)
self._pending = None
-InitializeClass( _SetupStepRegistryParser )
-
-
-class IExportScript( Interface ):
-
- """ A script responsible for exporting some portion of the site.
- """
- def __call__( site ):
-
- """ Return a sequence of tuples describing a set of exported files.
-
- o Each item is a three-tuple, ( 'data', 'content_type', 'filename' ),
- representing one file exported by the script.
-
- - 'data' is a string containing the file data;
-
- - 'content_type' is the MIME type of the data;
+InitializeClass( _ImportStepRegistryParser )
- - 'filename' is a suggested filename for use when downloading.
- """
-class ExportScriptRegistry( Implicit ):
+class ExportStepRegistry( Implicit ):
- """ Registry of known site-configuration export scripts.
+ """ Registry of known site-configuration export steps.
- o Each script is registered with a unique id.
+ o Each step is registered with a unique id.
o When called, with the portal object passed in as an argument,
- the script must return a sequence of three-tuples,
+ the step must return a sequence of three-tuples,
( 'data', 'content_type', 'filename' ), one for each file exported
- by the script.
+ by the step.
- 'data' is a string containing the file data;
@@ -361,27 +332,29 @@
- 'filename' is a suggested filename for use when downloading.
"""
+ __implements__ = ( IExportStepRegistry, )
+
security = ClassSecurityInfo()
def __init__( self ):
self._registered = {}
- security.declareProtected( ManagePortal, 'listScripts' )
- def listScripts( self ):
+ security.declareProtected( ManagePortal, 'listSteps' )
+ def listSteps( self ):
- """ Return a list of registered script IDs.
+ """ Return a list of registered step IDs.
"""
return self._registered.keys()
- security.declareProtected( ManagePortal, 'getScriptMetadata' )
- def getScriptMetadata( self, key, default=None ):
+ security.declareProtected( ManagePortal, 'getStepMetadata' )
+ def getStepMetadata( self, key, default=None ):
- """ Return a mapping of metadata for the script identified by 'key'.
+ """ Return a mapping of metadata for the step identified by 'key'.
- o Return 'default' if no such script is registered.
+ o Return 'default' if no such step is registered.
- o The 'handler' metadata is available via 'getScript'.
+ o The 'handler' metadata is available via 'getStep'.
"""
info = self._registered.get( key )
@@ -390,16 +363,16 @@
return info.copy()
- security.declareProtected( ManagePortal, 'listScriptMetadata' )
- def listScriptMetadata( self ):
+ security.declareProtected( ManagePortal, 'listStepMetadata' )
+ def listStepMetadata( self ):
- """ Return a sequence of mappings describing registered scripts.
+ """ Return a sequence of mappings describing registered steps.
- o Scripts will be alphabetical by ID.
+ o Steps will be alphabetical by ID.
"""
- script_ids = self.listScripts()
- script_ids.sort()
- return [ self.getScriptMetadata( x ) for x in script_ids ]
+ step_ids = self.listSteps()
+ step_ids.sort()
+ return [ self.getStepMetadata( x ) for x in step_ids ]
security.declareProtected( ManagePortal, 'exportAsXML' )
def exportAsXML( self ):
@@ -410,12 +383,12 @@
"""
return self._exportTemplate()
- security.declarePrivate( 'getScript' )
- def getScript( self, key, default=None ):
+ security.declarePrivate( 'getStep' )
+ def getStep( self, key, default=None ):
- """ Return the IExportScript registered for 'key'.
+ """ Return the IExportPlugin registered for 'key'.
- o Return 'default' if no such script is registered.
+ o Return 'default' if no such step is registered.
"""
marker = object()
info = self._registered.get( key, marker )
@@ -425,27 +398,27 @@
return _resolveDottedName( info[ 'handler' ] )
- security.declarePrivate( 'registerScript' )
- def registerScript( self, id, handler, title=None, description=None ):
+ security.declarePrivate( 'registerStep' )
+ def registerStep( self, id, handler, title=None, description=None ):
- """ Register an export script.
+ """ Register an export step.
- o 'id' is the unique identifier for this script
+ o 'id' is the unique identifier for this step
- o 'script' should implement IExportScript.
+ o 'step' should implement IExportPlugin.
- o 'title' is a one-line UI description for this script.
- If None, the first line of the documentation string of the script
+ o 'title' is a one-line UI description for this step.
+ If None, the first line of the documentation string of the step
is used, or the id if no docstring can be found.
- o 'description' is a one-line UI description for this script.
+ o 'description' is a one-line UI description for this step.
If None, the remaining line of the documentation string of
- the script is used, or default to ''.
+ the step is used, or default to ''.
"""
- already = self.getScript( id )
+ already = self.getStep( id )
if already:
- raise KeyError( 'Existing registration for script %s' % id )
+ raise KeyError( 'Existing registration for step %s' % id )
if title is None or description is None:
@@ -474,7 +447,7 @@
if reader is not None:
text = reader()
- parseString( text, _ExportScriptRegistryParser( self ) )
+ parseString( text, _ExportStepRegistryParser( self ) )
#
# Helper methods
@@ -487,9 +460,9 @@
security.declarePrivate( '_exportTemplate' )
_exportTemplate = PageTemplateFile( 'esrExport.xml', _xmldir )
-InitializeClass( ExportScriptRegistry )
+InitializeClass( ExportStepRegistry )
-class _ExportScriptRegistryParser( ContentHandler ):
+class _ExportStepRegistryParser( ContentHandler ):
security = ClassSecurityInfo()
security.declareObjectPrivate()
@@ -504,17 +477,17 @@
def startElement( self, name, attrs ):
- if name == 'export-scripts':
+ if name == 'export-steps':
if self._started:
- raise ValueError, 'Duplicated export-scripts element: %s' % name
+ raise ValueError, 'Duplicated export-steps element: %s' % name
self._started = True
- elif name == 'export-script':
+ elif name == 'export-step':
if self._pending is not None:
- raise ValueError, 'Cannot nest export-script elements'
+ raise ValueError, 'Cannot nest export-step elements'
self._pending = dict( [ ( k, v.encode( self._encoding ) )
for k, v in attrs.items() ] )
@@ -530,13 +503,13 @@
def endElement(self, name):
- if name == 'export-scripts':
+ if name == 'export-steps':
pass
- elif name == 'export-script':
+ elif name == 'export-step':
if self._pending is None:
- raise ValueError, 'No pending script!'
+ raise ValueError, 'No pending step!'
id = self._pending[ 'id' ]
handler = _resolveDottedName( self._pending[ 'handler' ] )
@@ -544,11 +517,11 @@
title = self._pending.get( 'title', id )
description = ''.join( self._pending.get( 'description', [] ) )
- self._registry.registerScript( id=id
- , handler=handler
- , title=title
- , description=description
- )
+ self._registry.registerStep( id=id
+ , handler=handler
+ , title=title
+ , description=description
+ )
self._pending = None
-InitializeClass( _ExportScriptRegistryParser )
+InitializeClass( _ExportStepRegistryParser )
More information about the CMF-checkins
mailing list