]> git.llucax.com Git - software/pymin.git/blobdiff - pymin/dispatcher.py
Improve WrongArgumentError class.
[software/pymin.git] / pymin / dispatcher.py
index 39b4b75cf6522f64e78b2d097422a2bfc69285e1..b5567a4747e47a854b232dc58a97e3559e65c629 100644 (file)
@@ -7,6 +7,8 @@ It's based on Zope or Cherrypy dispatching (but implemented from the scratch)
 and translates commands to functions/objects/methods.
 """
 
 and translates commands to functions/objects/methods.
 """
 
+import re
+
 __ALL__ = ('Error', 'HandlerError', 'CommandNotFoundError', 'Handler',
             'Dispatcher', 'handler', 'is_handler', 'get_help')
 
 __ALL__ = ('Error', 'HandlerError', 'CommandNotFoundError', 'Handler',
             'Dispatcher', 'handler', 'is_handler', 'get_help')
 
@@ -52,6 +54,20 @@ class CommandError(Error):
     def __unicode__(self):
         return u'Error in command "%s".' % u' '.join(self.command)
 
     def __unicode__(self):
         return u'Error in command "%s".' % u' '.join(self.command)
 
+class WrongArgumentsError(CommandError):
+    r"""WrongArgumentsError(handler, message) -> WrongArgumentsError instance.
+
+    This exception is raised when an empty command string is received.
+    """
+
+    def __init__(self, handler, message):
+        r"Initialize the object, see class documentation for more info."
+        self.handler = handler
+        self.message = message
+
+    def __unicode__(self):
+        return u'Command "%s" %s.' % (self.handler.__name__, self.message)
+
 class CommandNotSpecifiedError(CommandError):
     r"""CommandNotSpecifiedError() -> CommandNotSpecifiedError instance.
 
 class CommandNotSpecifiedError(CommandError):
     r"""CommandNotSpecifiedError() -> CommandNotSpecifiedError instance.
 
@@ -223,6 +239,8 @@ def parse_command(command):
     arguments is preserved and if there are multiple keyword arguments with
     the same key, the last value is the winner (all other values are lost).
 
     arguments is preserved and if there are multiple keyword arguments with
     the same key, the last value is the winner (all other values are lost).
 
+    The command should be a unicode string.
+
     Examples:
 
     >>> parse_command('hello world')
     Examples:
 
     >>> parse_command('hello world')
@@ -250,6 +268,12 @@ def parse_command(command):
     ([u'=hello'], {})
     >>> parse_command(r'\thello')
     ([u'\thello'], {})
     ([u'=hello'], {})
     >>> parse_command(r'\thello')
     ([u'\thello'], {})
+    >>> parse_command(r'hello \n')
+    ([u'hello', u'\n'], {})
+    >>> parse_command(r'hello \nmundo')
+    ([u'hello', u'\nmundo'], {})
+    >>> parse_command(r'test \N')
+    ([u'test', None], {})
     >>> parse_command(r'\N')
     ([None], {})
     >>> parse_command(r'none=\N')
     >>> parse_command(r'\N')
     ([None], {})
     >>> parse_command(r'none=\N')
@@ -274,9 +298,23 @@ def parse_command(command):
     escape = False
     keyword = None
     state = SEP
     escape = False
     keyword = None
     state = SEP
+    def register_token(buff, keyword, seq, dic):
+        if buff == r'\N':
+            buff = None
+        if keyword is not None:
+            dic[keyword.encode('utf-8')] = buff
+            keyword = None
+        else:
+            seq.append(buff)
+        buff = u''
+        return (buff, keyword)
     for n, c in enumerate(command):
         # Escaped character
         if escape:
     for n, c in enumerate(command):
         # Escaped character
         if escape:
