[Checkins] SVN: GenericSetup/branches/1.3/ Ignore import and export step handlers that we can not resolve. Remove some

Wichert Akkerman wichert at wiggy.net
Thu Sep 6 18:54:11 EDT 2007


Log message for revision 79508:
  Ignore import and export step handlers that we can not resolve. Remove some
  dotted name lookup/resolve cycles by modifying the registry to take a dotted
  name as a handler.
  
  Restore the import context after running steps from a profile so we do not
  break on nested calls.
  
  

Changed:
  U   GenericSetup/branches/1.3/CHANGES.txt
  U   GenericSetup/branches/1.3/registry.py
  U   GenericSetup/branches/1.3/tests/test_registry.py
  U   GenericSetup/branches/1.3/tool.py
  U   GenericSetup/branches/1.3/utils.py
  U   GenericSetup/branches/1.3/www/sutImportSteps.zpt

-=-
Modified: GenericSetup/branches/1.3/CHANGES.txt
===================================================================
--- GenericSetup/branches/1.3/CHANGES.txt	2007-09-06 19:22:39 UTC (rev 79507)
+++ GenericSetup/branches/1.3/CHANGES.txt	2007-09-06 22:54:10 UTC (rev 79508)
@@ -3,6 +3,11 @@
 
   GenericSetup 1.3.2 (unreleased)
 
+    - Ignore import and export step handlers that we can not resolve.
+
+    - Restore the import context after running steps from a profile
+      so we do not break on nested calls.
+
     - components: Provide log output when purging utilities or adapters.
 
     - components: Fixed an undefined variable name in a log message.

Modified: GenericSetup/branches/1.3/registry.py
===================================================================
--- GenericSetup/branches/1.3/registry.py	2007-09-06 19:22:39 UTC (rev 79507)
+++ GenericSetup/branches/1.3/registry.py	2007-09-06 22:54:10 UTC (rev 79508)
@@ -111,8 +111,11 @@
         if info is None:
             return default
 
-        return info.copy()
+        result = info.copy()
+        result['invalid'] =  _resolveDottedName( result[ 'handler' ] ) is None
 
+        return result
+
     security.declareProtected( ManagePortal, 'listStepMetadata' )
     def listStepMetadata( self ):
 
@@ -171,7 +174,8 @@
           - Attempting to register an older one after a newer one results
             in a KeyError.
 
-        o 'handler' should implement IImportPlugin.
+        o 'handler' is the dottoed name of a handler which 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
@@ -192,16 +196,23 @@
             raise KeyError( 'Existing registration for step %s, version %s'
                           % ( id, already[ 'version' ] ) )
 
+        if not isinstance(handler, str):
+            handler = _getDottedName( handler )
+
         if title is None or description is None:
 
