summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin Van Brunt <kmvanbrunt@gmail.com>2018-10-09 18:10:55 -0400
committerKevin Van Brunt <kmvanbrunt@gmail.com>2018-10-09 18:10:55 -0400
commit9b1bfab6e2a908d119e46bcc70e98d94739ab4a4 (patch)
treeede6d98da79a3ea2bebcbea03a468df449a38104
parentad3ca3263e2a3552b010d17458a51697f405720f (diff)
downloadcmd2-git-9b1bfab6e2a908d119e46bcc70e98d94739ab4a4.tar.gz
Added ability for argcompleter to determine difference between flag and negative number
-rwxr-xr-xcmd2/argparse_completer.py39
-rw-r--r--cmd2/pyscript_bridge.py13
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)