diff options
author | Eric Lin <anselor@gmail.com> | 2018-10-06 17:17:17 +0000 |
---|---|---|
committer | Eric Lin <anselor@gmail.com> | 2018-10-06 17:17:17 +0000 |
commit | 96bcfe012b3b1659dbd0244ac6c4a43dfb5328d6 (patch) | |
tree | 53aa46ab12fe2644023ff4ffa22b8beccce086c2 /cmd2/pyscript_bridge.py | |
parent | 49cbec9969b4b53248d6097d8f395d92c74f7228 (diff) | |
download | cmd2-git-96bcfe012b3b1659dbd0244ac6c4a43dfb5328d6.tar.gz |
Added handling of nargs=argparse.REMAINDER in both AutoCompleter and ArgparseFunctor
Should correctly force all subsequent arguments to go to the REMAINDER argument once it is detected.
Re-arranged the command generation in ArgparseFunctor to print flag arguments before positionals
Also forces the remainder arguments to always be last.
Diffstat (limited to 'cmd2/pyscript_bridge.py')
-rw-r--r-- | cmd2/pyscript_bridge.py | 65 |
1 files changed, 49 insertions, 16 deletions
diff --git a/cmd2/pyscript_bridge.py b/cmd2/pyscript_bridge.py index a70a7ae6..7920e1be 100644 --- a/cmd2/pyscript_bridge.py +++ b/cmd2/pyscript_bridge.py @@ -75,6 +75,10 @@ class ArgparseFunctor: # Dictionary mapping command argument name to value self._args = {} + # tag the argument that's a remainder type + self._remainder_arg = None + # separately track flag arguments so they will be printed before positionals + self._flag_args = [] # argparse object for the current command layer self.__current_subcommand_parser = parser @@ -109,7 +113,6 @@ class ArgparseFunctor: next_pos_index = 0 has_subcommand = False - consumed_kw = [] # Iterate through the current sub-command's arguments in order for action in self.__current_subcommand_parser._actions: @@ -118,7 +121,7 @@ class ArgparseFunctor: # this is a flag argument, search for the argument by name in the parameters if action.dest in kwargs: self._args[action.dest] = kwargs[action.dest] - consumed_kw.append(action.dest) + self._flag_args.append(action.dest) else: # This is a positional argument, search the positional arguments passed in. if not isinstance(action, argparse._SubParsersAction): @@ -157,6 +160,10 @@ class ArgparseFunctor: elif action.nargs == '*': self._args[action.dest] = args[next_pos_index:next_pos_index + pos_remain] next_pos_index += pos_remain + elif action.nargs == argparse.REMAINDER: + self._args[action.dest] = args[next_pos_index:next_pos_index + pos_remain] + next_pos_index += pos_remain + self._remainder_arg = action.dest elif action.nargs == '?': self._args[action.dest] = args[next_pos_index] next_pos_index += 1 @@ -168,7 +175,7 @@ class ArgparseFunctor: # Check if there are any extra arguments we don't know how to handle for kw in kwargs: - if kw not in self._args: # consumed_kw: + if kw not in self._args: raise TypeError("{}() got an unexpected keyword argument '{}'".format( self.__current_subcommand_parser.prog, kw)) @@ -214,27 +221,53 @@ class ArgparseFunctor: if ' ' in item: item = '"{}"'.format(item) cmd_str[0] += '{} '.format(item) + + # If this is a flag parameter that can accept a variable number of arguments and we have not + # reached the max number, add a list completion suffix to tell argparse to move to the next + # parameter + if action.option_strings and isinstance(action, _RangeAction) \ + and action.nargs_max > len(value): + cmd_str[0] += '{0}{0} '.format(self._parser.prefix_chars[0]) + else: value = str(value).strip() if ' ' in value: value = '"{}"'.format(value) cmd_str[0] += '{} '.format(value) + # If this is a flag parameter that can accept a variable number of arguments and we have not + # reached the max number, add a list completion suffix to tell argparse to move to the next + # parameter + if action.option_strings and isinstance(action, _RangeAction) \ + and action.nargs_max > 1: + cmd_str[0] += '{0}{0} '.format(self._parser.prefix_chars[0]) + + def process_action(action): + if isinstance(action, argparse._SubParsersAction): + cmd_str[0] += '{} '.format(self._args[action.dest]) + traverse_parser(action.choices[self._args[action.dest]]) + elif isinstance(action, argparse._AppendAction): + if isinstance(self._args[action.dest], list) or isinstance(self._args[action.dest], tuple): + for values in self._args[action.dest]: + process_flag(action, values) + else: + process_flag(action, self._args[action.dest]) + else: + process_flag(action, self._args[action.dest]) + def traverse_parser(parser): + # first process optional flag arguments for action in parser._actions: - # was something provided for the argument - if action.dest in self._args: - if isinstance(action, argparse._SubParsersAction): - cmd_str[0] += '{} '.format(self._args[action.dest]) - traverse_parser(action.choices[self._args[action.dest]]) - elif isinstance(action, argparse._AppendAction): - if isinstance(self._args[action.dest], list) or isinstance(self._args[action.dest], tuple): - for values in self._args[action.dest]: - process_flag(action, values) - else: - process_flag(action, self._args[action.dest]) - else: - process_flag(action, self._args[action.dest]) + if action.dest in self._args and action.dest in self._flag_args and action.dest != self._remainder_arg: + process_action(action) + # next process positional arguments + for action in parser._actions: + if action.dest in self._args and action.dest not in self._flag_args and action.dest != self._remainder_arg: + process_action(action) + # Keep remainder argument last + for action in parser._actions: + if action.dest in self._args and action.dest == self._remainder_arg: + process_action(action) traverse_parser(self._parser) |