[Checkins] SVN: z3c.quickentry/trunk/src/z3c/quickentry/ Implemented better error handling and reporting, so that user interfaces

Stephan Richter srichter at cosmos.phy.tufts.edu
Tue Feb 13 04:31:56 EST 2007


Log message for revision 72509:
  Implemented better error handling and reporting, so that user interfaces 
  can be more informative.
  

Changed:
  U   z3c.quickentry/trunk/src/z3c/quickentry/README.txt
  U   z3c.quickentry/trunk/src/z3c/quickentry/interfaces.py
  U   z3c.quickentry/trunk/src/z3c/quickentry/plugin.py
  U   z3c.quickentry/trunk/src/z3c/quickentry/processor.py

-=-
Modified: z3c.quickentry/trunk/src/z3c/quickentry/README.txt
===================================================================
--- z3c.quickentry/trunk/src/z3c/quickentry/README.txt	2007-02-12 22:11:59 UTC (rev 72508)
+++ z3c.quickentry/trunk/src/z3c/quickentry/README.txt	2007-02-13 09:31:51 UTC (rev 72509)
@@ -28,12 +28,16 @@
   ...     shortName = 'nm'
   ...     varName = 'name'
 
-Any plugin is instantiated using an initial text:
+Any plugin is instantiated using an initial text and optionally a position
+that is used during error reporting:
 
   >>> name = NamePlugin('nm=Stephan')
   >>> name
   <NamePlugin shortName='nm', varName='name'>
 
+  >>> NamePlugin('nm=Stephan', 35)
+  <NamePlugin shortName='nm', varName='name'>
+
 You can now ask the plugin, whether it can process this text:
 
   >>> name.canProcess()
@@ -83,18 +87,22 @@
   >>> phone.process(None)
   {'phone': u'978-555-5300'}
 
-If the text changes, so that the plugin cannot parse the text anymore, a value
-error is raised:
+If the text changes, so that the plugin cannot parse the text anymore, a
+process error is raised:
 
   >>> phone.text += ' (ext. 2134)'
   >>> phone.process(None)
   Traceback (most recent call last):
   ...
-  ValueError: The regex did match anymore. ...
+  ProcessError: The regex did match anymore. Probably some text was added
+                later that disrupted the pattern. (Position 0)
 
+
 Finally let's have a look at a more advanced example. We would like to be able
 to handle the string "<age><gender>" and parse it into 2 variables:
 
+  >>> from z3c.quickentry import interfaces
+
   >>> class AgeGenderPlugin(plugin.BasePlugin):
   ...     regex = re.compile('([0-9]{1,3})([FM])')
   ...
@@ -103,6 +111,8 @@
   ...
   ...     def process(self, context):
   ...         match = self.regex.match(self.text)
+  ...         if match is None:
+  ...            raise interfaces.ProcessError(self.position, u'Error here.')
   ...         return {'age': int(match.groups()[0]),
   ...                 'gender': unicode(match.groups()[1])}
 
@@ -130,6 +140,14 @@
   >>> pprint(AgeGenderPlugin('101F').process(None))
   {'age': 101, 'gender': u'F'}
 
+When an error occurs at any point during the processing, a process error must
+be raised:
+
+  >>> pprint(AgeGenderPlugin('27N').process(None))
+  Traceback (most recent call last):
+  ...
+  ProcessError: Error here. (Position 0)
+
 The plugin above used the ``BasePlugin`` class to minimize the
 boilerplate. The base plugin requires you to implement the ``canProcess()``
 and ``process()``:
@@ -226,7 +244,14 @@
   >>> pprint(info.process('nm=Stephan Richter,27M', context=object()))
   {'age': 27, 'gender': u'M', 'name': u'Stephan Richter'}
 
+But what happens, if no plugin can be found. Then a process error is raised:
 
+  >>> info.process('err=Value', context=object())
+  Traceback (most recent call last):
+  ...
+  ProcessError: No matching plugin found. (Position 0)
+
+
 Executing Processors
 --------------------
 

