summaryrefslogtreecommitdiff
path: root/cmd2
diff options
context:
space:
mode:
authorKevin Van Brunt <kmvanbrunt@gmail.com>2019-09-27 16:30:39 -0400
committerKevin Van Brunt <kmvanbrunt@gmail.com>2019-09-27 16:30:39 -0400
commit8043b56ecbffa30139b4c540c9bea77b642bafea (patch)
tree016f04e93e2f8c4fd7f7370f68404f3db8e1e76e /cmd2
parent29b52522300cea16dd9961667a5c7c055d4e3056 (diff)
downloadcmd2-git-8043b56ecbffa30139b4c540c9bea77b642bafea.tar.gz
AutoCompleter now handles mutually exclusive groups
Diffstat (limited to 'cmd2')
-rw-r--r--cmd2/argparse_completer.py51
-rw-r--r--[-rwxr-xr-x]cmd2/cmd2.py2
2 files changed, 47 insertions, 6 deletions
diff --git a/cmd2/argparse_completer.py b/cmd2/argparse_completer.py
index 04dfa00e..5fab8abe 100644
--- a/cmd2/argparse_completer.py
+++ b/cmd2/argparse_completer.py
@@ -120,7 +120,6 @@ class AutoCompleter(object):
self._flags = [] # all flags in this command
self._flag_to_action = {} # maps flags to the argparse action object
self._positional_actions = [] # actions for positional arguments (by position index)
- self._mutually_exclusive_groups = [] # Each item is a list of actions
self._subcommand_action = None # this will be set if self._parser has subcommands
# Start digging through the argparse structures.
@@ -140,10 +139,6 @@ class AutoCompleter(object):
if isinstance(action, argparse._SubParsersAction):
self._subcommand_action = action
- # Keep track of what actions are in mutually exclusive groups
- for group in self._parser._mutually_exclusive_groups:
- self._mutually_exclusive_groups.append(group._group_actions)
-
def complete_command(self, tokens: List[str], text: str, line: str, begidx: int, endidx: int) -> List[str]:
"""Complete the command using the argparse metadata and provided argument dictionary"""
if not tokens:
@@ -168,12 +163,52 @@ class AutoCompleter(object):
# Keeps track of arguments we've seen and any tokens they consumed
consumed_arg_values = dict() # dict(arg_name -> List[tokens])
+ # Completed mutually exclusive groups
+ completed_mutex_groups = dict() # dict(argparse._MutuallyExclusiveGroup -> Action which completed group)
+
def consume_argument(arg_state: AutoCompleter._ArgumentState) -> None:
"""Consuming token as an argument"""
arg_state.count += 1
consumed_arg_values.setdefault(arg_state.action.dest, [])
consumed_arg_values[arg_state.action.dest].append(token)
+ def update_mutex_groups(arg_action: argparse.Action) -> bool:
+ """
+ Check if an argument belongs to a mutually exclusive group and either mark that group
+ as complete or print an error if the group has already been completed
+ :param arg_action: the action of the argument
+ :return: False if the group has already been completed and there is a conflict, otherwise True
+ """
+ # Check if this action is in a mutually exclusive group
+ for group in self._parser._mutually_exclusive_groups:
+ if arg_action in group._group_actions:
+
+ # Check if the group this action belongs to has already been completed
+ if group in completed_mutex_groups:
+ group_action = completed_mutex_groups[group]
+ error = style_error("\nError: argument {}: not allowed with argument {}\n".
+ format(argparse._get_action_name(arg_action),
+ argparse._get_action_name(group_action)))
+ self._print_message(error)
+ return False
+
+ # Mark that this action completed the group
+ completed_mutex_groups[group] = arg_action
+
+ # Don't tab complete any of the other args in the group
+ for group_action in group._group_actions:
+ if group_action == arg_action:
+ continue
+ elif group_action in self._flag_to_action.values():
+ matched_flags.extend(group_action.option_strings)
+ elif group_action in remaining_positionals:
+ remaining_positionals.remove(group_action)
+
+ # Arg can only be in one group, so we are done
+ break
+
+ return True
+
#############################################################################################
# Parse all but the last token
#############################################################################################
@@ -227,6 +262,9 @@ class AutoCompleter(object):
action = self._flag_to_action[candidates_flags[0]]
if action is not None:
+ if not update_mutex_groups(action):
+ return []
+
if isinstance(action, (argparse._AppendAction,
argparse._AppendConstAction,
argparse._CountAction)):
@@ -287,6 +325,9 @@ class AutoCompleter(object):
# Check if we have a positional to consume this token
if pos_arg_state is not None:
+ if not update_mutex_groups(pos_arg_state.action):
+ return []
+
consume_argument(pos_arg_state)
# No more flags are allowed if this is a REMAINDER argument
diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py
index 50d562b4..5e1f9a72 100755..100644
--- a/cmd2/cmd2.py
+++ b/cmd2/cmd2.py
@@ -1848,7 +1848,7 @@ class Cmd(cmd.Cmd):
"""Keep accepting lines of input until the command is complete.
There is some pretty hacky code here to handle some quirks of
- self.pseudo_raw_input(). It returns a literal 'eof' if the input
+ self._pseudo_raw_input(). It returns a literal 'eof' if the input
pipe runs out. We can't refactor it because we need to retain
backwards compatibility with the standard library version of cmd.