changeset f31ac80a0b5a in /home/hg/repos/gajim
details:http://hg.gajim.org/gajim?cmd=changeset;node=f31ac80a0b5a description: Enhanced command arguments parser/adapter
diffstat:
src/commands/framework.py | 97 ++++++++++++++++++++++++++++++++++-------------- 1 files changed, 68 insertions(+), 29 deletions(-)
diffs (177 lines):
diff -r 938c39c32b42 -r f31ac80a0b5a src/commands/framework.py --- a/src/commands/framework.py Tue Sep 15 17:46:02 2009 +0300 +++ b/src/commands/framework.py Tue Sep 15 22:57:01 2009 +0300 @@ -19,8 +19,9 @@ """
import re -from types import FunctionType, UnicodeType, TupleType, ListType +from types import FunctionType, UnicodeType, TupleType, ListType, BooleanType from inspect import getargspec +from operator import itemgetter
class CommandInternalError(Exception): pass @@ -59,8 +60,13 @@ try: return self.handler(*args, **kwargs) except CommandError, exception: + # Re-raise an excepttion with a proper command attribute set, + # unless it is already set by the one who raised an exception. if not exception.command and not exception.name: raise CommandError(exception.message, self) + # This one is a little bit too wide, but as Python does not have + # anything more constrained - there is no other choice. Take a look here + # if command complains about invalid arguments while they are ok. except TypeError: raise CommandError("Command received invalid arguments", self)
@@ -136,13 +142,15 @@
for key, value in spec_kwargs: letter = key[0] + key = key.replace('_', '-') + value = ('=%s' % value) if not isinstance(value, BooleanType) else str()
if letter not in letters: - kwargs.append('-(-%s)%s=%s' % (letter, key[1:], value)) + kwargs.append('-(-%s)%s%s' % (letter, key[1:], value)) letters.append(letter) else: - kwargs.append('--%s=%s' % (key, value)) + kwargs.append('--%s%s' % (key, value))
usage = str() args = str() @@ -371,9 +379,10 @@ def parse_command_arguments(cls, arguments): """ Simple yet effective and sufficient in most cases parser which parses - command arguments and returns them as two lists, first representing - positional arguments, and second representing options as (key, value) - tuples. + command arguments and returns them as two lists. First represents + positional arguments as (argument, position), and second representing + options as (key, value, position) tuples, where position is a (start, + end) span tuple of where it was found in the string.
The format of the input arguments should be: <arg1, arg2> <<extra>> [-(-o)ption=value1, -(-a)nother=value2] [[extra_options]] @@ -386,27 +395,36 @@ """ args, opts = [], []
- # Need to store position of every option we have parsed in order to get - # arguments to be parsed correct later. - opt_positions = [] + def intersects_opts((given_start, given_end)): + """ + Check if something intersects with boundaries of any parsed option. + """ + for key, value, (start, end) in opts: + if given_start >= start and given_end <= end: + return True + return False
- def intersects((given_start, given_end)): + def intersects_args((given_start, given_end)): """ - Check if something intersects with boundaries of any parsed options. + Check if something intersects with boundaries of any parsed argument. """ - for start, end in opt_positions: - if given_start == start or given_end == end: + for arg, (start, end) in args: + if given_start >= start and given_end <= end: return True return False
for match in re.finditer(cls.OPT_PATTERN, arguments): if match: - opt_positions.append(match.span()) - opts.append((match.group('key'), match.group('value') or True)) + key = match.group('key') + value = match.group('value') or None + position = match.span() + opts.append((key, value, position))
for match in re.finditer(cls.ARG_PATTERN, arguments): - if match and not intersects(match.span()): - args.append(match.group('body')) + if match and not intersects_opts(match.span()): + body = match.group('body') + position = match.span() + args.append((body, position))
return args, opts
@@ -423,17 +441,49 @@
Dashes (-) in the option names will be converted to underscores. So you can map --one-more-option to a one_more_option=None. + + If initial value of a keyword argument is a boolean (False in most + cases) then this option will be treated as a switch, that is an option + which does not take an argument. Argument preceded by a switch will be + treated just like a normal positional argument. """ spec_args, spec_kwargs, var_args, var_kwargs = command.extract_arg_spec() spec_kwargs = dict(spec_kwargs)
if command.raw: - if len(spec_args) == 1: + if len(spec_args) == 1 and not spec_kwargs and not var_args and not var_kwargs: if arguments or command.empty: return (arguments,), {} raise CommandError("Can not be used without arguments", command) raise CommandInternalError("Raw command must define no more then one argument")
+ if command.expand_short: + expanded = [] + for spec_key, spec_value in spec_kwargs.iteritems(): + letter = spec_key[0] if len(spec_key) > 1 else None + if letter and letter not in expanded: + for index, (key, value, position) in enumerate(opts): + if key == letter: + expanded.append(letter) + opts[index] = (spec_key, value, position) + break + + for index, (key, value, position) in enumerate(opts): + if isinstance(spec_kwargs.get(key), BooleanType): + opts[index] = (key, True, position) + if value: + args.append((value, position)) + + # Sorting arguments and options (just to be sure) in regarding to their + # positions in the string. + args.sort(key=itemgetter(1)) + opts.sort(key=itemgetter(2)) + + # Stripping down position information supplied with arguments and options as it + # won't be needed again. + args = map(lambda (arg, position): arg, args) + opts = map(lambda (key, value, position): (key, value), opts) + if command.extra: if not var_args: positional_len = len(spec_args) - (1 if not command.source else 2) @@ -443,17 +493,6 @@ else: raise CommandInternalError("Can not have both, extra and *args")
- if command.expand_short: - expanded = [] - for spec_key, spec_value in spec_kwargs.iteritems(): - letter = spec_key[0] if len(spec_key) > 1 else None - if letter and letter not in expanded: - for index, (key, value) in enumerate(opts): - if key == letter: - expanded.append(letter) - opts[index] = (spec_key, value) - break - for index, (key, value) in enumerate(opts): if '-' in key: opts[index] = (key.replace('-', '_'), value)