+            # Not yet registered the token
+            if state == SEP and buff:
+                (buff, keyword) = register_token(buff, keyword, seq, dic)
+                state = TOKEN
             for e in escaped_chars:
                 if c == e:
                     buff += eval(u'"\\' + e + u'"')
             for e in escaped_chars:
                 if c == e:
                     buff += eval(u'"\\' + e + u'"')
@@ -301,14 +339,7 @@ def parse_command(command):
                     keyword = buff
                     buff = u''
                     continue
                     keyword = buff
                     buff = u''
                     continue
-                if buff == r'\N':
-                    buff = None
-                if keyword is not None: # Value found
-                    dic[str(keyword)] = buff
-                    keyword = None
-                else: # Normal parameter found
-                    seq.append(buff)
-                buff = u''
+                (buff, keyword) = register_token(buff, keyword, seq, dic)
             state = TOKEN
         # Getting a token
         if state == TOKEN:
             state = TOKEN
         # Getting a token
         if state == TOKEN:
@@ -350,14 +381,12 @@ def parse_command(command):
         raise ParseError(command,
                         u'keyword argument (%s) without value' % keyword)
     if buff:
         raise ParseError(command,
                         u'keyword argument (%s) without value' % keyword)
     if buff:
-        if buff == r'\N':
-            buff = None
-        if keyword is not None:
-            dic[str(keyword)] = buff
-        else:
-            seq.append(buff)
+        register_token(buff, keyword, seq, dic)
     return (seq, dic)
 
     return (seq, dic)
 
+args_re = re.compile(r'\w+\(\) takes (.+) (\d+) \w+ \((\d+) given\)')
+kw_re = re.compile(r'\w+\(\) got an unexpected keyword argument (.+)')
+
 class Dispatcher:
     r"""Dispatcher([root]) -> Dispatcher instance :: Command dispatcher.
 
 class Dispatcher:
     r"""Dispatcher([root]) -> Dispatcher instance :: Command dispatcher.
 
@@ -419,7 +448,27 @@ class Dispatcher:
                 raise CommandNotFoundError(command)
             handler = getattr(handler, route[0].encode('utf-8'))
             route = route[1:]
                 raise CommandNotFoundError(command)
             handler = getattr(handler, route[0].encode('utf-8'))
             route = route[1:]
-        return handler(*route, **kwargs)
+        try:
+            return handler(*route, **kwargs)
+        except TypeError, e:
+            m = args_re.match(unicode(e))
+            if m:
+                (quant, n_ok, n_bad)  = m.groups()
+                n_ok = int(n_ok)
+                n_bad = int(n_bad)
+                n_ok -= 1
+                n_bad -= 1
+                pl = ''
+                if n_ok > 1:
+                    pl = 's'
+                raise WrongArgumentsError(handler, u'takes %s %s argument%s, '
+                            '%s given' % (quant, n_ok, pl, n_bad))
+            m = kw_re.match(unicode(e))
+            if m:
+                (kw,)  = m.groups()
+                raise WrongArgumentsError(handler,
+                        u'got an unexpected keyword argument %s' % kw)
+            raise
 
 
 if __name__ == '__main__':
 
 
 if __name__ == '__main__':
@@ -501,6 +550,12 @@ if __name__ == '__main__':
     assert p == ([u'=hello'], {}), p
     p = parse_command(r'\thello')
     assert p == ([u'\thello'], {}), p
     assert p == ([u'=hello'], {}), p
     p = parse_command(r'\thello')
     assert p == ([u'\thello'], {}), p
+    p = parse_command(r'hello \n')
+    assert p == ([u'hello', u'\n'], {}), p
+    p = parse_command(r'hello \nmundo')
+    assert p == ([u'hello', u'\nmundo'], {}), p
+    p = parse_command(r'test \N')
+    assert p == ([u'test', None], {}), p
     p = parse_command(r'\N')
     assert p == ([None], {}), p
     p = parse_command(r'none=\N')
     p = parse_command(r'\N')
     assert p == ([None], {}), p
     p = parse_command(r'none=\N')