summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin Van Brunt <kmvanbrunt@gmail.com>2019-08-22 13:22:53 -0400
committerKevin Van Brunt <kmvanbrunt@gmail.com>2019-08-22 13:22:53 -0400
commit6af36b1753e0f70597def2210bd2b1279a44c188 (patch)
treed969ba525dc6040eeeef154809f2342be4a2b329
parentf1bf0ae07769630977f803ef685ce41d68cdfe6a (diff)
downloadcmd2-git-6af36b1753e0f70597def2210bd2b1279a44c188.tar.gz
AutoCompleter now passes parsed_args to all choices and completer functions
-rw-r--r--cmd2/argparse_completer.py16
-rw-r--r--cmd2/argparse_custom.py44
-rwxr-xr-xcmd2/cmd2.py32
-rw-r--r--cmd2/utils.py3
-rwxr-xr-xexamples/tab_autocompletion.py6
-rw-r--r--tests/test_argparse_completer.py10
6 files changed, 50 insertions, 61 deletions
diff --git a/cmd2/argparse_completer.py b/cmd2/argparse_completer.py
index 8565db77..20cc08c3 100644
--- a/cmd2/argparse_completer.py
+++ b/cmd2/argparse_completer.py
@@ -449,29 +449,25 @@ class AutoCompleter(object):
setattr(parsed_args, action.dest, tokens)
parsed_args.__parser__ = self._parser
+ # Arguments to completer/choices functions
+ args = []
+ kwargs = {'parsed_args': parsed_args}
+
# Check if the argument uses a specific tab completion function to provide its choices
if isinstance(arg_choices, ChoicesCallable) and arg_choices.is_completer:
- args = []
if arg_choices.is_method:
args.append(self._cmd2_app)
-
args.extend([text, line, begidx, endidx])
- if arg_choices.pass_parsed_args:
- args.append(parsed_args)
-
- results = arg_choices.to_call(*args)
+ results = arg_choices.to_call(*args, **kwargs)
# Otherwise use basic_complete on the choices
else:
# Check if the choices come from a function
if isinstance(arg_choices, ChoicesCallable) and not arg_choices.is_completer:
- args = []
if arg_choices.is_method:
args.append(self._cmd2_app)
- if arg_choices.pass_parsed_args:
- args.append(parsed_args)
- arg_choices = arg_choices.to_call(*args)
+ arg_choices = arg_choices.to_call(*args, **kwargs)
# Since arg_choices can be any iterable type, convert to a list
arg_choices = list(arg_choices)
diff --git a/cmd2/argparse_custom.py b/cmd2/argparse_custom.py
index 5d022721..56fb57ea 100644
--- a/cmd2/argparse_custom.py
+++ b/cmd2/argparse_custom.py
@@ -44,7 +44,7 @@ Tab Completion:
generated when the user hits tab.
Example:
- def my_choices_function):
+ def my_choices_function(**kwargs):
...
return my_generated_list
@@ -56,7 +56,7 @@ Tab Completion:
cases where the list of choices being generated relies on state data of the cmd2-based app
Example:
- def my_choices_method(self):
+ def my_choices_method(self, **kwargs):
...
return my_generated_list
@@ -66,7 +66,7 @@ Tab Completion:
function. completer_method should be used in those cases.
Example:
- def my_completer_function(text, line, begidx, endidx):
+ def my_completer_function(text, line, begidx, endidx, **kwargs):
...
return completions
parser.add_argument('-o', '--options', completer_function=my_completer_function)
@@ -238,21 +238,17 @@ class ChoicesCallable:
Enables using a callable as the choices provider for an argparse argument.
While argparse has the built-in choices attribute, it is limited to an iterable.
"""
- def __init__(self, is_method: bool, is_completer: bool, to_call: Callable, pass_parsed_args: bool):
+ def __init__(self, is_method: bool, is_completer: bool, to_call: Callable):
"""
Initializer
:param is_method: True if to_call is an instance method of a cmd2 app. False if it is a function.
:param is_completer: True if to_call is a tab completion routine which expects
the args: text, line, begidx, endidx
:param to_call: the callable object that will be called to provide choices for the argument
- :param pass_parsed_args: if True, then to_call will be passed an argparse Namespace of arguments
- parsed by AutoCompleter. This is useful if the value of a particular argument
- affects what data will be tab-completed.
"""
self.is_method = is_method
self.is_completer = is_completer
self.to_call = to_call
- self.pass_parsed_args = pass_parsed_args
def _set_choices_callable(action: argparse.Action, choices_callable: ChoicesCallable) -> None:
@@ -275,28 +271,24 @@ def _set_choices_callable(action: argparse.Action, choices_callable: ChoicesCall
setattr(action, ATTR_CHOICES_CALLABLE, choices_callable)
-def set_choices_function(action: argparse.Action, choices_function: Callable, pass_parsed_args: bool) -> None:
+def set_choices_function(action: argparse.Action, choices_function: Callable) -> None:
"""Set choices_function on an argparse action"""
- _set_choices_callable(action, ChoicesCallable(is_method=False, is_completer=False,
- to_call=choices_function, pass_parsed_args=pass_parsed_args))
+ _set_choices_callable(action, ChoicesCallable(is_method=False, is_completer=False, to_call=choices_function))
-def set_choices_method(action: argparse.Action, choices_method: Callable, pass_parsed_args: bool) -> None:
+def set_choices_method(action: argparse.Action, choices_method: Callable) -> None:
"""Set choices_method on an argparse action"""
- _set_choices_callable(action, ChoicesCallable(is_method=True, is_completer=False,
- to_call=choices_method, pass_parsed_args=pass_parsed_args))
+ _set_choices_callable(action, ChoicesCallable(is_method=True, is_completer=False, to_call=choices_method))
-def set_completer_function(action: argparse.Action, completer_function: Callable, pass_parsed_args: bool) -> None:
+def set_completer_function(action: argparse.Action, completer_function: Callable) -> None:
"""Set completer_function on an argparse action"""
- _set_choices_callable(action, ChoicesCallable(is_method=False, is_completer=True,
- to_call=completer_function, pass_parsed_args=pass_parsed_args))
+ _set_choices_callable(action, ChoicesCallable(is_method=False, is_completer=True, to_call=completer_function))
-def set_completer_method(action: argparse.Action, completer_method: Callable, pass_parsed_args: bool) -> None:
+def set_completer_method(action: argparse.Action, completer_method: Callable) -> None:
"""Set completer_method on an argparse action"""
- _set_choices_callable(action, ChoicesCallable(is_method=True, is_completer=True,
- to_call=completer_method, pass_parsed_args=pass_parsed_args))
+ _set_choices_callable(action, ChoicesCallable(is_method=True, is_completer=True, to_call=completer_method))
############################################################################################################
@@ -314,7 +306,6 @@ def _add_argument_wrapper(self, *args,
choices_method: Optional[Callable] = None,
completer_function: Optional[Callable] = None,
completer_method: Optional[Callable] = None,
- pass_parsed_args: bool = False,
suppress_tab_hint: bool = False,
descriptive_header: Optional[str] = None,
**kwargs) -> argparse.Action:
@@ -334,9 +325,6 @@ def _add_argument_wrapper(self, *args,
:param choices_method: cmd2-app method that provides choices for this argument
:param completer_function: tab-completion function that provides choices for this argument
:param completer_method: cmd2-app tab-completion method that provides choices for this argument
- :param pass_parsed_args: if True, then to_call will be passed an argparse Namespace of arguments
- parsed by AutoCompleter. This is useful if the value of a particular argument
- affects what data will be tab-completed.
:param suppress_tab_hint: when AutoCompleter has no results to show during tab completion, it displays the current
argument's help text as a hint. Set this to True to suppress the hint. If this argument's
help text is set to argparse.SUPPRESS, then tab hints will not display regardless of the
@@ -421,13 +409,13 @@ def _add_argument_wrapper(self, *args,
setattr(new_arg, ATTR_NARGS_RANGE, nargs_range)
if choices_function:
- set_choices_function(new_arg, choices_function, pass_parsed_args)
+ set_choices_function(new_arg, choices_function)
elif choices_method:
- set_choices_method(new_arg, choices_method, pass_parsed_args)
+ set_choices_method(new_arg, choices_method)
elif completer_function:
- set_completer_function(new_arg, completer_function, pass_parsed_args)
+ set_completer_function(new_arg, completer_function)
elif completer_method:
- set_completer_method(new_arg, completer_method, pass_parsed_args)
+ set_completer_method(new_arg, completer_method)
setattr(new_arg, ATTR_SUPPRESS_TAB_HINT, suppress_tab_hint)
setattr(new_arg, ATTR_DESCRIPTIVE_COMPLETION_HEADER, descriptive_header)
diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py
index c8b3a9db..326a0111 100755
--- a/cmd2/cmd2.py
+++ b/cmd2/cmd2.py
@@ -851,7 +851,7 @@ class Cmd(cmd.Cmd):
return tokens, raw_tokens
def delimiter_complete(self, text: str, line: str, begidx: int, endidx: int,
- match_against: Iterable, delimiter: str) -> List[str]:
+ match_against: Iterable, delimiter: str, **_kwargs) -> List[str]:
"""
Performs tab completion against a list but each match is split on a delimiter and only
the portion of the match being tab completed is shown as the completion suggestions.
@@ -882,6 +882,7 @@ class Cmd(cmd.Cmd):
:param endidx: the ending index of the prefix text
:param match_against: the list being matched against
:param delimiter: what delimits each portion of the matches (ex: paths are delimited by a slash)
+ :param _kwargs: a placeholder that handles the case when AutoCompleter passes keyword args
:return: a list of possible tab completions
"""
matches = utils.basic_complete(text, line, begidx, endidx, match_against)
@@ -913,7 +914,7 @@ class Cmd(cmd.Cmd):
def flag_based_complete(self, text: str, line: str, begidx: int, endidx: int,
flag_dict: Dict[str, Union[Iterable, Callable]], *,
- all_else: Union[None, Iterable, Callable] = None) -> List[str]:
+ all_else: Union[None, Iterable, Callable] = None, **_kwargs) -> List[str]:
"""Tab completes based on a particular flag preceding the token being completed.
:param text: the string prefix we are attempting to match (all matches must begin with it)
@@ -927,6 +928,7 @@ class Cmd(cmd.Cmd):
1. iterable list of strings to match against (dictionaries, lists, etc.)
2. function that performs tab completion (ex: path_complete)
:param all_else: an optional parameter for tab completing any token that isn't preceded by a flag in flag_dict
+ :param _kwargs: a placeholder that handles the case when AutoCompleter passes keyword args
:return: a list of possible tab completions
"""
# Get all tokens through the one being completed
@@ -955,7 +957,7 @@ class Cmd(cmd.Cmd):
def index_based_complete(self, text: str, line: str, begidx: int, endidx: int,
index_dict: Mapping[int, Union[Iterable, Callable]], *,
- all_else: Union[None, Iterable, Callable] = None) -> List[str]:
+ all_else: Union[None, Iterable, Callable] = None, **_kwargs) -> List[str]:
"""Tab completes based on a fixed position in the input string.
:param text: the string prefix we are attempting to match (all matches must begin with it)
@@ -969,6 +971,7 @@ class Cmd(cmd.Cmd):
1. iterable list of strings to match against (dictionaries, lists, etc.)
2. function that performs tab completion (ex: path_complete)
:param all_else: an optional parameter for tab completing any token that isn't at an index in index_dict
+ :param _kwargs: a placeholder that handles the case when AutoCompleter passes keyword args
:return: a list of possible tab completions
"""
# Get all tokens through the one being completed
@@ -999,7 +1002,7 @@ class Cmd(cmd.Cmd):
# noinspection PyUnusedLocal
def path_complete(self, text: str, line: str, begidx: int, endidx: int, *,
- path_filter: Optional[Callable[[str], bool]] = None) -> List[str]:
+ path_filter: Optional[Callable[[str], bool]] = None, **_kwargs) -> List[str]:
"""Performs completion of local file system paths
:param text: the string prefix we are attempting to match (all matches must begin with it)
@@ -1009,6 +1012,7 @@ class Cmd(cmd.Cmd):
:param path_filter: optional filter function that determines if a path belongs in the results
this function takes a path as its argument and returns True if the path should
be kept in the results
+ :param _kwargs: a placeholder that handles the case when AutoCompleter passes keyword args
:return: a list of possible tab completions
"""
@@ -1143,7 +1147,7 @@ class Cmd(cmd.Cmd):
return matches
def shell_cmd_complete(self, text: str, line: str, begidx: int, endidx: int, *,
- complete_blank: bool = False) -> List[str]:
+ complete_blank: bool = False, **_kwargs) -> List[str]:
"""Performs completion of executables either in a user's path or a given path
:param text: the string prefix we are attempting to match (all matches must begin with it)
@@ -1152,6 +1156,7 @@ class Cmd(cmd.Cmd):
:param endidx: the ending index of the prefix text
:param complete_blank: If True, then a blank will complete all shell commands in a user's path. If False, then
no completion is performed. Defaults to False to match Bash shell behavior.
+ :param _kwargs: a placeholder that handles the case when AutoCompleter passes keyword args
:return: a list of possible tab completions
"""
# Don't tab complete anything if no shell command has been started
@@ -1627,19 +1632,19 @@ class Cmd(cmd.Cmd):
return commands
- def _get_alias_completion_items(self) -> List[CompletionItem]:
+ def _get_alias_completion_items(self, **_kwargs) -> List[CompletionItem]:
"""Return list of current alias names and values as CompletionItems"""
return [CompletionItem(cur_key, self.aliases[cur_key]) for cur_key in self.aliases]
- def _get_macro_completion_items(self) -> List[CompletionItem]:
+ def _get_macro_completion_items(self, **_kwargs) -> List[CompletionItem]:
"""Return list of current macro names and values as CompletionItems"""
return [CompletionItem(cur_key, self.macros[cur_key].value) for cur_key in self.macros]
- def _get_settable_completion_items(self) -> List[CompletionItem]:
+ def _get_settable_completion_items(self, **_kwargs) -> List[CompletionItem]:
"""Return list of current settable names and descriptions as CompletionItems"""
return [CompletionItem(cur_key, self.settable[cur_key]) for cur_key in self.settable]
- def _get_commands_aliases_and_macros_for_completion(self) -> List[str]:
+ def _get_commands_aliases_and_macros_for_completion(self, **_kwargs) -> List[str]:
"""Return a list of visible commands, aliases, and macros for tab completion"""
visible_commands = set(self.get_visible_commands())
alias_names = set(self.aliases)
@@ -2659,7 +2664,7 @@ class Cmd(cmd.Cmd):
# noinspection PyTypeChecker
self.do_help('macro')
- def complete_help_command(self, text: str, line: str, begidx: int, endidx: int) -> List[str]:
+ def complete_help_command(self, text: str, line: str, begidx: int, endidx: int, **_kwargs) -> List[str]:
"""Completes the command argument of help"""
# Complete token against topics and visible commands
@@ -2668,7 +2673,7 @@ class Cmd(cmd.Cmd):
strs_to_match = list(topics | visible_commands)
return utils.basic_complete(text, line, begidx, endidx, strs_to_match)
- def complete_help_subcommand(self, text: str, line: str, begidx: int, endidx: int,
+ def complete_help_subcommand(self, text: str, line: str, begidx: int, endidx: int, *,
parsed_args: argparse.Namespace) -> List[str]:
"""Completes the subcommand argument of help"""
@@ -2700,7 +2705,7 @@ class Cmd(cmd.Cmd):
help_parser.add_argument('command', nargs=argparse.OPTIONAL, help="command to retrieve help for",
completer_method=complete_help_command)
help_parser.add_argument('subcommand', nargs=argparse.REMAINDER, help="subcommand to retrieve help for",
- completer_method=complete_help_subcommand, pass_parsed_args=True)
+ completer_method=complete_help_subcommand)
help_parser.add_argument('-v', '--verbose', action='store_true',
help="print a list of all commands with descriptions of each")
@@ -4118,8 +4123,7 @@ class Cmd(cmd.Cmd):
if getattr(func, CMD_ATTR_HELP_CATEGORY, None) == category:
self.disable_command(cmd_name, message_to_print)
- # noinspection PyUnusedLocal
- def _report_disabled_command_usage(self, *args, message_to_print: str, **kwargs) -> None:
+ def _report_disabled_command_usage(self, *_args, message_to_print: str, **_kwargs) -> None:
"""
Report when a disabled command has been run or had help called on it
:param args: not used
diff --git a/cmd2/utils.py b/cmd2/utils.py
index d0ce10bc..7ee20e88 100644
--- a/cmd2/utils.py
+++ b/cmd2/utils.py
@@ -624,7 +624,7 @@ class RedirectionSavedState(object):
# noinspection PyUnusedLocal
-def basic_complete(text: str, line: str, begidx: int, endidx: int, match_against: Iterable) -> List[str]:
+def basic_complete(text: str, line: str, begidx: int, endidx: int, match_against: Iterable, **_kwargs) -> List[str]:
"""
Basic tab completion function that matches against a list of strings without considering line contents
or cursor position. The args required by this function are defined in the header of Pythons's cmd.py.
@@ -634,6 +634,7 @@ def basic_complete(text: str, line: str, begidx: int, endidx: int, match_against
:param begidx: the beginning index of the prefix text
:param endidx: the ending index of the prefix text
:param match_against: the strings being matched against
+ :param _kwargs: a placeholder that handles the case when AutoCompleter passes keyword args
:return: a list of possible tab completions
"""
return [cur_match for cur_match in match_against if cur_match.startswith(text)]
diff --git a/examples/tab_autocompletion.py b/examples/tab_autocompletion.py
index 2142fe2e..8eb17c9c 100755
--- a/examples/tab_autocompletion.py
+++ b/examples/tab_autocompletion.py
@@ -16,7 +16,7 @@ actors = ['Mark Hamill', 'Harrison Ford', 'Carrie Fisher', 'Alec Guinness', 'Pet
'Jake Lloyd', 'Hayden Christensen', 'Christopher Lee']
-def query_actors() -> List[str]:
+def query_actors(**_kwargs) -> List[str]:
"""Simulating a function that queries and returns a completion values"""
return actors
@@ -103,11 +103,11 @@ class TabCompleteExample(cmd2.Cmd):
]
# noinspection PyMethodMayBeStatic
- def instance_query_actors(self) -> List[str]:
+ def instance_query_actors(self, **_kwargs) -> List[str]:
"""Simulating a function that queries and returns a completion values"""
return actors
- def instance_query_movie_ids(self) -> List[str]:
+ def instance_query_movie_ids(self, **_kwargs) -> List[str]:
"""Demonstrates showing tabular hinting of tab completion information"""
completions_with_desc = []
diff --git a/tests/test_argparse_completer.py b/tests/test_argparse_completer.py
index 2a94c1d8..fb063659 100644
--- a/tests/test_argparse_completer.py
+++ b/tests/test_argparse_completer.py
@@ -31,12 +31,12 @@ completions_from_function = ['completions', 'function', 'fairly', 'complete']
completions_from_method = ['completions', 'method', 'missed', 'spot']
-def choices_function() -> List[str]:
+def choices_function(**_kwargs) -> List[str]:
"""Function that provides choices"""
return choices_from_function
-def completer_function(text: str, line: str, begidx: int, endidx: int) -> List[str]:
+def completer_function(text: str, line: str, begidx: int, endidx: int, **_kwargs) -> List[str]:
"""Tab completion function"""
return basic_complete(text, line, begidx, endidx, completions_from_function)
@@ -123,11 +123,11 @@ class AutoCompleteTester(cmd2.Cmd):
############################################################################################################
# Begin code related to testing choices, choices_function, and choices_method parameters
############################################################################################################
- def choices_method(self) -> List[str]:
+ def choices_method(self, **_kwargs) -> List[str]:
"""Method that provides choices"""
return choices_from_method
- def completion_item_method(self) -> List[CompletionItem]:
+ def completion_item_method(self, **_kwargs) -> List[CompletionItem]:
"""Choices method that returns CompletionItems"""
items = []
for i in range(0, 10):
@@ -164,7 +164,7 @@ class AutoCompleteTester(cmd2.Cmd):
############################################################################################################
# Begin code related to testing completer_function and completer_method parameters
############################################################################################################
- def completer_method(self, text: str, line: str, begidx: int, endidx: int) -> List[str]:
+ def completer_method(self, text: str, line: str, begidx: int, endidx: int, **_kwargs) -> List[str]:
"""Tab completion method"""
return basic_complete(text, line, begidx, endidx, completions_from_method)