diff --git a/cmd2/argparse_completer.py b/cmd2/argparse_completer.py index e87e9c04a..03f2d965d 100755 --- a/cmd2/argparse_completer.py +++ b/cmd2/argparse_completer.py @@ -190,7 +190,8 @@ def __init__(self, token_start_index: int = 1, arg_choices: Dict[str, Union[List, Tuple, Callable]] = None, subcmd_args_lookup: dict = None, - tab_for_arg_help: bool = True): + tab_for_arg_help: bool = True, + cmd2_app=None): """ Create an AutoCompleter @@ -199,6 +200,8 @@ def __init__(self, :param arg_choices: dictionary mapping from argparse argument 'dest' name to list of choices :param subcmd_args_lookup: mapping a sub-command group name to a tuple to fill the child\ AutoCompleter's arg_choices and subcmd_args_lookup parameters + :param tab_for_arg_help: Enable of disable argument help when there's no completion result + :param cmd2_app: reference to the Cmd2 application. Enables argparse argument completion with class methods """ if not subcmd_args_lookup: subcmd_args_lookup = {} @@ -209,6 +212,7 @@ def __init__(self, self._arg_choices = arg_choices.copy() if arg_choices is not None else {} self._token_start_index = token_start_index self._tab_for_arg_help = tab_for_arg_help + self._cmd2_app = cmd2_app self._flags = [] # all flags in this command self._flags_without_args = [] # all flags that don't take arguments @@ -252,7 +256,8 @@ def __init__(self, subcmd_start = token_start_index + len(self._positional_actions) sub_completers[subcmd] = AutoCompleter(action.choices[subcmd], subcmd_start, arg_choices=subcmd_args, - subcmd_args_lookup=subcmd_lookup) + subcmd_args_lookup=subcmd_lookup, + cmd2_app=cmd2_app) sub_commands.append(subcmd) self._positional_completers[action.dest] = sub_completers self._arg_choices[action.dest] = sub_commands @@ -492,7 +497,16 @@ def _resolve_choices_for_arg(self, action: argparse.Action, used_values=()) -> L args = self._arg_choices[action.dest] if callable(args): - args = args() + try: + if self._cmd2_app is not None: + try: + args = args(self._cmd2_app) + except TypeError: + args = args() + else: + args = args() + except TypeError: + return [] try: iter(args) diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py index 288a506b0..8d8a5b076 100755 --- a/cmd2/cmd2.py +++ b/cmd2/cmd2.py @@ -1838,7 +1838,7 @@ def complete(self, text, state): def _autocomplete_default(self, text: str, line: str, begidx: int, endidx: int, argparser: argparse.ArgumentParser) -> List[str]: """Default completion function for argparse commands.""" - completer = AutoCompleter(argparser) + completer = AutoCompleter(argparser, cmd2_app=self) tokens, _ = self.tokens_for_completion(line, begidx, endidx) results = completer.complete_command(tokens, text, line, begidx, endidx) @@ -2070,7 +2070,7 @@ def onecmd_plus_hooks(self, line): :param line: str - line of text read from input :return: bool - True if cmdloop() should exit, False otherwise """ - stop = 0 + stop = False try: statement = self._complete_statement(line) (stop, statement) = self.postparsing_precmd(statement) diff --git a/examples/tab_autocompletion.py b/examples/tab_autocompletion.py index 17c8391dd..a1a8daeee 100755 --- a/examples/tab_autocompletion.py +++ b/examples/tab_autocompletion.py @@ -95,6 +95,10 @@ def __init__(self): }, } + def instance_query_actors(self) -> List[str]: + """Simulating a function that queries and returns a completion values""" + return actors + # This demonstrates a number of customizations of the AutoCompleter version of ArgumentParser # - The help output will separately group required vs optional flags # - The help output for arguments with multiple flags or with append=True is more concise @@ -222,7 +226,7 @@ def _do_vid_media_shows(self, args) -> None: # tag the action objects with completion providers. This can be a collection or a callable setattr(director_action, argparse_completer.ACTION_ARG_CHOICES, static_list_directors) - setattr(actor_action, argparse_completer.ACTION_ARG_CHOICES, query_actors) + setattr(actor_action, argparse_completer.ACTION_ARG_CHOICES, instance_query_actors) vid_movies_delete_parser = vid_movies_commands_subparsers.add_parser('delete')