Modified: z3c.quickentry/trunk/src/z3c/quickentry/interfaces.py
===================================================================
--- z3c.quickentry/trunk/src/z3c/quickentry/interfaces.py	2007-02-12 22:11:59 UTC (rev 72508)
+++ z3c.quickentry/trunk/src/z3c/quickentry/interfaces.py	2007-02-13 09:31:51 UTC (rev 72509)
@@ -17,7 +17,35 @@
 """
 __docformat__ = "reStructuredText"
 import zope.interface
+import zope.schema
 
+class IProcessError(zope.interface.Interface):
+
+    position = zope.schema.Int(
+        title=u'Position',
+        description=u'The position at which the error occured.',
+        required=True)
+
+    reason = zope.schema.Text(
+        title=u'Reason',
+        description=u'The reason for the parse error.',
+        required=True)
+
+class ProcessError(Exception):
+    zope.interface.implements(IProcessError)
+
+    def __init__(self, position, reason):
+        self.position = position
+        self.reason = reason
+
+    def __repr__(self):
+        return '<%s at pos %r: %r>' %(
+            self.__class__.__name__, self.position, self.reason)
+
+    def __str__(self):
+        return self.reason + u' (Position %i)' %self.position
+
+
 class IProcessor(zope.interface.Interface):
     """A processor for a quick entry text."""
 
@@ -55,6 +83,12 @@
         'The text that is going to be converted into values. '
         'The processor will fill this attribute after the initial text is set.')
 
+    position = zope.schema.Int(
+        title=u'Position',
+        description=u'The position at which the text started',
+        default=0,
+        required=True)
+
     def canProcess():
         """Determine whether the plugin can handle the text.
 

Modified: z3c.quickentry/trunk/src/z3c/quickentry/plugin.py
===================================================================
--- z3c.quickentry/trunk/src/z3c/quickentry/plugin.py	2007-02-12 22:11:59 UTC (rev 72508)
+++ z3c.quickentry/trunk/src/z3c/quickentry/plugin.py	2007-02-13 09:31:51 UTC (rev 72509)
@@ -24,8 +24,9 @@
     """An abstract base plugin."""
     zope.interface.implements(interfaces.IPlugin)
 
-    def __init__(self, initialText):
+    def __init__(self, initialText, position=0):
         self.text = initialText
+        self.position = position
 
     def canProcess(self):
         """See interfaces.IPlugin"""
@@ -73,8 +74,10 @@
     def process(self, context):
         """See interfaces.IPlugin"""
         if self.regex.match(self.text) is None:
-            raise ValueError('The regex did match anymore. Probably some text '
-                             'was added later that disrupted the pattern.')
+            raise interfaces.ProcessError(
+                self.position,
+                (u'The regex did match anymore. Probably some text '
+                 u'was added later that disrupted the pattern.'))
         return {self.varName: unicode(self.text)}
 
     def __repr__(self):

Modified: z3c.quickentry/trunk/src/z3c/quickentry/processor.py
===================================================================
--- z3c.quickentry/trunk/src/z3c/quickentry/processor.py	2007-02-12 22:11:59 UTC (rev 72508)
+++ z3c.quickentry/trunk/src/z3c/quickentry/processor.py	2007-02-13 09:31:51 UTC (rev 72509)
@@ -29,6 +29,7 @@
     plugins = ()
 
     def parse(self, text):
+        position = 0
         # Step 0: Get the sequence of all plugins; we store the result
         #         locally, since the lookup might be expensive.
         plugins = self.plugins
@@ -49,8 +50,14 @@
             # Step 2.3: If no plugin can handle the piece, it is simply added
             #           to the text of the last plugin's test.
             else:
+                if len(result) == 0:
+                    raise interfaces.ProcessError(
+                        position, u'No matching plugin found.')
                 result[-1].text += self.separationCharacter
                 result[-1].text += piece
+            # Step 2.4: Update the position
+            #           (add one for the separation character)
+            position += len(piece) + 1
         return result
 
     def process(self, text, context=None):



More information about the Checkins mailing list