From e1808a8455aadc9fac300c68e397b712a030ae29 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Tue, 25 Nov 2014 16:58:26 +0100 Subject: Parse command arguments using a decorator and make things more consistent Avoid surprises with some commands accepting quoted arguments and some other not. fix #2555 --- src/decorators.py | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) (limited to 'src/decorators.py') diff --git a/src/decorators.py b/src/decorators.py index 251d8749..627d8f59 100644 --- a/src/decorators.py +++ b/src/decorators.py @@ -2,6 +2,8 @@ Module containing various decorators """ +import common + class RefreshWrapper(object): def __init__(self): self.core = None @@ -41,3 +43,93 @@ class RefreshWrapper(object): return wrap refresh_wrapper = RefreshWrapper() + +class CommandArgParser(object): + """Modify the string argument of the function into a list of strings + containing the right number of extracted arguments, or None if we don’t + have enough. + """ + @staticmethod + def raw(func): + """Just call the function with a single string, which is the original string + untouched + """ + def wrap(self, args, *a, **kw): + return func(self, args, *a, **kw) + return wrap + + @staticmethod + def ignored(func): + """ + Call the function without any argument + """ + def wrap(self, args, *a, **kw): + return func(self, *a, **kw) + return wrap + + @staticmethod + def quoted(mandatory, optional=0, defaults=[], + ignore_trailing_arguments=False): + + """The function receives a list with a number of arguments that is between + the numbers `mandatory` and `optional`. + + If the string doesn’t contain at least `mandatory` arguments, we return + None because the given arguments are invalid. + + If there are any remaining arguments after `mandatory` and `optional` + arguments have been found (and “ignore_trailing_arguments" is not True), + we happen them to the last argument of the list. + + An argument is a string (with or without whitespaces) between to quotes + ("), or a whitespace separated word (if not inside quotes). + + The argument `defaults` is a list of strings that are used when an + optional argument is missing. For example if we accept one optional + argument, zero is available but we have one value in the `defaults` + list, we use that string inplace. The `defaults` list can only + replace missing optional arguments, not mandatory ones. And it + should not contain more than `mandatory` values. Also you cannot + + Example: + This method needs at least one argument, and accepts up to 3 + arguments + + >> @command_args_parser.quoted(1, 2, ['default for first arg'], False) + >> def f(args): + >> print(args) + + >> f('coucou les amis') # We have one mandatory and two optional + ['coucou', 'les', 'amis'] + >> f('"coucou les amis" "PROUT PROUT"') # One mandator and only one optional, + # no default for the second + ['coucou les amis', 'PROUT PROUT'] + >> f('') # Not enough args for mandatory number + None + >> f('"coucou les potes"') # One mandatory, and use the default value + # for the first optional + ['coucou les potes, 'default for first arg'] + >> f('"un et demi" deux trois quatre cinq six') # We have three trailing arguments + ['un et demi', 'deux', 'trois quatre cinq six'] + + """ + def first(func): + def second(self, args, *a, **kw): + default_args = defaults + args = common.shell_split(args) + if len(args) < mandatory: + return func(self, None, *a, **kw) + res, args = args[:mandatory], args[mandatory:] + opt_args = args[:optional] + if opt_args: + res += opt_args + args = args[len(opt_args):] + default_args = default_args[len(opt_args):] + res += default_args + if args and res and not ignore_trailing_arguments: + res[-1] += " " + " ".join(args) + return func(self, res, *a, **kw) + return second + return first + +command_args_parser = CommandArgParser() -- cgit v1.2.3