[CMF-checkins] CVS: CMF/CMFSetup - registry.py:1.2 utils.py:1.2
Tres Seaver
tseaver at zope.com
Tue May 11 23:09:25 EDT 2004
Update of /cvs-repository/CMF/CMFSetup
In directory cvs.zope.org:/tmp/cvs-serv8682
Modified Files:
registry.py utils.py
Log Message:
- utils.py:
o Add utility function, '_extractDocstring', to parse title
/ description from a callable.
- registry.py:
o Add pseudo-interfaces (not checked) for the callables registered
with SetupStepRegistry.
o Add 'title' to metadata tracked by SSR.
o Add ExportScriptRegistry, paralleling SSR, but used to track
the scripts which export site configuration as files.
=== CMF/CMFSetup/registry.py 1.1 => 1.2 ===
--- CMF/CMFSetup/registry.py:1.1 Tue May 11 21:35:47 2004
+++ CMF/CMFSetup/registry.py Tue May 11 23:09:22 2004
@@ -1,4 +1,4 @@
-""" Classes: SetupStepRegistry
+""" Classes: SetupStepRegistry, ExportScriptRegistry
$Id$
"""
@@ -9,12 +9,28 @@
from AccessControl import ClassSecurityInfo
from Acquisition import Implicit
from Globals import InitializeClass
+from Interface import Interface
from Products.PageTemplates.PageTemplateFile import PageTemplateFile
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 ):
@@ -35,12 +51,15 @@
o Order is not significant.
"""
- return self._steps.keys()
+ return self._registered.keys()
security.declareProtected( ManagePortal, 'sortSteps' )
def sortSteps( self ):
- """ Return a sequence of step IDs, sorted topologically by dependency.
+ """ Return a sequence of registered step IDs
+
+ o Sequence is sorted topologically by dependency, with the dependent
+ steps *after* the steps they depend on.
"""
return self._computeTopologicalSort()
@@ -78,7 +97,7 @@
"""
result = {}
- info = self._steps.get( key )
+ info = self._registered.get( key )
if info is None:
return default
@@ -113,12 +132,12 @@
security.declarePrivate( 'getStep' )
def getStep( self, key, default=None ):
- """ Return the callable for the step identified by 'key'.
+ """ Return the ISetupStep registered for 'key'.
o Return 'default' if no such step is registered.
"""
marker = object()
- info = self._steps.get( key, marker )
+ info = self._registered.get( key, marker )
if info is marker:
return default
@@ -131,6 +150,7 @@
, version
, callable
, dependencies=()
+ , title=None
, description=None
):
""" Register a setup step.
@@ -147,20 +167,20 @@
- Attempting to register an older one after a newer one results
in a KeyError.
- o 'callable' is the setup code, which is passed a context object when
- called, and is expected to return a user-friendly message as to
- what happened. The context object provides access to data files
- and the portal object.
+ o 'callable' should implement ISetupStep.
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 'description' defaults to the first line of the function doc
- string, and can be used in a display enumerating steps. If the
- docstring is also empty, the id of the step is used as a final
- fallback.
+ o 'title' is a one-line UI description for this step.
+ If None, the first line of the documentation string of the callable
+ 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 callable is used, or default to ''.
"""
already = self.getStepMetadata( id )
@@ -172,10 +192,11 @@
, 'version' : version
, 'callable' : callable
, 'dependencies' : dependencies
+ , 'title' : title
, 'description' : description
}
- self._steps[ id ] = info
+ self._registered[ id ] = info
security.declarePrivate( 'importFromXML' )
def importFromXML( self, text ):
@@ -197,7 +218,7 @@
security.declarePrivate( '_clear' )
def _clear( self ):
- self._steps = {}
+ self._registered = {}
security.declarePrivate( '_computeTopologicalSort' )
def _computeTopologicalSort( self ):
@@ -205,7 +226,7 @@
result = []
graph = [ ( x[ 'id' ], x[ 'dependencies' ] )
- for x in self._steps.values() ]
+ for x in self._registered.values() ]
for node, edges in graph:
@@ -231,8 +252,6 @@
security.declareObjectPrivate()
security.setDefaultAccess( 'deny' )
- _WHITESPACE = re.compile( r'\s*' )
-
def __init__( self, registry, encoding='latin-1' ):
self._registry = registry
@@ -289,14 +308,154 @@
callable = _resolveDottedName( self._pending[ 'callable' ] )
dependencies = tuple( self._pending.get( 'dependencies', () ) )
+ title = self._pending.get( 'title', id )
description = ''.join( self._pending.get( 'description', [] ) )
self._registry.registerStep( id=id
, version=version
, callable=callable
, dependencies=dependencies
+ , title=title
, description=description
)
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;
+
+ - 'filename' is a suggested filename for use when downloading.
+ """
+
+class ExportScriptRegistry( Implicit ):
+
+ """ Registry of known site-configuration export scripts.
+
+ o Each script 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,
+ ( 'data', 'content_type', 'filename' ), one for each file exported
+ by the script.
+
+ - 'data' is a string containing the file data;
+
+ - 'content_type' is the MIME type of the data;
+
+ - 'filename' is a suggested filename for use when downloading.
+
+ """
+ security = ClassSecurityInfo()
+
+ def __init__( self ):
+
+ self._registered = {}
+
+ security.declareProtected( ManagePortal, 'listScripts' )
+ def listScripts( self ):
+
+ """ Return a list of registered script IDs.
+ """
+ return self._registered.keys()
+
+ security.declareProtected( ManagePortal, 'getScriptMetadata' )
+ def getScriptMetadata( self, key, default=None ):
+
+ """ Return a mapping of metadata for the script identified by 'key'.
+
+ o Return 'default' if no such script is registered.
+
+ o The 'callable' metadata is available via 'getScript'.
+ """
+ result = {}
+
+ info = self._registered.get( key )
+
+ if info is None:
+ return default
+
+ for key, value in info.items():
+
+ if key == 'callable':
+ result[ key ] = _getDottedName( value )
+ else:
+ result[ key ] = value
+
+ return result
+
+ security.declareProtected( ManagePortal, 'listScriptMetadata' )
+ def listScriptMetadata( self ):
+
+ """ Return a sequence of mappings describing registered scripts.
+
+ o Order is not significant.
+ """
+ return [ self.getStepMetadata( x ) for x in self.listScripts() ]
+
+ security.declarePrivate( 'getScript' )
+ def getScript( self, key, default=None ):
+
+ """ Return the IExportScript registered for 'key'.
+
+ o Return 'default' if no such script is registered.
+ """
+ marker = object()
+ info = self._registered.get( key, marker )
+
+ if info is marker:
+ return default
+
+ return info[ 'callable' ]
+
+ security.declarePrivate( 'registerScript' )
+ def registerScript( self, id, callable, title=None, description=None ):
+
+ """ Register an export script.
+
+ o 'id' is the unique identifier for this script
+
+ o 'script' should implement IExportScript.
+
+ o 'title' is a one-line UI description for this script.
+ If None, the first line of the documentation string of the script
+ is used, or the id if no docstring can be found.
+
+ o 'description' is a one-line UI description for this script.
+ If None, the remaining line of the documentation string of
+ the script is used, or default to ''.
+ """
+ already = self.getScript( id )
+
+ if already:
+ raise KeyError( 'Existing registration for script %s' % id )
+
+ if title is None or description is None:
+
+ t, d = _extractDocstring( callable, id, '' )
+
+ title = title or t
+ description = description or d
+
+ info = { 'id' : id
+ , 'callable' : callable
+ , 'title' : title
+ , 'description' : description
+ }
+
+ self._registered[ id ] = info
+
+InitializeClass( ExportScriptRegistry )
=== CMF/CMFSetup/utils.py 1.1 => 1.2 ===
--- CMF/CMFSetup/utils.py:1.1 Tue May 11 21:35:47 2004
+++ CMF/CMFSetup/utils.py Tue May 11 23:09:22 2004
@@ -3,6 +3,7 @@
$Id$
"""
import os
+from inspect import getdoc
from Globals import package_home
@@ -45,3 +46,23 @@
return obj
+def _extractDocstring( func, default_title, default_description ):
+
+ try:
+ doc = getdoc( func )
+ lines = doc.split( '\n' )
+
+ except AttributeError:
+
+ title = default_title
+ description = default_description
+
+ else:
+ title = lines[ 0 ]
+
+ if len( lines ) > 1 and lines[ 1 ].strip() == '':
+ del lines[ 1 ]
+
+ description = '\n'.join( lines[ 1: ] )
+
+ return title, description
More information about the CMF-checkins
mailing list