[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