diff options
author | Kevin Van Brunt <kmvanbrunt@gmail.com> | 2020-08-13 14:19:05 -0400 |
---|---|---|
committer | Kevin Van Brunt <kmvanbrunt@gmail.com> | 2020-08-13 14:20:31 -0400 |
commit | e6da8596c433f46bc337c7e8a14c7de1b0310e4c (patch) | |
tree | 09f5a3225376e26dcb03419d6243c8fc52433b07 | |
parent | 5dd2d03ef35a3d33ff53d82c8039d68e263246ee (diff) | |
download | cmd2-git-e6da8596c433f46bc337c7e8a14c7de1b0310e4c.tar.gz |
Replaced choices_function / choices_method with choices_provider.
Replaced completer_function / completer_method with completer.
ArgparseCompleter now always passes cmd2.Cmd or CommandSet instance as the self
argument to choices_provider and completer functions.
Moved basic_complete from utils into cmd2.Cmd class.
Moved CompletionError to exceptions.py
-rw-r--r-- | CHANGELOG.md | 10 | ||||
-rw-r--r-- | cmd2/__init__.py | 4 | ||||
-rw-r--r-- | cmd2/argparse_completer.py | 25 | ||||
-rw-r--r-- | cmd2/argparse_custom.py | 148 | ||||
-rw-r--r-- | cmd2/cmd2.py | 76 | ||||
-rw-r--r-- | cmd2/exceptions.py | 25 | ||||
-rw-r--r-- | cmd2/utils.py | 62 | ||||
-rw-r--r-- | docs/api/exceptions.rst | 3 | ||||
-rw-r--r-- | docs/api/utils.rst | 9 | ||||
-rw-r--r-- | docs/features/completion.rst | 20 | ||||
-rwxr-xr-x | examples/arg_decorators.py | 2 | ||||
-rw-r--r-- | examples/argparse_completion.py | 98 | ||||
-rw-r--r-- | examples/modular_commands/commandset_basic.py | 3 | ||||
-rw-r--r-- | examples/modular_commands/commandset_complex.py | 3 | ||||
-rw-r--r-- | examples/modular_commands_main.py | 96 | ||||
-rw-r--r-- | tests/test_argparse_completer.py | 206 | ||||
-rw-r--r-- | tests/test_argparse_custom.py | 22 | ||||
-rwxr-xr-x | tests/test_completion.py | 14 | ||||
-rw-r--r-- | tests_isolated/test_commandset/test_commandset.py | 17 |
19 files changed, 329 insertions, 514 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index fd8d29ba..d6a918f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +## 2.0.0 (TBD) +* Breaking changes + * Argparse Completion / Settables + * Replaced `choices_function` / `choices_method` with `choices_provider`. + * Replaced `completer_function` / `completer_method` with `completer`. + * ArgparseCompleter now always passes `cmd2.Cmd` or `CommandSet` instance as the + `self argument` to choices_provider and completer functions. + * Moved `basic_complete` from utils into `cmd2.Cmd` class. + * Moved `CompletionError` to exceptions.py + ## 1.3.3 (August 13, 2020) * Breaking changes * CommandSet command functions (do_, complete_, help_) will no longer have the cmd2 app diff --git a/cmd2/__init__.py b/cmd2/__init__.py index 9f0bb176..ca080ee8 100644 --- a/cmd2/__init__.py +++ b/cmd2/__init__.py @@ -32,8 +32,8 @@ from .command_definition import CommandSet, with_default_category from .constants import COMMAND_NAME, DEFAULT_SHORTCUTS from .decorators import with_argument_list, with_argparser, with_argparser_and_unknown_args, with_category, \ as_subcommand_to -from .exceptions import Cmd2ArgparseError, SkipPostcommandHooks, CommandSetRegistrationError +from .exceptions import Cmd2ArgparseError, CommandSetRegistrationError, CompletionError, SkipPostcommandHooks from . import plugin from .parsing import Statement from .py_bridge import CommandResult -from .utils import categorize, CompletionError, Settable +from .utils import categorize, Settable diff --git a/cmd2/argparse_completer.py b/cmd2/argparse_completer.py index 0efaebe9..8bfaec80 100644 --- a/cmd2/argparse_completer.py +++ b/cmd2/argparse_completer.py @@ -24,8 +24,8 @@ from .argparse_custom import ( generate_range_error, ) 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' @@ -459,7 +459,7 @@ class ArgparseCompleter: if action.help != argparse.SUPPRESS: match_against.append(flag) - return basic_complete(text, line, begidx, endidx, match_against) + return self._cmd2_app.basic_complete(text, line, begidx, endidx, match_against) def _format_completions(self, arg_state: _ArgumentState, completions: List[Union[str, CompletionItem]]) -> List[str]: # Check if the results are CompletionItems and that there aren't too many to display @@ -533,7 +533,7 @@ class ArgparseCompleter: return completer.complete_subcommand_help(tokens[token_index:], text, line, begidx, endidx) 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 [] @@ -577,16 +577,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 @@ -630,6 +629,6 @@ 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) return self._format_completions(arg_state, results) diff --git a/cmd2/argparse_custom.py b/cmd2/argparse_custom.py index 12c18644..0687dc74 100644 --- a/cmd2/argparse_custom.py +++ b/cmd2/argparse_custom.py @@ -39,75 +39,36 @@ hints about the current argument that print when tab is pressed. In addition, you can add tab completion for each argument's values using parameters passed to add_argument(). -Below are the 5 add_argument() parameters for enabling tab completion of an +Below are the 3 add_argument() parameters for enabling tab completion of an argument's value. Only one can be used at a time. ``choices`` - pass a list of values to the choices parameter. Example:: - parser.add_argument('-o', '--options', choices=['An Option', 'SomeOtherOption']) + my_list = ['An Option', 'SomeOtherOption'] parser.add_argument('-o', '--options', choices=my_list) -``choices_function`` - pass a function that returns choices. This is good in +``choices_provider`` - pass a function that returns choices. This is good in cases where the choice list is dynamically generated when the user hits tab. Example:: - def my_choices_function(): + def my_choices_provider(self): ... return my_generated_list - parser.add_argument('-o', '--options', choices_function=my_choices_function) + parser.add_argument("arg", choices_provider=my_choices_provider) -``choices_method`` - this is equivalent to choices_function, but the function -needs to be an instance method of a cmd2.Cmd or cmd2.CommandSet subclass. When -ArgparseCompleter calls the method, it well detect whether is is bound to a -CommandSet or Cmd subclass. -If bound to a cmd2.Cmd subclass, it will pass the app instance as the `self` -argument. This is good in cases where the list of choices being generated -relies on state data of the cmd2-based app. -If bound to a cmd2.CommandSet subclass, it will pass the CommandSet instance -as the `self` argument. +``completer`` - pass a tab completion function that does custom completion. - Example:: - - def my_choices_method(self): - ... - return my_generated_list - - parser.add_argument("arg", choices_method=my_choices_method) - - -``completer_function`` - pass a tab completion function that does custom -completion. Since custom tab completion operations commonly need to modify -cmd2's instance variables related to tab completion, it will be rare to need a -completer function. completer_method should be used in those cases. - - Example:: - - def my_completer_function(text, line, begidx, endidx): - ... - return completions - parser.add_argument('-o', '--options', completer_function=my_completer_function) - -``completer_method`` - this is equivalent to completer_function, but the function -needs to be an instance method of a cmd2.Cmd or cmd2.CommandSet subclass. When -ArgparseCompleter calls the method, it well detect whether is is bound to a -CommandSet or Cmd subclass. -If bound to a cmd2.Cmd subclass, it will pass the app instance as the `self` -argument. This is good in cases where the list of choices being generated -relies on state data of the cmd2-based app. -If bound to a cmd2.CommandSet subclass, it will pass the CommandSet instance -as the `self` argument, and the app instance as the positional argument. -cmd2 provides a few completer methods for convenience (e.g., -path_complete, delimiter_complete) +cmd2 provides a few completer methods for convenience (e.g., path_complete, +delimiter_complete) Example:: # This adds file-path completion to an argument - parser.add_argument('-o', '--options', completer_method=cmd2.Cmd.path_complete) - + parser.add_argument('-o', '--options', completer=cmd2.Cmd.path_complete) You can use functools.partial() to prepopulate values of the underlying choices and completer functions/methods. @@ -115,13 +76,22 @@ path_complete, delimiter_complete) Example:: # This says to call path_complete with a preset value for its path_filter argument - completer_method = functools.partial(path_complete, - path_filter=lambda path: os.path.isdir(path)) - parser.add_argument('-o', '--options', choices_method=completer_method) - -Of the 5 tab completion parameters, choices is the only one where argparse + dir_completer = functools.partial(path_complete, + path_filter=lambda path: os.path.isdir(path)) + parser.add_argument('-o', '--options', completer=dir_completer) + +For `choices_provider` and `completer`, do not set them to a bound method. This +is because ArgparseCompleter passes the `self` argument explicitly to these +functions. When ArgparseCompleter calls one, it will detect whether it is bound +to a `Cmd` subclass or `CommandSet`. If bound to a `cmd2.Cmd subclass`, it will +pass the app instance as the `self` argument. If bound to a `cmd2.CommandSet` +subclass, it will pass the `CommandSet` instance as the `self` argument. +Therefore instead of passing something like `self.path_complete`, pass +`cmd2.Cmd.path_complete`. + +Of the 3 tab completion parameters, choices is the only one where argparse validates user input against items in the choices list. This is because the -other 4 parameters are meant to tab complete data sets that are viewed as +other 2 parameters are meant to tab complete data sets that are viewed as dynamic. Therefore it is up to the developer to validate if the user has typed an acceptable value for these arguments. @@ -129,10 +99,8 @@ The following functions exist in cases where you may want to manually add a choice-providing function/method to an existing argparse action. For instance, in __init__() of a custom action class. - - set_choices_function(action, func) - - set_choices_method(action, method) - - set_completer_function(action, func) - - set_completer_method(action, method) + - set_choices_provider(action, func) + - set_completer(action, func) There are times when what's being tab completed is determined by a previous argument on the command line. In theses cases, Autocompleter can pass a @@ -142,8 +110,8 @@ choices/completer function should have an argument called arg_tokens. Example:: - def my_choices_method(self, arg_tokens) - def my_completer_method(self, text, line, begidx, endidx, arg_tokens) + def my_choices_provider(self, arg_tokens) + def my_completer(self, text, line, begidx, endidx, arg_tokens) All values of the arg_tokens dictionary are lists, even if a particular argument expects only 1 token. Since ArgparseCompleter is for tab completion, @@ -295,15 +263,13 @@ 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): + def __init__(self, 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 """ - self.is_method = is_method self.is_completer = is_completer self.to_call = to_call @@ -318,34 +284,24 @@ def _set_choices_callable(action: argparse.Action, choices_callable: ChoicesCall # Verify consistent use of parameters if action.choices is not None: err_msg = ("None of the following parameters can be used alongside a choices parameter:\n" - "choices_function, choices_method, completer_function, completer_method") + "choices_provider, completer") raise (TypeError(err_msg)) elif action.nargs == 0: err_msg = ("None of the following parameters can be used on an action that takes no arguments:\n" - "choices_function, choices_method, completer_function, completer_method") + "choices_provider, completer") raise (TypeError(err_msg)) setattr(action, ATTR_CHOICES_CALLABLE, choices_callable) -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)) - - -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)) - - -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)) +def set_choices_provider(action: argparse.Action, choices_provider: Callable) -> None: + """Set choices_provider on an argparse action""" + _set_choices_callable(action, ChoicesCallable(is_completer=False, to_call=choices_provider)) -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)) +def set_completer(action: argparse.Action, completer: Callable) -> None: + """Set completer on an argparse action""" + _set_choices_callable(action, ChoicesCallable(is_completer=True, to_call=completer)) ############################################################################################################ @@ -359,10 +315,8 @@ orig_actions_container_add_argument = argparse._ActionsContainer.add_argument def _add_argument_wrapper(self, *args, nargs: Union[int, str, Tuple[int], Tuple[int, int], None] = None, - choices_function: Optional[Callable] = None, - choices_method: Optional[Callable] = None, - completer_function: Optional[Callable] = None, - completer_method: Optional[Callable] = None, + choices_provider: Optional[Callable] = None, + completer: Optional[Callable] = None, suppress_tab_hint: bool = False, descriptive_header: Optional[str] = None, **kwargs) -> argparse.Action: @@ -378,10 +332,8 @@ def _add_argument_wrapper(self, *args, to specify a max value with no upper bound, use a 1-item tuple (min,) # Added args used by ArgparseCompleter - :param choices_function: function that provides choices for this argument - :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 choices_provider: function that provides choices for this argument + :param completer: tab completion function that provides choices for this argument :param suppress_tab_hint: when ArgparseCompleter 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 @@ -393,7 +345,7 @@ def _add_argument_wrapper(self, *args, :param kwargs: keyword-arguments recognized by argparse._ActionsContainer.add_argument Note: You can only use 1 of the following in your argument: - choices, choices_function, choices_method, completer_function, completer_method + choices, choices_provider, completer See the header of this file for more information @@ -401,12 +353,12 @@ def _add_argument_wrapper(self, *args, :raises: ValueError on incorrect parameter usage """ # Verify consistent use of arguments - choices_callables = [choices_function, choices_method, completer_function, completer_method] + choices_callables = [choices_provider, completer] num_params_set = len(choices_callables) - choices_callables.count(None) if num_params_set > 1: err_msg = ("Only one of the following parameters may be used at a time:\n" - "choices_function, choices_method, completer_function, completer_method") + "choices_provider, completer") raise (ValueError(err_msg)) # Pre-process special ranged nargs @@ -465,14 +417,10 @@ def _add_argument_wrapper(self, *args, # Set the custom attributes setattr(new_arg, ATTR_NARGS_RANGE, nargs_range) - if choices_function: - set_choices_function(new_arg, choices_function) - elif choices_method: - set_choices_method(new_arg, choices_method) - elif completer_function: - set_completer_function(new_arg, completer_function) - elif completer_method: - set_completer_method(new_arg, completer_method) + if choices_provider: + set_choices_provider(new_arg, choices_provider) + elif completer: + set_completer(new_arg, completer) 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 610ce4a3..aaa45af6 100644 --- a/cmd2/cmd2.py +++ b/cmd2/cmd2.py @@ -51,6 +51,7 @@ from .decorators import with_argparser, as_subcommand_to from .exceptions import ( CommandSetRegistrationError, Cmd2ShlexError, + CompletionError, EmbeddedConsoleExit, EmptyStatement, RedirectionError, @@ -59,7 +60,7 @@ from .exceptions import ( from .history import History, HistoryItem from .parsing import Macro, MacroArg, Statement, StatementParser, shlex_split from .rl_utils import RlType, rl_get_point, rl_make_safe_prompt, rl_set_prompt, rl_type, rl_warning, vt100_support -from .utils import CompletionError, get_defining_class, Settable +from .utils import get_defining_class, Settable # Set up readline if rl_type == RlType.NONE: # pragma: no cover @@ -1042,6 +1043,21 @@ class Cmd(cmd.Cmd): return tokens, raw_tokens + # noinspection PyMethodMayBeStatic, PyUnusedLocal + def basic_complete(self, text: str, line: str, begidx: int, endidx: int, match_against: Iterable) -> 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 Python's cmd.py. + + :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 match_against: the strings being matched against + :return: a list of possible tab completions + """ + return [cur_match for cur_match in match_against if cur_match.startswith(text)] + def delimiter_complete(self, text: str, line: str, begidx: int, endidx: int, match_against: Iterable, delimiter: str) -> List[str]: """ @@ -1076,7 +1092,7 @@ class Cmd(cmd.Cmd): :param delimiter: what delimits each portion of the matches (ex: paths are delimited by a slash) :return: a list of possible tab completions """ - matches = utils.basic_complete(text, line, begidx, endidx, match_against) + matches = self.basic_complete(text, line, begidx, endidx, match_against) # Display only the portion of the match that's being completed based on delimiter if matches: @@ -1137,7 +1153,7 @@ class Cmd(cmd.Cmd): # Perform tab completion using an Iterable if isinstance(match_against, Iterable): - completions_matches = utils.basic_complete(text, line, begidx, endidx, match_against) + completions_matches = self.basic_complete(text, line, begidx, endidx, match_against) # Perform tab completion using a function elif callable(match_against): @@ -1181,7 +1197,7 @@ class Cmd(cmd.Cmd): # Perform tab completion using a Iterable if isinstance(match_against, Iterable): - matches = utils.basic_complete(text, line, begidx, endidx, match_against) + matches = self.basic_complete(text, line, begidx, endidx, match_against) # Perform tab completion using a function elif callable(match_against): @@ -1765,7 +1781,7 @@ class Cmd(cmd.Cmd): # Otherwise complete token against anything a user can run else: match_against = self._get_commands_aliases_and_macros_for_completion() - self.completion_matches = utils.basic_complete(text, line, begidx, endidx, match_against) + self.completion_matches = self.basic_complete(text, line, begidx, endidx, match_against) # If we have one result and we are at the end of the line, then add a space if allowed if len(self.completion_matches) == 1 and endidx == len(line) and self.allow_appended_space: @@ -2695,9 +2711,9 @@ class Cmd(cmd.Cmd): epilog=alias_create_epilog) alias_create_parser.add_argument('name', help='name of this alias') alias_create_parser.add_argument('command', help='what the alias resolves to', - choices_method=_get_commands_aliases_and_macros_for_completion) + choices_provider=_get_commands_aliases_and_macros_for_completion) alias_create_parser.add_argument('command_args', nargs=argparse.REMAINDER, help='arguments to pass to command', - completer_method=path_complete) + completer=path_complete) @as_subcommand_to('alias', 'create', alias_create_parser, help=alias_create_description.lower()) def _alias_create(self, args: argparse.Namespace) -> None: @@ -2738,7 +2754,7 @@ class Cmd(cmd.Cmd): alias_delete_parser = DEFAULT_ARGUMENT_PARSER(add_help=False, description=alias_delete_description) alias_delete_parser.add_argument('names', nargs=argparse.ZERO_OR_MORE, help='alias(es) to delete', - choices_method=_get_alias_completion_items, descriptive_header='Value') + choices_provider=_get_alias_completion_items, descriptive_header='Value') alias_delete_parser.add_argument('-a', '--all', action='store_true', help="delete all aliases") @as_subcommand_to('alias', 'delete', alias_delete_parser, help=alias_delete_help) @@ -2766,7 +2782,7 @@ class Cmd(cmd.Cmd): alias_list_parser = DEFAULT_ARGUMENT_PARSER(add_help=False, description=alias_list_description) alias_list_parser.add_argument('names', nargs=argparse.ZERO_OR_MORE, help='alias(es) to list', - choices_method=_get_alias_completion_items, descriptive_header='Value') + choices_provider=_get_alias_completion_items, descriptive_header='Value') @as_subcommand_to('alias', 'list', alias_list_parser, help=alias_delete_help) def _alias_list(self, args: argparse.Namespace) -> None: @@ -2846,9 +2862,9 @@ class Cmd(cmd.Cmd): epilog=macro_create_epilog) macro_create_parser.add_argument('name', help='name of this macro') macro_create_parser.add_argument('command', help='what the macro resolves to', - choices_method=_get_commands_aliases_and_macros_for_completion) + choices_provider=_get_commands_aliases_and_macros_for_completion) macro_create_parser.add_argument('command_args', nargs=argparse.REMAINDER, - help='arguments to pass to command', completer_method=path_complete) + help='arguments to pass to command', completer=path_complete) @as_subcommand_to('macro', 'create', macro_create_parser, help=macro_create_help) def _macro_create(self, args: argparse.Namespace) -> None: @@ -2935,7 +2951,7 @@ class Cmd(cmd.Cmd): macro_delete_description = "Delete specified macros or all macros if --all is used" macro_delete_parser = DEFAULT_ARGUMENT_PARSER(add_help=False, description=macro_delete_description) macro_delete_parser.add_argument('names', nargs=argparse.ZERO_OR_MORE, help='macro(s) to delete', - choices_method=_get_macro_completion_items, descriptive_header='Value') + choices_provider=_get_macro_completion_items, descriptive_header='Value') macro_delete_parser.add_argument('-a', '--all', action='store_true', help="delete all macros") @as_subcommand_to('macro', 'delete', macro_delete_parser, help=macro_delete_help) @@ -2963,7 +2979,7 @@ class Cmd(cmd.Cmd): macro_list_parser = DEFAULT_ARGUMENT_PARSER(add_help=False, description=macro_list_description) macro_list_parser.add_argument('names', nargs=argparse.ZERO_OR_MORE, help='macro(s) to list', - choices_method=_get_macro_completion_items, descriptive_header='Value') + choices_provider=_get_macro_completion_items, descriptive_header='Value') @as_subcommand_to('macro', 'list', macro_list_parser, help=macro_list_help) def _macro_list(self, args: argparse.Namespace) -> None: @@ -2985,7 +3001,7 @@ class Cmd(cmd.Cmd): topics = set(self.get_help_topics()) visible_commands = set(self.get_visible_commands()) strs_to_match = list(topics | visible_commands) - return utils.basic_complete(text, line, begidx, endidx, strs_to_match) + return self.basic_complete(text, line, begidx, endidx, strs_to_match) def complete_help_subcommands(self, text: str, line: str, begidx: int, endidx: int, arg_tokens: Dict[str, List[str]]) -> List[str]: @@ -3012,9 +3028,9 @@ class Cmd(cmd.Cmd): help_parser = DEFAULT_ARGUMENT_PARSER(description="List available commands or provide " "detailed help for a specific command") help_parser.add_argument('command', nargs=argparse.OPTIONAL, help="command to retrieve help for", - completer_method=complete_help_command) + completer=complete_help_command) help_parser.add_argument('subcommands', nargs=argparse.REMAINDER, help="subcommand(s) to retrieve help for", - completer_method=complete_help_subcommands) + completer=complete_help_subcommands) help_parser.add_argument('-v', '--verbose', action='store_true', help="print a list of all commands with descriptions of each") @@ -3276,10 +3292,8 @@ class Cmd(cmd.Cmd): arg_name = 'value' settable_parser.add_argument(arg_name, metavar=arg_name, help=settable.description, choices=settable.choices, - choices_function=settable.choices_function, - choices_method=settable.choices_method, - completer_function=settable.completer_function, - completer_method=settable.completer_method) + choices_provider=settable.choices_provider, + completer=settable.completer) from .argparse_completer import ArgparseCompleter completer = ArgparseCompleter(settable_parser, self) @@ -3297,12 +3311,12 @@ class Cmd(cmd.Cmd): set_parser_parent.add_argument('-v', '--verbose', action='store_true', help='include description of parameters when viewing') set_parser_parent.add_argument('param', nargs=argparse.OPTIONAL, help='parameter to set or view', - choices_method=_get_settable_completion_items, descriptive_header='Description') + choices_provider=_get_settable_completion_items, descriptive_header='Description') # Create the parser for the set command set_parser = DEFAULT_ARGUMENT_PARSER(parents=[set_parser_parent]) set_parser.add_argument('value', nargs=argparse.OPTIONAL, help='new value for settable', - completer_method=complete_set_value) + completer=complete_set_value) # Preserve quotes so users can pass in quoted empty strings and flags (e.g. -h) as the value @with_argparser(set_parser, preserve_quotes=True) @@ -3363,9 +3377,9 @@ class Cmd(cmd.Cmd): self.poutput(result_str) shell_parser = DEFAULT_ARGUMENT_PARSER(description="Execute a command as if at the OS prompt") - shell_parser.add_argument('command', help='the command to run', completer_method=shell_cmd_complete) + shell_parser.add_argument('command', help='the command to run', completer=shell_cmd_complete) shell_parser.add_argument('command_args', nargs=argparse.REMAINDER, help='arguments to pass to command', - completer_method=path_complete) + completer=path_complete) # Preserve quotes since we are passing these strings to the shell @with_argparser(shell_parser, preserve_quotes=True) @@ -3656,9 +3670,9 @@ class Cmd(cmd.Cmd): return py_bridge.stop run_pyscript_parser = DEFAULT_ARGUMENT_PARSER(description="Run a Python script file inside the console") - run_pyscript_parser.add_argument('script_path', help='path to the script file', completer_method=path_complete) + run_pyscript_parser.add_argument('script_path', help='path to the script file', completer=path_complete) run_pyscript_parser.add_argument('script_arguments', nargs=argparse.REMAINDER, - help='arguments to pass to script', completer_method=path_complete) + help='arguments to pass to script', completer=path_complete) @with_argparser(run_pyscript_parser) def do_run_pyscript(self, args: argparse.Namespace) -> Optional[bool]: @@ -3752,10 +3766,10 @@ class Cmd(cmd.Cmd): help='edit and then run selected history items') history_action_group.add_argument('-o', '--output_file', metavar='FILE', help='output commands to a script file, implies -s', - completer_method=path_complete) + completer=path_complete) history_action_group.add_argument('-t', '--transcript', metavar='TRANSCRIPT_FILE', help='output commands and results to a transcript file,\nimplies -s', - completer_method=path_complete) + completer=path_complete) history_action_group.add_argument('-c', '--clear', action='store_true', help='clear all history') history_format_group = history_parser.add_argument_group(title='formatting') @@ -4062,7 +4076,7 @@ class Cmd(cmd.Cmd): edit_parser = DEFAULT_ARGUMENT_PARSER(description=edit_description) edit_parser.add_argument('file_path', nargs=argparse.OPTIONAL, - help="optional path to a file to open in editor", completer_method=path_complete) + help="optional path to a file to open in editor", completer=path_complete) @with_argparser(edit_parser) def do_edit(self, args: argparse.Namespace) -> None: @@ -4105,8 +4119,8 @@ class Cmd(cmd.Cmd): run_script_parser = DEFAULT_ARGUMENT_PARSER(description=run_script_description) run_script_parser.add_argument('-t', '--transcript', metavar='TRANSCRIPT_FILE', help='record the output of the script as a transcript file', - completer_method=path_complete) - run_script_parser.add_argument('script_path', help="path to the script file", completer_method=path_complete) + completer=path_complete) + run_script_parser.add_argument('script_path', help="path to the script file", completer=path_complete) @with_argparser(run_script_parser) def do_run_script(self, args: argparse.Namespace) -> Optional[bool]: diff --git a/cmd2/exceptions.py b/cmd2/exceptions.py index d253985a..832794bd 100644 --- a/cmd2/exceptions.py +++ b/cmd2/exceptions.py @@ -31,6 +31,31 @@ class CommandSetRegistrationError(Exception): """ pass + +class CompletionError(Exception): + """ + Raised during tab completion operations to report any sort of error you want printed. This can also be used + just to display a message, even if it's not an error. For instance, ArgparseCompleter raises CompletionErrors + to display tab completion hints and sets apply_style to False so hints aren't colored like error text. + + Example use cases + + - Reading a database to retrieve a tab completion data set failed + - A previous command line argument that determines the data set being completed is invalid + - Tab completion hints + """ + def __init__(self, *args, apply_style: bool = True, **kwargs): + """ + Initializer for CompletionError + :param apply_style: If True, then ansi.style_error will be applied to the message text when printed. + Set to False in cases where the message text already has the desired style. + Defaults to True. + """ + self.apply_style = apply_style + + # noinspection PyArgumentList + super().__init__(*args, **kwargs) + ############################################################################################################ # The following exceptions are NOT part of the public API and are intended for internal use only. ############################################################################################################ diff --git a/cmd2/utils.py b/cmd2/utils.py index a2b1c854..d8d6b7cc 100644 --- a/cmd2/utils.py +++ b/cmd2/utils.py @@ -74,40 +74,13 @@ def str_to_bool(val: str) -> bool: raise ValueError("must be True or False (case-insensitive)") -class CompletionError(Exception): - """ - Raised during tab completion operations to report any sort of error you want printed. This can also be used - just to display a message, even if it's not an error. For instance, ArgparseCompleter raises CompletionErrors - to display tab completion hints and sets apply_style to False so hints aren't colored like error text. - - Example use cases - - - Reading a database to retrieve a tab completion data set failed - - A previous command line argument that determines the data set being completed is invalid - - Tab completion hints - """ - def __init__(self, *args, apply_style: bool = True, **kwargs): - """ - Initializer for CompletionError - :param apply_style: If True, then ansi.style_error will be applied to the message text when printed. - Set to False in cases where the message text already has the desired style. - Defaults to True. - """ - self.apply_style = apply_style - - # noinspection PyArgumentList - super().__init__(*args, **kwargs) - - class Settable: """Used to configure a cmd2 instance member to be settable via the set command in the CLI""" def __init__(self, name: str, val_type: Callable, description: str, *, onchange_cb: Callable[[str, Any, Any], Any] = None, choices: Iterable = None, - choices_function: Optional[Callable] = None, - choices_method: Optional[Callable] = None, - completer_function: Optional[Callable] = None, - completer_method: Optional[Callable] = None): + choices_provider: Optional[Callable] = None, + completer: Optional[Callable] = None): """ Settable Initializer @@ -129,14 +102,11 @@ class Settable: same settings in argparse-based tab completion. A maximum of one of these should be provided. :param choices: iterable of accepted values - :param choices_function: function that provides choices for this argument - :param choices_method: cmd2-app method that provides choices for this argument (See note below) - :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 (See note below) + :param choices_provider: function that provides choices for this argument + :param completer: tab completion function that provides choices for this argument Note: - For choices_method and completer_method, do not set them to a bound method. This is because + For choices_provider and completer, do not set them to a bound method. This is because ArgparseCompleter passes the self argument explicitly to these functions. Therefore instead of passing something like self.path_complete, pass cmd2.Cmd.path_complete. @@ -150,10 +120,8 @@ class Settable: self.description = description self.onchange_cb = onchange_cb self.choices = choices - self.choices_function = choices_function - self.choices_method = choices_method - self.completer_function = completer_function - self.completer_method = completer_method + self.choices_provider = choices_provider + self.completer = completer def namedtuple_with_defaults(typename: str, field_names: Union[str, List[str]], @@ -689,22 +657,6 @@ class RedirectionSavedState: self.saved_redirecting = saved_redirecting -# noinspection PyUnusedLocal -def basic_complete(text: str, line: str, begidx: int, endidx: int, match_against: Iterable) -> 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 Python's cmd.py. - - :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 match_against: the strings being matched against - :return: a list of possible tab completions - """ - return [cur_match for cur_match in match_against if cur_match.startswith(text)] - - class TextAlignment(Enum): """Horizontal text alignment""" LEFT = 1 diff --git a/docs/api/exceptions.rst b/docs/api/exceptions.rst index db23eb0a..98afa97a 100644 --- a/docs/api/exceptions.rst +++ b/docs/api/exceptions.rst @@ -12,3 +12,6 @@ Custom cmd2 exceptions .. autoclass:: cmd2.exceptions.CommandSetRegistrationError :members: + +.. autoclass:: cmd2.exceptions.CompletionError + :members: diff --git a/docs/api/utils.rst b/docs/api/utils.rst index d9166401..188f5b16 100644 --- a/docs/api/utils.rst +++ b/docs/api/utils.rst @@ -36,15 +36,6 @@ IO Handling :members: -Tab Completion --------------- - -.. autoclass:: cmd2.utils.CompletionError - :members: - -.. autofunction:: cmd2.utils.basic_complete - - Text Alignment -------------- diff --git a/docs/features/completion.rst b/docs/features/completion.rst index 77a51136..0e6bedd9 100644 --- a/docs/features/completion.rst +++ b/docs/features/completion.rst @@ -38,7 +38,7 @@ Included Tab Completion Functions --------------------------------- ``cmd2`` provides the following tab completion functions -- :attr:`cmd2.utils.basic_complete` - helper method for tab completion against +- :attr:`cmd2.Cmd.basic_complete` - helper method for tab completion against a list - :attr:`cmd2.Cmd.path_complete` - helper method provides flexible tab completion of file system paths @@ -79,7 +79,7 @@ to be reported to the user. These include the following example cases: is invalid - Tab completion hints -``cmd2`` provides the :class:`cmd2.utils.CompletionError` exception class for +``cmd2`` provides the :class:`cmd2.exceptions.CompletionError` exception class for this capability. If an error occurs in which it is more desirable to display a message than a stack trace, then raise a ``CompletionError``. By default, the message displays in red like an error. However, ``CompletionError`` has a @@ -95,18 +95,18 @@ Tab Completion Using argparse Decorators When using one the argparse-based :ref:`api/decorators:cmd2.decorators`, ``cmd2`` provides automatic tab completion of flag names. -Tab completion of argument values can be configured by using one of five +Tab completion of argument values can be configured by using one of three parameters to :meth:`argparse.ArgumentParser.add_argument` - ``choices`` -- ``choices_function`` or ``choices_method`` -- ``completer_function`` or ``completer_method`` +- ``choices_provider`` +- ``completer`` See the arg_decorators_ or colors_ example for a demonstration of how to use the ``choices`` parameter. See the argparse_completion_ example for a -demonstration of how to use the ``choices_function`` and ``choices_method`` -parameters. See the arg_decorators_ or argparse_completion_ example for a -demonstration of how to use the ``completer_method`` parameter. +demonstration of how to use the ``choices_provider`` parameter. See the +arg_decorators_ or argparse_completion_ example for a demonstration +of how to use the ``completer`` parameter. When tab completing flags or argument values for a ``cmd2`` command using one of these decorators, ``cmd2`` keeps track of state so that once a flag has @@ -126,8 +126,8 @@ When tab completing things like a unique ID from a database, it can often be beneficial to provide the user with some extra context about the item being completed, such as a description. To facilitate this, ``cmd2`` defines the :class:`cmd2.argparse_custom.CompletionItem` class which can be returned from -any of the 4 completion functions: ``choices_function``, ``choices_method``, -``completion_function``, or ``completion_method``. +any of the 3 completion parameters: ``choices``, ``choices_provider``, and +``completer``. See the argparse_completion_ example or the implementation of the built-in :meth:`~cmd2.Cmd.do_set` command for demonstration of how this is used. diff --git a/examples/arg_decorators.py b/examples/arg_decorators.py index a085341d..9ffcd8ce 100755 --- a/examples/arg_decorators.py +++ b/examples/arg_decorators.py @@ -18,7 +18,7 @@ class ArgparsingApp(cmd2.Cmd): help='add comma for thousands separator') fsize_parser.add_argument('-u', '--unit', choices=['MB', 'KB'], help='unit to display size in') fsize_parser.add_argument('file_path', help='path of file', - completer_method=cmd2.Cmd.path_complete) + completer=cmd2.Cmd.path_complete) @cmd2.with_argparser(fsize_parser) def do_fsize(self, args: argparse.Namespace) -> None: diff --git a/examples/argparse_completion.py b/examples/argparse_completion.py index e44533b3..c2cb31f6 100644 --- a/examples/argparse_completion.py +++ b/examples/argparse_completion.py @@ -6,62 +6,19 @@ A simple example demonstrating how to integrate tab completion with argparse-bas import argparse from typing import Dict, List -from cmd2 import Cmd, Cmd2ArgumentParser, CompletionItem, with_argparser -from cmd2.utils import CompletionError, basic_complete +from cmd2 import Cmd, Cmd2ArgumentParser, CompletionError, CompletionItem, with_argparser # Data source for argparse.choices food_item_strs = ['Pizza', 'Ham', 'Ham Sandwich', 'Potato'] -def choices_function() -> List[str]: - """Choices functions are useful when the choice list is dynamically generated (e.g. from data in a database)""" - return ['a', 'dynamic', 'list', 'goes', 'here'] - - -def completer_function(text: str, line: str, begidx: int, endidx: int) -> List[str]: - """ - A tab completion function not dependent on instance data. Since custom tab completion operations commonly - need to modify cmd2's instance variables related to tab completion, it will be rare to need a completer - function. completer_method should be used in those cases. - """ - match_against = ['a', 'dynamic', 'list', 'goes', 'here'] - return basic_complete(text, line, begidx, endidx, match_against) - - -def choices_completion_item() -> List[CompletionItem]: - """Return CompletionItem instead of strings. These give more context to what's being tab completed.""" - items = \ - { - 1: "My item", - 2: "Another item", - 3: "Yet another item" - } - return [CompletionItem(item_id, description) for item_id, description in items.items()] - - -def choices_arg_tokens(arg_tokens: Dict[str, List[str]]) -> List[str]: - """ - If a choices or completer function/method takes a value called arg_tokens, then it will be - passed a dictionary that maps the command line tokens up through the one being completed - to their argparse argument name. All values of the arg_tokens dictionary are lists, even if - a particular argument expects only 1 token. - """ - # Check if choices_function flag has appeared - values = ['choices_function', 'flag'] - if 'choices_function' in arg_tokens: - values.append('is {}'.format(arg_tokens['choices_function'][0])) - else: - values.append('not supplied') - return values - - class ArgparseCompletion(Cmd): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.sport_item_strs = ['Bat', 'Basket', 'Basketball', 'Football', 'Space Ball'] - def choices_method(self) -> List[str]: - """Choices methods are useful when the choice list is based on instance data of your application""" + def choices_provider(self) -> List[str]: + """A choices provider is useful when the choice list is based on instance data of your application""" return self.sport_item_strs def choices_completion_error(self) -> List[str]: @@ -76,6 +33,33 @@ class ArgparseCompletion(Cmd): return self.sport_item_strs raise CompletionError("debug must be true") + # noinspection PyMethodMayBeStatic + def choices_completion_item(self) -> List[CompletionItem]: + """Return CompletionItem instead of strings. These give more context to what's being tab completed.""" + items = \ + { + 1: "My item", + 2: "Another item", + 3: "Yet another item" + } + return [CompletionItem(item_id, description) for item_id, description in items.items()] + + # noinspection PyMethodMayBeStatic + def choices_arg_tokens(self, arg_tokens: Dict[str, List[str]]) -> List[str]: + """ + If a choices or completer function/method takes a value called arg_tokens, then it will be + passed a dictionary that maps the command line tokens up through the one being completed + to their argparse argument name. All values of the arg_tokens dictionary are lists, even if + a particular argument expects only 1 token. + """ + # Check if choices_provider flag has appeared + values = ['choices_provider', 'flag'] + if 'choices_provider' in arg_tokens: + values.append('is {}'.format(arg_tokens['choices_provider'][0])) + else: + values.append('not supplied') + return values + # Parser for example command example_parser = Cmd2ArgumentParser(description="Command demonstrating tab completion with argparse\n" "Notice even the flags of this command tab complete") @@ -85,29 +69,25 @@ class ArgparseCompletion(Cmd): example_parser.add_argument('--choices', choices=food_item_strs, metavar="CHOICE", help="tab complete using choices") - # Tab complete from choices provided by a choices function and choices method - example_parser.add_argument('--choices_function', choices_function=choices_function, - help="tab complete using a choices_function") - example_parser.add_argument('--choices_method', choices_method=choices_method, - help="tab complete using a choices_method") + # Tab complete from choices provided by a choices_provider + example_parser.add_argument('--choices_provider', choices_provider=choices_provider, + help="tab complete using a choices_provider") - # Tab complete using a completer function and completer method - example_parser.add_argument('--completer_function', completer_function=completer_function, - help="tab complete using a completer_function") - example_parser.add_argument('--completer_method', completer_method=Cmd.path_complete, - help="tab complete using a completer_method") + # Tab complete using a completer + example_parser.add_argument('--completer', completer=Cmd.path_complete, + help="tab complete using a completer") # Demonstrate raising a CompletionError while tab completing - example_parser.add_argument('--completion_error', choices_method=choices_completion_error, + example_parser.add_argument('--completion_error', choices_provider=choices_completion_error, help="raise a CompletionError while tab completing if debug is False") # Demonstrate returning CompletionItems instead of strings - example_parser.add_argument('--completion_item', choices_function=choices_completion_item, metavar="ITEM_ID", + example_parser.add_argument('--completion_item', choices_provider=choices_completion_item, metavar="ITEM_ID", descriptive_header="Description", help="demonstrate use of CompletionItems") # Demonstrate use of arg_tokens dictionary - example_parser.add_argument('--arg_tokens', choices_function=choices_arg_tokens, + example_parser.add_argument('--arg_tokens', choices_provider=choices_arg_tokens, help="demonstrate use of arg_tokens dictionary") @with_argparser(example_parser) diff --git a/examples/modular_commands/commandset_basic.py b/examples/modular_commands/commandset_basic.py index 2ceda439..9c94e01e 100644 --- a/examples/modular_commands/commandset_basic.py +++ b/examples/modular_commands/commandset_basic.py @@ -4,8 +4,7 @@ A simple example demonstrating a loadable command set """ from typing import List -from cmd2 import Cmd, CommandSet, Statement, with_category, with_default_category -from cmd2.utils import CompletionError +from cmd2 import Cmd, CommandSet, CompletionError, Statement, with_category, with_default_category @with_default_category('Basic Completion') diff --git a/examples/modular_commands/commandset_complex.py b/examples/modular_commands/commandset_complex.py index 7c6b1300..a4343ae3 100644 --- a/examples/modular_commands/commandset_complex.py +++ b/examples/modular_commands/commandset_complex.py @@ -8,7 +8,6 @@ import argparse from typing import List import cmd2 -from cmd2 import utils @cmd2.with_default_category('Fruits') @@ -42,7 +41,7 @@ class CommandSetA(cmd2.CommandSet): self._cmd.poutput(', '.join(['{}']*len(args)).format(*args)) def complete_durian(self, text: str, line: str, begidx: int, endidx: int) -> List[str]: - return utils.basic_complete(text, line, begidx, endidx, ['stinks', 'smells', 'disgusting']) + return self._cmd.basic_complete(text, line, begidx, endidx, ['stinks', 'smells', 'disgusting']) elderberry_parser = cmd2.Cmd2ArgumentParser('elderberry') elderberry_parser.add_argument('arg1') diff --git a/examples/modular_commands_main.py b/examples/modular_commands_main.py index b698e00f..1b0ec64d 100644 --- a/examples/modular_commands_main.py +++ b/examples/modular_commands_main.py @@ -5,58 +5,13 @@ A complex example demonstrating a variety of methods to load CommandSets using a with examples of how to integrate tab completion with argparse-based commands. """ import argparse -from typing import Dict, Iterable, List, Optional +from typing import Iterable, List, Optional -from cmd2 import Cmd, Cmd2ArgumentParser, CommandSet, CompletionItem, with_argparser -from cmd2.utils import CompletionError, basic_complete from modular_commands.commandset_basic import BasicCompletionCommandSet # noqa: F401 from modular_commands.commandset_complex import CommandSetA # noqa: F401 from modular_commands.commandset_custominit import CustomInitCommandSet # noqa: F401 -# Data source for argparse.choices -food_item_strs = ['Pizza', 'Ham', 'Ham Sandwich', 'Potato'] - - -def choices_function() -> List[str]: - """Choices functions are useful when the choice list is dynamically generated (e.g. from data in a database)""" - return ['a', 'dynamic', 'list', 'goes', 'here'] - - -def completer_function(text: str, line: str, begidx: int, endidx: int) -> List[str]: - """ - A tab completion function not dependent on instance data. Since custom tab completion operations commonly - need to modify cmd2's instance variables related to tab completion, it will be rare to need a completer - function. completer_method should be used in those cases. - """ - match_against = ['a', 'dynamic', 'list', 'goes', 'here'] - return basic_complete(text, line, begidx, endidx, match_against) - - -def choices_completion_item() -> List[CompletionItem]: - """Return CompletionItem instead of strings. These give more context to what's being tab completed.""" - items = \ - { - 1: "My item", - 2: "Another item", - 3: "Yet another item" - } - return [CompletionItem(item_id, description) for item_id, description in items.items()] - - -def choices_arg_tokens(arg_tokens: Dict[str, List[str]]) -> List[str]: - """ - If a choices or completer function/method takes a value called arg_tokens, then it will be - passed a dictionary that maps the command line tokens up through the one being completed - to their argparse argument name. All values of the arg_tokens dictionary are lists, even if - a particular argument expects only 1 token. - """ - # Check if choices_function flag has appeared - values = ['choices_function', 'flag'] - if 'choices_function' in arg_tokens: - values.append('is {}'.format(arg_tokens['choices_function'][0])) - else: - values.append('not supplied') - return values +from cmd2 import Cmd, Cmd2ArgumentParser, CommandSet, with_argparser class WithCommandSets(Cmd): @@ -64,55 +19,26 @@ class WithCommandSets(Cmd): super().__init__(command_sets=command_sets) self.sport_item_strs = ['Bat', 'Basket', 'Basketball', 'Football', 'Space Ball'] - def choices_method(self) -> List[str]: - """Choices methods are useful when the choice list is based on instance data of your application""" + def choices_provider(self) -> List[str]: + """A choices provider is useful when the choice list is based on instance data of your application""" return self.sport_item_strs - def choices_completion_error(self) -> List[str]: - """ - CompletionErrors can be raised if an error occurs while tab completing. - - Example use cases - - Reading a database to retrieve a tab completion data set failed - - A previous command line argument that determines the data set being completed is invalid - """ - if self.debug: - return self.sport_item_strs - raise CompletionError("debug must be true") - # Parser for example command example_parser = Cmd2ArgumentParser(description="Command demonstrating tab completion with argparse\n" "Notice even the flags of this command tab complete") # Tab complete from a list using argparse choices. Set metavar if you don't # want the entire choices list showing in the usage text for this command. - example_parser.add_argument('--choices', choices=food_item_strs, metavar="CHOICE", + example_parser.add_argument('--choices', choices=['some', 'choices', 'here'], metavar="CHOICE", help="tab complete using choices") - # Tab complete from choices provided by a choices function and choices method - example_parser.add_argument('--choices_function', choices_function=choices_function, - help="tab complete using a choices_function") - example_parser.add_argument('--choices_method', choices_method=choices_method, - help="tab complete using a choices_method") - - # Tab complete using a completer function and completer method - example_parser.add_argument('--completer_function', completer_function=completer_function, - help="tab complete using a completer_function") - example_parser.add_argument('--completer_method', completer_method=Cmd.path_complete, - help="tab complete using a completer_method") - - # Demonstrate raising a CompletionError while tab completing - example_parser.add_argument('--completion_error', choices_method=choices_completion_error, - help="raise a CompletionError while tab completing if debug is False") - - # Demonstrate returning CompletionItems instead of strings - example_parser.add_argument('--completion_item', choices_function=choices_completion_item, metavar="ITEM_ID", - descriptive_header="Description", - help="demonstrate use of CompletionItems") + # Tab complete from choices provided by a choices provider + example_parser.add_argument('--choices_provider', choices_provider=choices_provider, + help="tab complete using a choices_provider") - # Demonstrate use of arg_tokens dictionary - example_parser.add_argument('--arg_tokens', choices_function=choices_arg_tokens, - help="demonstrate use of arg_tokens dictionary") + # Tab complete using a completer + example_parser.add_argument('--completer', completer=Cmd.path_complete, + help="tab complete using a completer") @with_argparser(example_parser) def do_example(self, _: argparse.Namespace) -> None: diff --git a/tests/test_argparse_completer.py b/tests/test_argparse_completer.py index 151923ea..b896a9bd 100644 --- a/tests/test_argparse_completer.py +++ b/tests/test_argparse_completer.py @@ -9,50 +9,10 @@ from typing import List import pytest import cmd2 -from cmd2 import Cmd2ArgumentParser, CompletionItem, with_argparser -from cmd2.utils import CompletionError, StdSim, basic_complete - +from cmd2 import Cmd2ArgumentParser, CompletionError, CompletionItem, with_argparser +from cmd2.utils import StdSim from .conftest import complete_tester, run_cmd -# Lists used in our tests (there is a mix of sorted and unsorted on purpose) -static_int_choices_list = [-1, 1, -2, 2, 0, -12] -static_choices_list = ['static', 'choices', 'stop', 'here'] -choices_from_function = ['choices', 'function', 'chatty', 'smith'] -choices_from_method = ['choices', 'method', 'most', 'improved'] - -set_value_choices = ['set', 'value', 'choices'] -one_or_more_choices = ['one', 'or', 'more', 'choices'] -optional_choices = ['a', 'few', 'optional', 'choices'] -range_choices = ['some', 'range', 'choices'] -remainder_choices = ['remainder', 'choices'] - -positional_choices = ['the', 'positional', 'choices'] - -completions_from_function = ['completions', 'function', 'fairly', 'complete'] -completions_from_method = ['completions', 'method', 'missed', 'spot'] - - -def choices_function() -> List[str]: - """Function that provides choices""" - return choices_from_function - - -def completer_function(text: str, line: str, begidx: int, endidx: int) -> List[str]: - """Tab completion function""" - return basic_complete(text, line, begidx, endidx, completions_from_function) - - -def choices_takes_arg_tokens(arg_tokens: argparse.Namespace) -> List[str]: - """Choices function that receives arg_tokens from ArgparseCompleter""" - return [arg_tokens['parent_arg'][0], arg_tokens['subcommand'][0]] - - -def completer_takes_arg_tokens(text: str, line: str, begidx: int, endidx: int, - arg_tokens: argparse.Namespace) -> List[str]: - """Completer function that receives arg_tokens from ArgparseCompleter""" - match_against = [arg_tokens['parent_arg'][0], arg_tokens['subcommand'][0]] - return basic_complete(text, line, begidx, endidx, match_against) - # noinspection PyMethodMayBeStatic,PyUnusedLocal,PyProtectedMember class AutoCompleteTester(cmd2.Cmd): @@ -105,15 +65,19 @@ class AutoCompleteTester(cmd2.Cmd): pass ############################################################################################################ - # Begin code related to testing choices, choices_function, and choices_method parameters + # Begin code related to testing choices and choices_provider parameters ############################################################################################################ STR_METAVAR = "HEADLESS" TUPLE_METAVAR = ('arg1', 'others') CUSTOM_DESC_HEADER = "Custom Header" - def choices_method(self) -> List[str]: + static_int_choices_list = [-1, 1, -2, 2, 0, -12] + static_choices_list = ['static', 'choices', 'stop', 'here'] + choices_from_provider = ['choices', 'provider', 'probably', 'improved'] + + def choices_provider(self) -> List[str]: """Method that provides choices""" - return choices_from_method + return self.choices_from_provider def completion_item_method(self) -> List[CompletionItem]: """Choices method that returns CompletionItems""" @@ -128,17 +92,15 @@ class AutoCompleteTester(cmd2.Cmd): # Flag args for choices command. Include string and non-string arg types. choices_parser.add_argument("-l", "--list", help="a flag populated with a choices list", choices=static_choices_list) - choices_parser.add_argument("-f", "--function", help="a flag populated with a choices function", - choices_function=choices_function) - choices_parser.add_argument("-m", "--method", help="a flag populated with a choices method", - choices_method=choices_method) + choices_parser.add_argument("-p", "--provider", help="a flag populated with a choices provider", + choices_provider=choices_provider) choices_parser.add_argument('-d', "--desc_header", help='this arg has a descriptive header', - choices_method=completion_item_method, + choices_provider=completion_item_method, descriptive_header=CUSTOM_DESC_HEADER) choices_parser.add_argument('-n', "--no_header", help='this arg has no descriptive header', - choices_method=completion_item_method, metavar=STR_METAVAR) + choices_provider=completion_item_method, metavar=STR_METAVAR) choices_parser.add_argument('-t', "--tuple_metavar", help='this arg has tuple for a metavar', - choices_method=completion_item_method, metavar=TUPLE_METAVAR, + choices_provider=completion_item_method, metavar=TUPLE_METAVAR, nargs=argparse.ONE_OR_MORE) choices_parser.add_argument('-i', '--int', type=int, help='a flag with an int type', choices=static_int_choices_list) @@ -146,35 +108,40 @@ class AutoCompleteTester(cmd2.Cmd): # Positional args for choices command choices_parser.add_argument("list_pos", help="a positional populated with a choices list", choices=static_choices_list) - choices_parser.add_argument("function_pos", help="a positional populated with a choices function", - choices_function=choices_function) - choices_parser.add_argument("method_pos", help="a positional populated with a choices method", - choices_method=choices_method) + choices_parser.add_argument("method_pos", help="a positional populated with a choices provider", + choices_provider=choices_provider) @with_argparser(choices_parser) def do_choices(self, args: argparse.Namespace) -> None: pass ############################################################################################################ - # Begin code related to testing completer_function and completer_method parameters + # Begin code related to testing completer parameter ############################################################################################################ - def completer_method(self, text: str, line: str, begidx: int, endidx: int) -> List[str]: - """Tab completion method""" - return basic_complete(text, line, begidx, endidx, completions_from_method) + completions_for_flag = ['completions', 'flag', 'fairly', 'complete'] + completions_for_pos_1 = ['completions', 'positional_1', 'probably', 'missed', 'spot'] + completions_for_pos_2 = ['completions', 'positional_2', 'probably', 'missed', 'me'] + + def flag_completer(self, text: str, line: str, begidx: int, endidx: int) -> List[str]: + return self.basic_complete(text, line, begidx, endidx, self.completions_for_flag) + + def pos_1_completer(self, text: str, line: str, begidx: int, endidx: int) -> List[str]: + return self.basic_complete(text, line, begidx, endidx, self.completions_for_pos_1) + + def pos_2_completer(self, text: str, line: str, begidx: int, endidx: int) -> List[str]: + return self.basic_complete(text, line, begidx, endidx, self.completions_for_pos_2) completer_parser = Cmd2ArgumentParser() # Flag args for completer command - completer_parser.add_argument("-f", "--function", help="a flag using a completer function", - completer_function=completer_function) - completer_parser.add_argument("-m", "--method", help="a flag using a completer method", - completer_method=completer_method) + completer_parser.add_argument("-c", "--completer", help="a flag using a completer", + completer=flag_completer) # Positional args for completer command - completer_parser.add_argument("function_pos", help="a positional using a completer function", - completer_function=completer_function) - completer_parser.add_argument("method_pos", help="a positional using a completer method", - completer_method=completer_method) + completer_parser.add_argument("pos_1", help="a positional using a completer method", + completer=pos_1_completer) + completer_parser.add_argument("pos_2", help="a positional using a completer method", + completer=pos_2_completer) @with_argparser(completer_parser) def do_completer(self, args: argparse.Namespace) -> None: @@ -183,6 +150,13 @@ class AutoCompleteTester(cmd2.Cmd): ############################################################################################################ # Begin code related to nargs ############################################################################################################ + set_value_choices = ['set', 'value', 'choices'] + one_or_more_choices = ['one', 'or', 'more', 'choices'] + optional_choices = ['a', 'few', 'optional', 'choices'] + range_choices = ['some', 'range', 'choices'] + remainder_choices = ['remainder', 'choices'] + positional_choices = ['the', 'positional', 'choices'] + nargs_parser = Cmd2ArgumentParser() # Flag args for nargs command @@ -234,10 +208,10 @@ class AutoCompleteTester(cmd2.Cmd): raise CompletionError('choice broke something') comp_error_parser = Cmd2ArgumentParser() - comp_error_parser.add_argument('completer', help='positional arg', - completer_method=completer_raise_error) + comp_error_parser.add_argument('completer_pos', help='positional arg', + completer=completer_raise_error) comp_error_parser.add_argument('--choice', help='flag arg', - choices_method=choice_raise_error) + choices_provider=choice_raise_error) @with_argparser(comp_error_parser) def do_raise_completion_error(self, args: argparse.Namespace) -> None: @@ -246,6 +220,16 @@ class AutoCompleteTester(cmd2.Cmd): ############################################################################################################ # Begin code related to receiving arg_tokens ############################################################################################################ + def choices_takes_arg_tokens(self, arg_tokens: argparse.Namespace) -> List[str]: + """Choices function that receives arg_tokens from ArgparseCompleter""" + return [arg_tokens['parent_arg'][0], arg_tokens['subcommand'][0]] + + def completer_takes_arg_tokens(self, text: str, line: str, begidx: int, endidx: int, + arg_tokens: argparse.Namespace) -> List[str]: + """Completer function that receives arg_tokens from ArgparseCompleter""" + match_against = [arg_tokens['parent_arg'][0], arg_tokens['subcommand'][0]] + return self.basic_complete(text, line, begidx, endidx, match_against) + arg_tokens_parser = Cmd2ArgumentParser() arg_tokens_parser.add_argument('parent_arg', help='arg from a parent parser') @@ -253,8 +237,8 @@ class AutoCompleteTester(cmd2.Cmd): arg_tokens_subparser = arg_tokens_parser.add_subparsers(dest='subcommand') arg_tokens_subcmd_parser = arg_tokens_subparser.add_parser('subcmd') - arg_tokens_subcmd_parser.add_argument('choices_pos', choices_function=choices_takes_arg_tokens) - arg_tokens_subcmd_parser.add_argument('completer_pos', completer_function=completer_takes_arg_tokens) + arg_tokens_subcmd_parser.add_argument('choices_pos', choices_provider=choices_takes_arg_tokens) + arg_tokens_subcmd_parser.add_argument('completer_pos', completer=completer_takes_arg_tokens) # Used to override parent_arg in arg_tokens_parser arg_tokens_subcmd_parser.add_argument('--parent_arg') @@ -409,13 +393,11 @@ def test_autcomp_flag_completion(ac_app, command_and_args, text, completions): @pytest.mark.parametrize('flag, text, completions', [ - ('-l', '', static_choices_list), + ('-l', '', AutoCompleteTester.static_choices_list), ('--list', 's', ['static', 'stop']), - ('-f', '', choices_from_function), - ('--function', 'ch', ['choices', 'chatty']), - ('-m', '', choices_from_method), - ('--method', 'm', ['method', 'most']), - ('-i', '', static_int_choices_list), + ('-p', '', AutoCompleteTester.choices_from_provider), + ('--provider', 'pr', ['provider', 'probably']), + ('-i', '', AutoCompleteTester.static_int_choices_list), ('--int', '1', ['1 ']), ('--int', '-', [-1, -2, -12]), ('--int', '-1', [-1, -12]) @@ -444,12 +426,10 @@ def test_autocomp_flag_choices_completion(ac_app, flag, text, completions): @pytest.mark.parametrize('pos, text, completions', [ - (1, '', static_choices_list), + (1, '', AutoCompleteTester.static_choices_list), (1, 's', ['static', 'stop']), - (2, '', choices_from_function), - (2, 'ch', ['choices', 'chatty']), - (3, '', choices_from_method), - (3, 'm', ['method', 'most']) + (2, '', AutoCompleteTester.choices_from_provider), + (2, 'pr', ['provider', 'probably']), ]) def test_autocomp_positional_choices_completion(ac_app, pos, text, completions): # Generate line were preceding positionals are already filled @@ -467,10 +447,8 @@ def test_autocomp_positional_choices_completion(ac_app, pos, text, completions): @pytest.mark.parametrize('flag, text, completions', [ - ('-f', '', completions_from_function), - ('--function', 'f', ['function', 'fairly']), - ('-m', '', completions_from_method), - ('--method', 'm', ['method', 'missed']) + ('-c', '', AutoCompleteTester.completions_for_flag), + ('--completer', 'f', ['flag', 'fairly']) ]) def test_autocomp_flag_completers(ac_app, flag, text, completions): line = 'completer {} {}'.format(flag, text) @@ -487,10 +465,10 @@ def test_autocomp_flag_completers(ac_app, flag, text, completions): @pytest.mark.parametrize('pos, text, completions', [ - (1, '', completions_from_function), - (1, 'c', ['completions', 'complete']), - (2, '', completions_from_method), - (2, 'm', ['method', 'missed']) + (1, '', AutoCompleteTester.completions_for_pos_1), + (1, 'p', ['positional_1', 'probably']), + (2, '', AutoCompleteTester.completions_for_pos_2), + (2, 'm', ['missed', 'me']), ]) def test_autocomp_positional_completers(ac_app, pos, text, completions): # Generate line were preceding positionals are already filled @@ -513,18 +491,18 @@ def test_autocomp_blank_token(ac_app): blank = '' - # Blank flag arg + # Blank flag arg will be consumed. Therefore we expect to be completing the first positional. text = '' - line = 'completer -m {} {}'.format(blank, text) + line = 'completer -c {} {}'.format(blank, text) endidx = len(line) begidx = endidx - len(text) completer = ArgparseCompleter(ac_app.completer_parser, ac_app) - tokens = ['completer', '-f', blank, text] + tokens = ['completer', '-c', blank, text] completions = completer.complete_command(tokens, text, line, begidx, endidx) - assert completions == completions_from_function + assert sorted(completions) == sorted(AutoCompleteTester.completions_for_pos_1) - # Blank positional arg + # Blank arg for first positional will be consumed. Therefore we expect to be completing the second positional. text = '' line = 'completer {} {}'.format(blank, text) endidx = len(line) @@ -533,7 +511,7 @@ def test_autocomp_blank_token(ac_app): completer = ArgparseCompleter(ac_app.completer_parser, ac_app) tokens = ['completer', blank, text] completions = completer.complete_command(tokens, text, line, begidx, endidx) - assert completions == completions_from_method + assert sorted(completions) == sorted(AutoCompleteTester.completions_for_pos_2) @pytest.mark.parametrize('num_aliases, show_description', [ @@ -567,54 +545,54 @@ def test_completion_items(ac_app, num_aliases, show_description): @pytest.mark.parametrize('args, completions', [ # Flag with nargs = 2 - ('--set_value', set_value_choices), + ('--set_value', AutoCompleteTester.set_value_choices), ('--set_value set', ['value', 'choices']), # Both args are filled. At positional arg now. - ('--set_value set value', positional_choices), + ('--set_value set value', AutoCompleteTester.positional_choices), # Using the flag again will reset the choices available - ('--set_value set value --set_value', set_value_choices), + ('--set_value set value --set_value', AutoCompleteTester.set_value_choices), # Flag with nargs = ONE_OR_MORE - ('--one_or_more', one_or_more_choices), + ('--one_or_more', AutoCompleteTester.one_or_more_choices), ('--one_or_more one', ['or', 'more', 'choices']), # Flag with nargs = OPTIONAL - ('--optional', optional_choices), + ('--optional', AutoCompleteTester.optional_choices), # Only one arg allowed for an OPTIONAL. At positional now. - ('--optional optional', positional_choices), + ('--optional optional', AutoCompleteTester.positional_choices), # Flag with nargs range (1, 2) - ('--range', range_choices), + ('--range', AutoCompleteTester.range_choices), ('--range some', ['range', 'choices']), # Already used 2 args so at positional - ('--range some range', positional_choices), + ('--range some range', AutoCompleteTester.positional_choices), # Flag with nargs = REMAINDER - ('--remainder', remainder_choices), + ('--remainder', AutoCompleteTester.remainder_choices), ('--remainder remainder ', ['choices ']), # No more flags can appear after a REMAINDER flag) ('--remainder choices --set_value', ['remainder ']), # Double dash ends the current flag - ('--range choice --', positional_choices), + ('--range choice --', AutoCompleteTester.positional_choices), # Double dash ends a REMAINDER flag - ('--remainder remainder --', positional_choices), + ('--remainder remainder --', AutoCompleteTester.positional_choices), # No more flags after a double dash - ('-- --one_or_more ', positional_choices), + ('-- --one_or_more ', AutoCompleteTester.positional_choices), # Consume positional - ('', positional_choices), + ('', AutoCompleteTester.positional_choices), ('positional', ['the', 'choices']), # Intermixed flag and positional - ('positional --set_value', set_value_choices), + ('positional --set_value', AutoCompleteTester.set_value_choices), ('positional --set_value set', ['choices', 'value']), # Intermixed flag and positional with flag finishing @@ -622,12 +600,12 @@ def test_completion_items(ac_app, num_aliases, show_description): ('positional --range choice --', ['the', 'choices']), # REMAINDER positional - ('the positional', remainder_choices), + ('the positional', AutoCompleteTester.remainder_choices), ('the positional remainder', ['choices ']), ('the positional remainder choices', []), # REMAINDER positional. Flags don't work in REMAINDER - ('the positional --set_value', remainder_choices), + ('the positional --set_value', AutoCompleteTester.remainder_choices), ('the positional remainder --set_value', ['choices ']) ]) def test_autcomp_nargs(ac_app, args, completions): diff --git a/tests/test_argparse_custom.py b/tests/test_argparse_custom.py index 3ce90118..8a34edb7 100644 --- a/tests/test_argparse_custom.py +++ b/tests/test_argparse_custom.py @@ -41,13 +41,9 @@ def fake_func(): @pytest.mark.parametrize('kwargs, is_valid', [ - ({'choices_function': fake_func}, True), - ({'choices_method': fake_func}, True), - ({'completer_function': fake_func}, True), - ({'completer_method': fake_func}, True), - ({'choices_function': fake_func, 'choices_method': fake_func}, False), - ({'choices_method': fake_func, 'completer_function': fake_func}, False), - ({'completer_function': fake_func, 'completer_method': fake_func}, False), + ({'choices_provider': fake_func}, True), + ({'completer': fake_func}, True), + ({'choices_provider': fake_func, 'completer': fake_func}, False), ]) def test_apcustom_choices_callable_count(kwargs, is_valid): parser = Cmd2ArgumentParser() @@ -60,10 +56,8 @@ def test_apcustom_choices_callable_count(kwargs, is_valid): @pytest.mark.parametrize('kwargs', [ - ({'choices_function': fake_func}), - ({'choices_method': fake_func}), - ({'completer_function': fake_func}), - ({'completer_method': fake_func}) + ({'choices_provider': fake_func}), + ({'completer': fake_func}) ]) def test_apcustom_no_choices_callables_alongside_choices(kwargs): with pytest.raises(TypeError) as excinfo: @@ -73,10 +67,8 @@ def test_apcustom_no_choices_callables_alongside_choices(kwargs): @pytest.mark.parametrize('kwargs', [ - ({'choices_function': fake_func}), - ({'choices_method': fake_func}), - ({'completer_function': fake_func}), - ({'completer_method': fake_func}) + ({'choices_provider': fake_func}), + ({'completer': fake_func}) ]) def test_apcustom_no_choices_callables_when_nargs_is_0(kwargs): with pytest.raises(TypeError) as excinfo: diff --git a/tests/test_completion.py b/tests/test_completion.py index 48a055d0..ac4216bb 100755 --- a/tests/test_completion.py +++ b/tests/test_completion.py @@ -65,13 +65,13 @@ class CompletionsExample(cmd2.Cmd): cmd2.Cmd.__init__(self, multiline_commands=['test_multiline']) self.foo = 'bar' self.add_settable(utils.Settable('foo', str, description="a settable param", - completer_method=CompletionsExample.complete_foo_val)) + completer=CompletionsExample.complete_foo_val)) def do_test_basic(self, args): pass def complete_test_basic(self, text, line, begidx, endidx): - return utils.basic_complete(text, line, begidx, endidx, food_item_strs) + return self.basic_complete(text, line, begidx, endidx, food_item_strs) def do_test_delimited(self, args): pass @@ -84,7 +84,7 @@ class CompletionsExample(cmd2.Cmd): def complete_test_sort_key(self, text, line, begidx, endidx): num_strs = ['2', '11', '1'] - return utils.basic_complete(text, line, begidx, endidx, num_strs) + return self.basic_complete(text, line, begidx, endidx, num_strs) def do_test_raise_exception(self, args): pass @@ -96,7 +96,7 @@ class CompletionsExample(cmd2.Cmd): pass def complete_test_multiline(self, text, line, begidx, endidx): - return utils.basic_complete(text, line, begidx, endidx, sport_item_strs) + return self.basic_complete(text, line, begidx, endidx, sport_item_strs) def do_test_no_completer(self, args): """Completing this should result in completedefault() being called""" @@ -541,7 +541,7 @@ def test_basic_completion_single(cmd2_app): endidx = len(line) begidx = endidx - len(text) - assert utils.basic_complete(text, line, begidx, endidx, food_item_strs) == ['Pizza'] + assert cmd2_app.basic_complete(text, line, begidx, endidx, food_item_strs) == ['Pizza'] def test_basic_completion_multiple(cmd2_app): text = '' @@ -549,7 +549,7 @@ def test_basic_completion_multiple(cmd2_app): endidx = len(line) begidx = endidx - len(text) - matches = sorted(utils.basic_complete(text, line, begidx, endidx, food_item_strs)) + matches = sorted(cmd2_app.basic_complete(text, line, begidx, endidx, food_item_strs)) assert matches == sorted(food_item_strs) def test_basic_completion_nomatch(cmd2_app): @@ -558,7 +558,7 @@ def test_basic_completion_nomatch(cmd2_app): endidx = len(line) begidx = endidx - len(text) - assert utils.basic_complete(text, line, begidx, endidx, food_item_strs) == [] + assert cmd2_app.basic_complete(text, line, begidx, endidx, food_item_strs) == [] def test_delimiter_completion(cmd2_app): text = '/home/' diff --git a/tests_isolated/test_commandset/test_commandset.py b/tests_isolated/test_commandset/test_commandset.py index bab5d536..4b2c2137 100644 --- a/tests_isolated/test_commandset/test_commandset.py +++ b/tests_isolated/test_commandset/test_commandset.py @@ -51,7 +51,7 @@ class CommandSetA(CommandSetBase): self._cmd.last_result = {'args': args} def complete_durian(self, text: str, line: str, begidx: int, endidx: int) -> List[str]: - return utils.basic_complete(text, line, begidx, endidx, ['stinks', 'smells', 'disgusting']) + return self._cmd.basic_complete(text, line, begidx, endidx, ['stinks', 'smells', 'disgusting']) elderberry_parser = cmd2.Cmd2ArgumentParser('elderberry') elderberry_parser.add_argument('arg1') @@ -284,7 +284,6 @@ class LoadableBase(cmd2.CommandSet): self._cmd.pwarning('This command does nothing without sub-parsers registered') self._cmd.do_help('cut') - stir_parser = cmd2.Cmd2ArgumentParser('stir') stir_subparsers = stir_parser.add_subparsers(title='item', help='what to stir') @@ -379,7 +378,7 @@ class LoadableVegetables(cmd2.CommandSet): return ['quartered', 'diced'] bokchoy_parser = cmd2.Cmd2ArgumentParser(add_help=False) - bokchoy_parser.add_argument('style', completer_method=complete_style_arg) + bokchoy_parser.add_argument('style', completer=complete_style_arg) @cmd2.as_subcommand_to('cut', 'bokchoy', bokchoy_parser) def cut_bokchoy(self, ns: argparse.Namespace): @@ -584,7 +583,7 @@ class AppWithSubCommands(cmd2.Cmd): return ['quartered', 'diced'] bokchoy_parser = cmd2.Cmd2ArgumentParser(add_help=False) - bokchoy_parser.add_argument('style', completer_method=complete_style_arg) + bokchoy_parser.add_argument('style', completer=complete_style_arg) @cmd2.as_subcommand_to('cut', 'bokchoy', bokchoy_parser) def cut_bokchoy(self, _: cmd2.Statement): @@ -634,12 +633,12 @@ class WithCompleterCommandSet(cmd2.CommandSet): def complete_states(self, text: str, line: str, begidx: int, endidx: int) -> List[str]: assert self is complete_states_expected_self - return utils.basic_complete(text, line, begidx, endidx, self.states) + return self._cmd.basic_complete(text, line, begidx, endidx, self.states) class SubclassCommandSetCase1(WithCompleterCommandSet): parser = cmd2.Cmd2ArgumentParser() - parser.add_argument('state', type=str, completer_method=WithCompleterCommandSet.complete_states) + parser.add_argument('state', type=str, completer=WithCompleterCommandSet.complete_states) @cmd2.with_argparser(parser) def do_case1(self, ns: argparse.Namespace): @@ -648,7 +647,7 @@ class SubclassCommandSetCase1(WithCompleterCommandSet): class SubclassCommandSetErrorCase2(WithCompleterCommandSet): parser = cmd2.Cmd2ArgumentParser() - parser.add_argument('state', type=str, completer_method=WithCompleterCommandSet.complete_states) + parser.add_argument('state', type=str, completer=WithCompleterCommandSet.complete_states) @cmd2.with_argparser(parser) def do_error2(self, ns: argparse.Namespace): @@ -661,7 +660,7 @@ class SubclassCommandSetCase2(cmd2.CommandSet): super(SubclassCommandSetCase2, self).__init__() parser = cmd2.Cmd2ArgumentParser() - parser.add_argument('state', type=str, completer_method=WithCompleterCommandSet.complete_states) + parser.add_argument('state', type=str, completer=WithCompleterCommandSet.complete_states) @cmd2.with_argparser(parser) def do_case2(self, ns: argparse.Namespace): @@ -790,7 +789,7 @@ class CommandSetWithPathComplete(cmd2.CommandSet): super(CommandSetWithPathComplete, self).__init__() parser = argparse.ArgumentParser() - parser.add_argument('path', nargs='+', help='paths', completer_method=cmd2.Cmd.path_complete) + parser.add_argument('path', nargs='+', help='paths', completer=cmd2.Cmd.path_complete) @cmd2.with_argparser(parser) def do_path(self, app: cmd2.Cmd, args): |