diff options
author | Kevin Van Brunt <kmvanbrunt@gmail.com> | 2018-10-09 18:10:55 -0400 |
---|---|---|
committer | Kevin Van Brunt <kmvanbrunt@gmail.com> | 2018-10-09 18:10:55 -0400 |
commit | 9b1bfab6e2a908d119e46bcc70e98d94739ab4a4 (patch) | |
tree | ede6d98da79a3ea2bebcbea03a468df449a38104 /cmd2 | |
parent | ad3ca3263e2a3552b010d17458a51697f405720f (diff) | |
download | cmd2-git-9b1bfab6e2a908d119e46bcc70e98d94739ab4a4.tar.gz |
Added ability for argcompleter to determine difference between flag and negative number
Diffstat (limited to 'cmd2')
-rwxr-xr-x | cmd2/argparse_completer.py | 39 | ||||
-rw-r--r-- | cmd2/pyscript_bridge.py | 13 |
2 files changed, 37 insertions, 15 deletions
diff --git a/cmd2/argparse_completer.py b/cmd2/argparse_completer.py index 2cd2d1c6..f3438bed 100755 --- a/cmd2/argparse_completer.py +++ b/cmd2/argparse_completer.py @@ -209,6 +209,33 @@ def register_custom_actions(parser: argparse.ArgumentParser) -> None: parser.register('action', 'append', _AppendRangeAction) +def token_resembles_flag(token: str, parser: argparse.ArgumentParser): + """Determine if a token looks like a flag. Based on argparse._parse_optional().""" + # if it's an empty string, it was meant to be a positional + if not token: + return False + + # if it doesn't start with a prefix, it was meant to be positional + if not token[0] in parser.prefix_chars: + return False + + # if it's just a single character, it was meant to be positional + if len(token) == 1: + return False + + # if it looks like a negative number, it was meant to be positional + # unless there are negative-number-like options + if parser._negative_number_matcher.match(token): + if not parser._has_negative_number_optionals: + return False + + # if it contains a space, it was meant to be a positional + if ' ' in token: + return False + + # Looks like a flag + return True + class AutoCompleter(object): """Automatically command line tab completion based on argparse parameters""" @@ -334,9 +361,8 @@ class AutoCompleter(object): def consume_flag_argument() -> None: """Consuming token as a flag argument""" # we're consuming flag arguments - # if this is not empty and is not another potential flag, count towards flag arguments - # if the token is a single character, it doesn't matter whether it matches a flag prefix - if token and (len(token) == 1 or token[0] not in self._parser.prefix_chars) and flag_action is not None: + # if the token does not look like a new flag, then count towards flag arguments + if not token_resembles_flag(token, self._parser) and flag_action is not None: flag_arg.count += 1 # does this complete a option item for the flag @@ -431,12 +457,7 @@ class AutoCompleter(object): skip_flag = True # At this point we're no longer consuming flag arguments. Is the current argument a potential flag? - # If the argument is the start of a flag and this is the last token, we proceed forward to try - # and match against our known flags. - # If this argument is not the last token and the argument is exactly a flag prefix, then this - # token should be consumed as an argument to a prior flag or positional argument. - if len(token) > 0 and token[0] in self._parser.prefix_chars and not skip_flag and \ - (is_last_token or token not in self._parser.prefix_chars): + if token_resembles_flag(token, self._parser) and not skip_flag: # reset some tracking values flag_arg.reset() # don't reset positional tracking because flags can be interspersed anywhere between positionals diff --git a/cmd2/pyscript_bridge.py b/cmd2/pyscript_bridge.py index 037c7a77..11a2cbb3 100644 --- a/cmd2/pyscript_bridge.py +++ b/cmd2/pyscript_bridge.py @@ -12,7 +12,7 @@ import functools import sys from typing import List, Callable, Optional -from .argparse_completer import _RangeAction +from .argparse_completer import _RangeAction, token_resembles_flag from .utils import namedtuple_with_defaults, StdSim, quote_string_if_needed # Python 3.4 require contextlib2 for temporarily redirecting stderr and stdout @@ -225,9 +225,9 @@ class ArgparseFunctor: if isinstance(value, List) or isinstance(value, tuple): for item in value: item = str(item).strip() - if self._parser._parse_optional(item) is not None: - raise ValueError('Value provided for {} ({}) appears to be an optional'. - format(action.dest, item)) + if token_resembles_flag(item, self._parser): + raise ValueError('{} appears to be a flag and should be supplied as a keyword argument ' + 'to the function.'.format(item)) item = quote_string_if_needed(item) cmd_str[0] += '{} '.format(item) @@ -240,8 +240,9 @@ class ArgparseFunctor: else: value = str(value).strip() - if self._parser._parse_optional(value) is not None: - raise ValueError('Value provided for {} ({}) appears to be an optional'.format(action.dest, value)) + if token_resembles_flag(value, self._parser): + raise ValueError('{} appears to be a flag and should be supplied as a keyword argument ' + 'to the function.'.format(value)) value = quote_string_if_needed(value) cmd_str[0] += '{} '.format(value) |