summaryrefslogtreecommitdiff
path: root/cmd2/argparse_completer.py
diff options
context:
space:
mode:
authorTodd Leonhardt <todd.leonhardt@gmail.com>2021-02-19 21:35:13 -0500
committerTodd Leonhardt <todd.leonhardt@gmail.com>2021-02-19 21:35:13 -0500
commit3e180a810e9c4b9d251c135667d1d150b0bbd0dd (patch)
tree03e49d5da86d40efa9118eccfd8bd4bbf3dcf86b /cmd2/argparse_completer.py
parent4c70bdb03d34c43f833bf77c441452cd402d0715 (diff)
parent06aaf962689840631325c70ea7e9056d176c7f67 (diff)
downloadcmd2-git-3e180a810e9c4b9d251c135667d1d150b0bbd0dd.tar.gz
Merge branch 'master' into black
# Conflicts: # cmd2/__init__.py # cmd2/argparse_completer.py # cmd2/argparse_custom.py # cmd2/cmd2.py # cmd2/decorators.py # cmd2/exceptions.py # cmd2/utils.py # examples/arg_decorators.py # examples/argparse_completion.py # examples/modular_commands_main.py # tests/test_argparse_completer.py # tests/test_argparse_custom.py # tests/test_cmd2.py # tests/test_completion.py # tests/test_history.py
Diffstat (limited to 'cmd2/argparse_completer.py')
-rw-r--r--cmd2/argparse_completer.py96
1 files changed, 45 insertions, 51 deletions
diff --git a/cmd2/argparse_completer.py b/cmd2/argparse_completer.py
index 88cd2938..99378733 100644
--- a/cmd2/argparse_completer.py
+++ b/cmd2/argparse_completer.py
@@ -37,14 +37,13 @@ from .argparse_custom import (
from .command_definition import (
CommandSet,
)
+from .exceptions import (
+ CompletionError,
+)
from .table_creator import (
Column,
SimpleTable,
)
-from .utils import (
- CompletionError,
- basic_complete,
-)
# If no descriptive header is supplied, then this will be used instead
DEFAULT_DESCRIPTIVE_HEADER = 'Description'
@@ -209,11 +208,19 @@ class ArgparseCompleter:
if isinstance(action, argparse._SubParsersAction):
self._subcommand_action = action
- def complete_command(
- self, tokens: List[str], text: str, line: str, begidx: int, endidx: int, *, cmd_set: Optional[CommandSet] = None
- ) -> List[str]:
+ def complete(self, text: str, line: str, begidx: int, endidx: int, tokens: List[str], *,
+ cmd_set: Optional[CommandSet] = None) -> List[str]:
"""
- Complete the command using the argparse metadata and provided argument dictionary
+ Complete text using argparse metadata
+
+ :param text: the string prefix we are attempting to match (all matches must begin with it)
+ :param line: the current input line with leading whitespace removed
+ :param begidx: the beginning index of the prefix text
+ :param endidx: the ending index of the prefix text
+ :param tokens: list of argument tokens being passed to the parser
+ :param cmd_set: if tab completing a command, the CommandSet the command's function belongs to, if applicable.
+ Defaults to None.
+
:raises: CompletionError for various types of tab completion errors
"""
if not tokens:
@@ -290,7 +297,7 @@ class ArgparseCompleter:
#############################################################################################
# Parse all but the last token
#############################################################################################
- for token_index, token in enumerate(tokens[1:-1], start=1):
+ for token_index, token in enumerate(tokens[:-1]):
# If we're in a positional REMAINDER arg, force all future tokens to go to that
if pos_arg_state is not None and pos_arg_state.is_remainder:
@@ -384,12 +391,10 @@ class ArgparseCompleter:
if action.dest != argparse.SUPPRESS:
parent_tokens[action.dest] = [token]
- completer = ArgparseCompleter(
- self._subcommand_action.choices[token], self._cmd2_app, parent_tokens=parent_tokens
- )
- return completer.complete_command(
- tokens[token_index:], text, line, begidx, endidx, cmd_set=cmd_set
- )
+ completer = ArgparseCompleter(self._subcommand_action.choices[token], self._cmd2_app,
+ parent_tokens=parent_tokens)
+ return completer.complete(text, line, begidx, endidx, tokens[token_index + 1:],
+ cmd_set=cmd_set)
else:
# Invalid subcommand entered, so no way to complete remaining tokens
return []
@@ -433,9 +438,8 @@ class ArgparseCompleter:
# Check if we are completing a flag's argument
if flag_arg_state is not None:
- completion_results = self._complete_for_arg(
- flag_arg_state, text, line, begidx, endidx, consumed_arg_values, cmd_set=cmd_set
- )
+ completion_results = self._complete_arg(text, line, begidx, endidx, flag_arg_state, consumed_arg_values,
+ cmd_set=cmd_set)
# If we have results, then return them
if completion_results:
@@ -460,9 +464,8 @@ class ArgparseCompleter:
action = remaining_positionals.popleft()
pos_arg_state = _ArgumentState(action)
- completion_results = self._complete_for_arg(
- pos_arg_state, text, line, begidx, endidx, consumed_arg_values, cmd_set=cmd_set
- )
+ completion_results = self._complete_arg(text, line, begidx, endidx, pos_arg_state, consumed_arg_values,
+ cmd_set=cmd_set)
# If we have results, then return them
if completion_results:
@@ -496,7 +499,7 @@ class ArgparseCompleter:
if action.help != argparse.SUPPRESS:
match_against.append(flag)
- matches = basic_complete(text, line, begidx, endidx, match_against)
+ matches = self._cmd2_app.basic_complete(text, line, begidx, endidx, match_against)
# Build a dictionary linking actions with their matched flag names
matched_actions = dict() # type: Dict[argparse.Action, List[str]]
@@ -568,26 +571,26 @@ class ArgparseCompleter:
return completions
- def complete_subcommand_help(self, tokens: List[str], text: str, line: str, begidx: int, endidx: int) -> List[str]:
+ def complete_subcommand_help(self, text: str, line: str, begidx: int, endidx: int, tokens: List[str]) -> List[str]:
"""
Supports cmd2's help command in the completion of subcommand names
- :param tokens: command line tokens
:param text: the string prefix we are attempting to match (all matches must begin with it)
:param line: the current input line with leading whitespace removed
:param begidx: the beginning index of the prefix text
:param endidx: the ending index of the prefix text
+ :param tokens: arguments passed to command/subcommand
:return: List of subcommand completions
"""
# If our parser has subcommands, we must examine the tokens and check if they are subcommands
# If so, we will let the subcommand's parser handle the rest of the tokens via another ArgparseCompleter.
if self._subcommand_action is not None:
- for token_index, token in enumerate(tokens[1:], start=1):
+ for token_index, token in enumerate(tokens):
if token in self._subcommand_action.choices:
completer = ArgparseCompleter(self._subcommand_action.choices[token], self._cmd2_app)
- return completer.complete_subcommand_help(tokens[token_index:], text, line, begidx, endidx)
+ return completer.complete_subcommand_help(text, line, begidx, endidx, tokens[token_index + 1:])
elif token_index == len(tokens) - 1:
# Since this is the last token, we will attempt to complete it
- return basic_complete(text, line, begidx, endidx, self._subcommand_action.choices)
+ return self._cmd2_app.basic_complete(text, line, begidx, endidx, self._subcommand_action.choices)
else:
break
return []
@@ -595,31 +598,23 @@ class ArgparseCompleter:
def format_help(self, tokens: List[str]) -> str:
"""
Supports cmd2's help command in the retrieval of help text
- :param tokens: command line tokens
+ :param tokens: arguments passed to help command
:return: help text of the command being queried
"""
# If our parser has subcommands, we must examine the tokens and check if they are subcommands
# If so, we will let the subcommand's parser handle the rest of the tokens via another ArgparseCompleter.
if self._subcommand_action is not None:
- for token_index, token in enumerate(tokens[1:], start=1):
+ for token_index, token in enumerate(tokens):
if token in self._subcommand_action.choices:
completer = ArgparseCompleter(self._subcommand_action.choices[token], self._cmd2_app)
- return completer.format_help(tokens[token_index:])
+ return completer.format_help(tokens[token_index + 1:])
else:
break
return self._parser.format_help()
- def _complete_for_arg(
- self,
- arg_state: _ArgumentState,
- text: str,
- line: str,
- begidx: int,
- endidx: int,
- consumed_arg_values: Dict[str, List[str]],
- *,
- cmd_set: Optional[CommandSet] = None
- ) -> List[str]:
+ def _complete_arg(self, text: str, line: str, begidx: int, endidx: int,
+ arg_state: _ArgumentState, consumed_arg_values: Dict[str, List[str]], *,
+ cmd_set: Optional[CommandSet] = None) -> List[str]:
"""
Tab completion routine for an argparse argument
:return: list of completions
@@ -647,16 +642,15 @@ class ArgparseCompleter:
args = []
kwargs = {}
if isinstance(arg_choices, ChoicesCallable):
- if arg_choices.is_method:
- # The completer may or may not be defined in the same class as the command. Since completer
- # functions are registered with the command argparser before anything is instantiated, we
- # need to find an instance at runtime that matches the types during declaration
- cmd_set = self._cmd2_app._resolve_func_self(arg_choices.to_call, cmd_set)
- if cmd_set is None:
- # No cases matched, raise an error
- raise CompletionError('Could not find CommandSet instance matching defining type for completer')
+ # The completer may or may not be defined in the same class as the command. Since completer
+ # functions are registered with the command argparser before anything is instantiated, we
+ # need to find an instance at runtime that matches the types during declaration
+ self_arg = self._cmd2_app._resolve_func_self(arg_choices.to_call, cmd_set)
+ if self_arg is None:
+ # No cases matched, raise an error
+ raise CompletionError('Could not find CommandSet instance matching defining type for completer')
- args.append(cmd_set)
+ args.append(self_arg)
# Check if arg_choices.to_call expects arg_tokens
to_call_params = inspect.signature(arg_choices.to_call).parameters
@@ -687,7 +681,7 @@ class ArgparseCompleter:
arg_choices = [choice for choice in arg_choices if choice not in used_values]
# Do tab completion on the choices
- results = basic_complete(text, line, begidx, endidx, arg_choices)
+ results = self._cmd2_app.basic_complete(text, line, begidx, endidx, arg_choices)
if not results:
# Reset the value for matches_sorted. This is because completion of flag names