-            t, d = _extractDocstring( handler, id, '' )
+            method = _resolveDottedName(handler)
+            if method is None:
+                t,d = id, ''
+            else:
+                t, d = _extractDocstring( method, id, '' )
 
             title = title or t
             description = description or d
 
         info = { 'id'           : id
                , 'version'      : version
-               , 'handler'      : _getDottedName( handler )
+               , 'handler'      : handler
                , 'dependencies' : dependencies
                , 'title'        : title
                , 'description'  : description
@@ -326,8 +337,11 @@
         if info is None:
             return default
 
-        return info.copy()
+        result = info.copy()
+        result['invalid'] =  _resolveDottedName( result[ 'handler' ] ) is None
 
+        return result
+
     security.declareProtected( ManagePortal, 'listStepMetadata' )
     def listStepMetadata( self ):
 
@@ -370,7 +384,8 @@
 
         o 'id' is the unique identifier for this step
 
-        o 'step' should implement IExportPlugin.
+        o 'handler' is the dottoed name of a handler which should implement
+           IImportPlugin.
 
         o 'title' is a one-line UI description for this step.
           If None, the first line of the documentation string of the step
@@ -380,15 +395,22 @@
           If None, the remaining line of the documentation string of
           the step is used, or default to ''.
         """
+        if not isinstance(handler, str):
+            handler = _getDottedName( handler )
+
         if title is None or description is None:
 
-            t, d = _extractDocstring( handler, id, '' )
+            method = _resolveDottedName(handler)
+            if method is None:
+                t,d = id, ''
+            else:
+                t, d = _extractDocstring( method, id, '' )
 
             title = title or t
             description = description or d
 
         info = { 'id'           : id
-               , 'handler'      : _getDottedName( handler )
+               , 'handler'      : handler
                , 'title'        : title
                , 'description'  : description
                }

Modified: GenericSetup/branches/1.3/tests/test_registry.py
===================================================================
--- GenericSetup/branches/1.3/tests/test_registry.py	2007-09-06 19:22:39 UTC (rev 79507)
+++ GenericSetup/branches/1.3/tests/test_registry.py	2007-09-06 22:54:10 UTC (rev 79508)
@@ -38,13 +38,18 @@
 def TWO_FUNC( context ): pass
 def THREE_FUNC( context ): pass
 def FOUR_FUNC( context ): pass
+def DOC_FUNC( site ):
+    """This is the first line.
 
+    This is the second line.
+    """
+
 ONE_FUNC_NAME = '%s.%s' % ( __name__, ONE_FUNC.__name__ )
 TWO_FUNC_NAME = '%s.%s' % ( __name__, TWO_FUNC.__name__ )
 THREE_FUNC_NAME = '%s.%s' % ( __name__, THREE_FUNC.__name__ )
 FOUR_FUNC_NAME = '%s.%s' % ( __name__, FOUR_FUNC.__name__ )
+DOC_FUNC_NAME = '%s.%s' % ( __name__, DOC_FUNC.__name__ )
 
-
 #==============================================================================
 #   SSR tests
 #==============================================================================
@@ -91,49 +96,35 @@
 
     def test_registerStep_docstring( self ):
 
-        def func_with_doc( site ):
-            """This is the first line.
-
-            This is the second line.
-            """
-        FUNC_NAME = '%s.%s' % ( __name__, func_with_doc.__name__ )
-
         registry = self._makeOne()
 
         registry.registerStep( id='docstring'
                              , version='1'
-                             , handler=func_with_doc
+                             , handler=DOC_FUNC
                              , dependencies=()
                              )
 
         info = registry.getStepMetadata( 'docstring' )
         self.assertEqual( info[ 'id' ], 'docstring' )
-        self.assertEqual( info[ 'handler' ], FUNC_NAME )
+        self.assertEqual( info[ 'handler' ], DOC_FUNC_NAME )
         self.assertEqual( info[ 'dependencies' ], () )
         self.assertEqual( info[ 'title' ], 'This is the first line.' )
         self.assertEqual( info[ 'description' ] , 'This is the second line.' )
 
     def test_registerStep_docstring_override( self ):
 
-        def func_with_doc( site ):
-            """This is the first line.
-
-            This is the second line.
-            """
-        FUNC_NAME = '%s.%s' % ( __name__, func_with_doc.__name__ )
-
         registry = self._makeOne()
 
         registry.registerStep( id='docstring'
                              , version='1'
-                             , handler=func_with_doc
+                             , handler=DOC_FUNC
                              , dependencies=()
                              , title='Title'
                              )
 
         info = registry.getStepMetadata( 'docstring' )
         self.assertEqual( info[ 'id' ], 'docstring' )
-        self.assertEqual( info[ 'handler' ], FUNC_NAME )
+        self.assertEqual( info[ 'handler' ], DOC_FUNC_NAME )
         self.assertEqual( info[ 'dependencies' ], () )
         self.assertEqual( info[ 'title' ], 'Title' )
         self.assertEqual( info[ 'description' ] , 'This is the second line.' )
@@ -171,7 +162,7 @@
 
         registry = self._makeOne()
 
-        registry.registerStep( id='one', version='1', handler=ONE_FUNC )
+        registry.registerStep( id='one', version='1', handler=ONE_FUNC_NAME )
 
         self.assertRaises( KeyError
                          , registry.registerStep
@@ -180,7 +171,7 @@
                          , handler=ONE_FUNC
                          )
 
-        registry.registerStep( id='one', version='1', handler=ONE_FUNC )
+        registry.registerStep( id='one', version='1', handler=ONE_FUNC_NAME )
 
         info_list = registry.listStepMetadata()
         self.assertEqual( len( info_list ), 1 )
@@ -632,7 +623,7 @@
     def test_registerStep_simple( self ):
 
         registry = self._makeOne()
-        registry.registerStep( 'one', ONE_FUNC )
+        registry.registerStep( 'one', ONE_FUNC_NAME )
         info = registry.getStepMetadata( 'one', {} )
 
         self.assertEqual( info[ 'id' ], 'one' )
@@ -642,38 +633,24 @@
 
     def test_registerStep_docstring( self ):
 
-        def func_with_doc( site ):
-            """This is the first line.
-
-            This is the second line.
-            """
-        FUNC_NAME = '%s.%s' % ( __name__, func_with_doc.__name__ )
-
         registry = self._makeOne()
-        registry.registerStep( 'one', func_with_doc )
+        registry.registerStep( 'one', DOC_FUNC )
         info = registry.getStepMetadata( 'one', {} )
 
         self.assertEqual( info[ 'id' ], 'one' )
-        self.assertEqual( info[ 'handler' ], FUNC_NAME )
+        self.assertEqual( info[ 'handler' ], DOC_FUNC_NAME )
         self.assertEqual( info[ 'title' ], 'This is the first line.' )
         self.assertEqual( info[ 'description' ] , 'This is the second line.' )
 
     def test_registerStep_docstring_with_override( self ):
 
-        def func_with_doc( site ):
-            """This is the first line.
-
-            This is the second line.
-            """
-        FUNC_NAME = '%s.%s' % ( __name__, func_with_doc.__name__ )
-
         registry = self._makeOne()
-        registry.registerStep( 'one', func_with_doc
+        registry.registerStep( 'one', DOC_FUNC
                                , description='Description' )
         info = registry.getStepMetadata( 'one', {} )
 
         self.assertEqual( info[ 'id' ], 'one' )
-        self.assertEqual( info[ 'handler' ], FUNC_NAME )
+        self.assertEqual( info[ 'handler' ], DOC_FUNC_NAME )
         self.assertEqual( info[ 'title' ], 'This is the first line.' )
         self.assertEqual( info[ 'description' ], 'Description' )
 

Modified: GenericSetup/branches/1.3/tool.py
===================================================================
--- GenericSetup/branches/1.3/tool.py	2007-09-06 19:22:39 UTC (rev 79507)
+++ GenericSetup/branches/1.3/tool.py	2007-09-06 22:54:10 UTC (rev 79508)
@@ -50,6 +50,7 @@
 from upgrade import listProfilesWithUpgrades
 from upgrade import _upgrade_registry
 
+from utils import _getDottedName
 from utils import _resolveDottedName
 from utils import _wwwdir
 
@@ -162,7 +163,7 @@
         self._import_registry = ImportStepRegistry()
         self._export_registry = ExportStepRegistry()
         self._export_registry.registerStep('step_registries',
-                                           exportStepRegistries,
+                                           _getDottedName(exportStepRegistries),
                                            'Export import / export steps.',
                                           )
         self._toolset_registry = ToolsetRegistry()
@@ -258,6 +259,7 @@
                                  run_dependencies=True, purge_old=None):
         """ See ISetupTool.
         """
+        old_context = self._import_context_id
         context = self._getImportContext(profile_id, purge_old)
 
         self.applyContext(context)
@@ -265,6 +267,7 @@
         info = self._import_registry.getStepMetadata(step_id)
 
         if info is None:
+            self._import_context_id = old_context
             raise ValueError, 'No such import step: %s' % step_id
 
         dependencies = info.get('dependencies', ())
@@ -285,6 +288,8 @@
         messages[step_id] = '\n'.join(message_list)
         steps.append(step_id)
 
+        self._import_context_id = old_context
+
         return { 'steps' : steps, 'messages' : messages }
 
     security.declareProtected(ManagePortal, 'runImportStep')
@@ -308,12 +313,16 @@
         """
         __traceback_info__ = profile_id
 
+        old_context = self._import_context_id
         context = self._getImportContext(profile_id, purge_old)
 
         result = self._runImportStepsFromContext(context, purge_old=purge_old)
         prefix = 'import-all-%s' % profile_id.replace(':', '_')
         name = self._mangleTimestampName(prefix, 'log')
         self._createReport(name, result['steps'], result['messages'])
+
+        self._import_context_id = old_context
+
         return result
 
     security.declareProtected(ManagePortal, 'runAllImportSteps')
@@ -356,7 +365,9 @@
             handler = self._export_registry.getStep(step_id)
 
             if handler is None:
-                raise ValueError('Invalid export step: %s' % step_id)
+                logger = logging.getLogger('GenericSetup')
+                logger.error('Step %s has an invalid handler' % step_id)
+                continue
 
             messages[step_id] = handler(context)
 
@@ -910,8 +921,7 @@
 
             id = step_info['id']
             version = step_info['version']
-            handler = _resolveDottedName(step_info['handler'])
-
+            handler = step_info['handler']
             dependencies = tuple(step_info.get('dependencies', ()))
             title = step_info.get('title', id)
             description = ''.join(step_info.get('description', []))
@@ -940,8 +950,7 @@
         for step_info in info_list:
 
             id = step_info['id']
-            handler = _resolveDottedName(step_info['handler'])
-
+            handler = step_info['handler']
             title = step_info.get('title', id)
             description = ''.join(step_info.get('description', []))
 
@@ -957,12 +966,19 @@
         """ Run a single import step, using a pre-built context.
         """
         __traceback_info__ = step_id
+        marker = object()
 
         handler = self._import_registry.getStep(step_id)
 
-        if handler is None:
+        if handler is marker:
             raise ValueError('Invalid import step: %s' % step_id)
 
+        if handler is None:
+            msg = 'Step %s has an invalid import handler' % step_id
+            logger = logging.getLogger('GenericSetup')
+            logger.error(msg)
+            return 'ERROR: ' + msg
+
         return handler(context)
 
     security.declarePrivate('_doRunExportSteps')
@@ -972,15 +988,22 @@
         """
         context = TarballExportContext(self)
         messages = {}
+        marker = object()
 
         for step_id in steps:
 
-            handler = self._export_registry.getStep(step_id)
+            handler = self._export_registry.getStep(step_id, marker)
 
-            if handler is None:
+            if handler is marker:
                 raise ValueError('Invalid export step: %s' % step_id)
 
-            messages[step_id] = handler(context)
+            if handler is None:
+                msg = 'Step %s has an invalid import handler' % step_id
+                logger = logging.getLogger('GenericSetup')
+                logger.error(msg)
+                messages[step_id] = msg
+            else:
+                messages[step_id] = handler(context)
 
         return { 'steps' : steps
                , 'messages' : messages

Modified: GenericSetup/branches/1.3/utils.py
===================================================================
--- GenericSetup/branches/1.3/utils.py	2007-09-06 19:22:39 UTC (rev 79507)
+++ GenericSetup/branches/1.3/utils.py	2007-09-06 22:54:10 UTC (rev 79508)
@@ -115,14 +115,17 @@
             del parts_copy[ -1 ]
 
             if not parts_copy:
-                raise
+                return None
 
     parts = parts[ 1: ] # Funky semantics of __import__'s return value
 
     obj = module
 
     for part in parts:
-        obj = getattr( obj, part )
+        try:
+            obj = getattr( obj, part )
+        except AttributeError:
+            return None
 
     return obj
 

Modified: GenericSetup/branches/1.3/www/sutImportSteps.zpt
===================================================================
--- GenericSetup/branches/1.3/www/sutImportSteps.zpt	2007-09-06 19:22:39 UTC (rev 79507)
+++ GenericSetup/branches/1.3/www/sutImportSteps.zpt	2007-09-06 22:54:10 UTC (rev 79508)
@@ -62,7 +62,8 @@
   <tr valign="top"
       tal:define="info python: registry.getStepMetadata( step_id );"
       tal:attributes="class python:
-                     repeat[ 'step_id' ].odd and 'row-normal' or 'row-hilite'" >
+                     repeat[ 'step_id' ].odd and 'row-normal' or 'row-hilite';
+                     style python:info['invalid'] and 'background: red' or None" >
    <td class="list-item" width="16">
     <input type="checkbox" name="ids:list" value="STEP_ID"
            tal:attributes="value step_id" />



More information about the Checkins mailing list