X-Git-Url: https://git.llucax.com/software/pymin.git/blobdiff_plain/7d2f3da6aee023c991c9669a6a4664bd8be53a97..3eefa8b40b56992b0c96cf085ef9a6059b7c7fc4:/pymin/dispatcher.py diff --git a/pymin/dispatcher.py b/pymin/dispatcher.py index 39b4b75..10e7fb8 100644 --- a/pymin/dispatcher.py +++ b/pymin/dispatcher.py @@ -7,6 +7,8 @@ It's based on Zope or Cherrypy dispatching (but implemented from the scratch) and translates commands to functions/objects/methods. """ +import re + __ALL__ = ('Error', 'HandlerError', 'CommandNotFoundError', 'Handler', 'Dispatcher', 'handler', 'is_handler', 'get_help') @@ -52,6 +54,19 @@ class CommandError(Error): def __unicode__(self): return u'Error in command "%s".' % u' '.join(self.command) +class WrongArgumentsError(CommandError): + r"""WrongArgumentsError() -> WrongArgumentsError instance. + + This exception is raised when an empty command string is received. + """ + + def __init__(self, message): + r"Initialize the object, see class documentation for more info." + self.message = message + + def __unicode__(self): + return self.message + class CommandNotSpecifiedError(CommandError): r"""CommandNotSpecifiedError() -> CommandNotSpecifiedError instance. @@ -223,6 +238,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). + The command should be a unicode string. + Examples: >>> parse_command('hello world') @@ -250,6 +267,12 @@ def parse_command(command): ([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') @@ -274,9 +297,23 @@ def parse_command(command): 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: + # 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'"') @@ -301,14 +338,7 @@ def parse_command(command): 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: @@ -350,14 +380,11 @@ def parse_command(command): 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) +args_re = re.compile(r'\w+\(\) takes (.+) (\d+) \w+ \((\d+) given\)') + class Dispatcher: r"""Dispatcher([root]) -> Dispatcher instance :: Command dispatcher. @@ -419,7 +446,23 @@ class Dispatcher: 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( + u'Command "%s" takes %s %s argument%s, %s given.' + % (handler.__name__, quant, n_ok, pl, n_bad)) + raise if __name__ == '__main__': @@ -501,6 +544,12 @@ if __name__ == '__main__': 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')