From 878601bc07e5298d50fbf1bd6a8fc2062fef5ed4 Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Mon, 17 Feb 2020 12:45:20 -0500 Subject: Renamed AutoCompleter to ArgparseCompleter for clarity --- CHANGELOG.md | 1 + cmd2/argparse_completer.py | 30 +++++++++++++++--------------- cmd2/argparse_custom.py | 24 ++++++++++++------------ cmd2/cmd2.py | 22 +++++++++++----------- cmd2/utils.py | 4 ++-- tests/test_argparse_completer.py | 24 ++++++++++++------------ 6 files changed, 53 insertions(+), 52 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb328f11..dcd2ef42 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ * Only tab complete after redirection tokens if redirection is allowed * Other * Removed undocumented `py run` command since it was replaced by `run_pyscript` a while ago + * Renamed `AutoCompleter` to `ArgparseCompleter` for clarity ## 0.10.0 (February 7, 2020) * Enhancements diff --git a/cmd2/argparse_completer.py b/cmd2/argparse_completer.py index 185e01a2..eae2ae28 100644 --- a/cmd2/argparse_completer.py +++ b/cmd2/argparse_completer.py @@ -2,7 +2,7 @@ # flake8: noqa C901 # NOTE: Ignoring flake8 cyclomatic complexity in this file """ -This module defines the AutoCompleter class which provides argparse-based tab completion to cmd2 apps. +This module defines the ArgparseCompleter class which provides argparse-based tab completion to cmd2 apps. See the header of argparse_custom.py for instructions on how to use these features. """ @@ -64,7 +64,7 @@ def _looks_like_flag(token: str, parser: argparse.ArgumentParser) -> bool: # noinspection PyProtectedMember -class AutoCompleter: +class ArgparseCompleter: """Automatic command line tab completion based on argparse parameters""" class _ArgumentState: @@ -103,12 +103,12 @@ class AutoCompleter: def __init__(self, parser: argparse.ArgumentParser, cmd2_app: cmd2.Cmd, *, parent_tokens: Optional[Dict[str, List[str]]] = None) -> None: """ - Create an AutoCompleter + Create an ArgparseCompleter :param parser: ArgumentParser instance - :param cmd2_app: reference to the Cmd2 application that owns this AutoCompleter + :param cmd2_app: reference to the Cmd2 application that owns this ArgparseCompleter :param parent_tokens: optional dictionary mapping parent parsers' arg names to their tokens - this is only used by AutoCompleter when recursing on subcommand parsers + This is only used by ArgparseCompleter when recursing on subcommand parsers Defaults to None """ self._parser = parser @@ -167,7 +167,7 @@ class AutoCompleter: # Completed mutually exclusive groups completed_mutex_groups = dict() # dict(argparse._MutuallyExclusiveGroup -> Action which completed group) - def consume_argument(arg_state: AutoCompleter._ArgumentState) -> None: + def consume_argument(arg_state: ArgparseCompleter._ArgumentState) -> None: """Consuming token as an argument""" arg_state.count += 1 consumed_arg_values.setdefault(arg_state.action.dest, []) @@ -286,7 +286,7 @@ class AutoCompleter: # earlier in the command line. Reset them now for this use of it. consumed_arg_values[action.dest] = [] - new_arg_state = AutoCompleter._ArgumentState(action) + new_arg_state = ArgparseCompleter._ArgumentState(action) # Keep track of this flag if it can receive arguments if new_arg_state.max > 0: @@ -319,8 +319,8 @@ class AutoCompleter: if action.dest != argparse.SUPPRESS: parent_tokens[action.dest] = [token] - completer = AutoCompleter(self._subcommand_action.choices[token], self._cmd2_app, - parent_tokens=parent_tokens) + completer = ArgparseCompleter(self._subcommand_action.choices[token], self._cmd2_app, + parent_tokens=parent_tokens) return completer.complete_command(tokens[token_index:], text, line, begidx, endidx) else: # Invalid subcommand entered, so no way to complete remaining tokens @@ -328,7 +328,7 @@ class AutoCompleter: # Otherwise keep track of the argument else: - pos_arg_state = AutoCompleter._ArgumentState(action) + pos_arg_state = ArgparseCompleter._ArgumentState(action) # Check if we have a positional to consume this token if pos_arg_state is not None: @@ -393,7 +393,7 @@ class AutoCompleter: # If we aren't current tracking a positional, then get the next positional arg to handle this token if pos_arg_state is None: action = remaining_positionals.popleft() - pos_arg_state = AutoCompleter._ArgumentState(action) + pos_arg_state = ArgparseCompleter._ArgumentState(action) try: completion_results = self._complete_for_arg(pos_arg_state.action, text, line, @@ -483,11 +483,11 @@ class AutoCompleter: :return: List of subcommand completions """ # If our parser has subcommands, we must examine the tokens and check if they are subcommands - # If so, we will let the subcommand's parser handle the rest of the tokens via another AutoCompleter. + # If so, we will let the subcommand's parser handle the rest of the tokens via another ArgparseCompleter. if self._subcommand_action is not None: for token_index, token in enumerate(tokens[1:], start=1): if token in self._subcommand_action.choices: - completer = AutoCompleter(self._subcommand_action.choices[token], self._cmd2_app) + completer = ArgparseCompleter(self._subcommand_action.choices[token], self._cmd2_app) 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 @@ -503,11 +503,11 @@ class AutoCompleter: :return: help text of the command being queried """ # If our parser has subcommands, we must examine the tokens and check if they are subcommands - # If so, we will let the subcommand's parser handle the rest of the tokens via another AutoCompleter. + # If so, we will let the subcommand's parser handle the rest of the tokens via another ArgparseCompleter. if self._subcommand_action is not None: for token_index, token in enumerate(tokens[1:], start=1): if token in self._subcommand_action.choices: - completer = AutoCompleter(self._subcommand_action.choices[token], self._cmd2_app) + completer = ArgparseCompleter(self._subcommand_action.choices[token], self._cmd2_app) return completer.format_help(tokens[token_index:]) else: break diff --git a/cmd2/argparse_custom.py b/cmd2/argparse_custom.py index a59270c3..fd1ea057 100644 --- a/cmd2/argparse_custom.py +++ b/cmd2/argparse_custom.py @@ -26,7 +26,7 @@ value with no upper bound, use a 1-item tuple (min,) parser.add_argument('-f', nargs=(3, 5)) Tab Completion: - cmd2 uses its AutoCompleter class to enable argparse-based tab completion on all commands that use the + cmd2 uses its ArgparseCompleter class to enable argparse-based tab completion on all commands that use the @with_argparse wrappers. Out of the box you get tab completion of commands, subcommands, and flag names, as well as instructive 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(). @@ -53,7 +53,7 @@ Tab Completion: choices_method This is exactly like choices_function, but the function needs to be an instance method of a cmd2-based class. - When AutoCompleter calls the method, it will pass the app instance as the self argument. This is good in + When ArgparseCompleter calls the method, 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 Example: @@ -74,7 +74,7 @@ Tab Completion: completer_method This is exactly like completer_function, but the function needs to be an instance method of a cmd2-based class. - When AutoCompleter calls the method, it will pass the app instance as the self argument. cmd2 provides + When ArgparseCompleter calls the method, it will pass the app instance as the self argument. cmd2 provides a few completer methods for convenience (e.g., path_complete, delimiter_complete) Example: @@ -113,12 +113,12 @@ Tab Completion: def my_completer_method(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 - AutoCompleter is for tab completion, it does not convert the tokens to their actual argument types or validate + ArgparseCompleter is for tab completion, it does not convert the tokens to their actual argument types or validate their values. All tokens are stored in the dictionary as the raw strings provided on the command line. It is up to the developer to determine if the user entered the correct argument type (e.g. int) and validate their values. CompletionError Class: - Raised during tab completion operations to report any sort of error you want printed by the AutoCompleter + Raised during tab completion operations to report any sort of error you want printed by the ArgparseCompleter Example use cases - Reading a database to retrieve a tab completion data set failed @@ -127,7 +127,7 @@ CompletionError Class: CompletionItem Class: This class was added to help in cases where uninformative data is being tab completed. For instance, tab completing ID numbers isn't very helpful to a user without context. Returning a list of CompletionItems - instead of a regular string for completion results will signal the AutoCompleter to output the completion + instead of a regular string for completion results will signal the ArgparseCompleter to output the completion results in a table of completion tokens with descriptions instead of just a table of tokens. Instead of this: @@ -231,7 +231,7 @@ def generate_range_error(range_min: int, range_max: Union[int, float]) -> str: class CompletionError(Exception): """ - Raised during tab completion operations to report any sort of error you want printed by the AutoCompleter + Raised during tab completion operations to report any sort of error you want printed by the ArgparseCompleter Example use cases - Reading a database to retrieve a tab completion data set failed @@ -353,15 +353,15 @@ def _add_argument_wrapper(self, *args, :param nargs: extends argparse nargs functionality by allowing tuples which specify a range (min, max) to specify a max value with no upper bound, use a 1-item tuple (min,) - # Added args used by AutoCompleter + # 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 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 - value passed for suppress_tab_hint. Defaults to False. + :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 + regardless of the value passed for suppress_tab_hint. Defaults to False. :param descriptive_header: if the provided choices are CompletionItems, then this header will display during tab completion. Defaults to None. diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py index 5a728e56..9e1085b2 100644 --- a/cmd2/cmd2.py +++ b/cmd2/cmd2.py @@ -1425,8 +1425,8 @@ class Cmd(cmd.Cmd): def _autocomplete_default(self, text: str, line: str, begidx: int, endidx: int, *, argparser: argparse.ArgumentParser, preserve_quotes: bool) -> List[str]: """Default completion function for argparse commands""" - from .argparse_completer import AutoCompleter - completer = AutoCompleter(argparser, self) + from .argparse_completer import ArgparseCompleter + completer = ArgparseCompleter(argparser, self) tokens, raw_tokens = self.tokens_for_completion(line, begidx, endidx) # To have tab-completion parsing match command line parsing behavior, @@ -2560,11 +2560,11 @@ class Cmd(cmd.Cmd): if func is None or argparser is None: return [] - # Combine the command and its subcommand tokens for the AutoCompleter + # Combine the command and its subcommand tokens for the ArgparseCompleter tokens = [command] + arg_tokens['subcommands'] - from .argparse_completer import AutoCompleter - completer = AutoCompleter(argparser, self) + from .argparse_completer import ArgparseCompleter + completer = ArgparseCompleter(argparser, self) return completer.complete_subcommand_help(tokens, text, line, begidx, endidx) help_parser = DEFAULT_ARGUMENT_PARSER(description="List available commands or provide " @@ -2576,7 +2576,7 @@ class Cmd(cmd.Cmd): help_parser.add_argument('-v', '--verbose', action='store_true', help="print a list of all commands with descriptions of each") - # Get rid of cmd's complete_help() functions so AutoCompleter will complete the help command + # Get rid of cmd's complete_help() functions so ArgparseCompleter will complete the help command if getattr(cmd.Cmd, 'complete_help', None) is not None: delattr(cmd.Cmd, 'complete_help') @@ -2594,8 +2594,8 @@ class Cmd(cmd.Cmd): # If the command function uses argparse, then use argparse's help if func is not None and argparser is not None: - from .argparse_completer import AutoCompleter - completer = AutoCompleter(argparser, self) + from .argparse_completer import ArgparseCompleter + completer = ArgparseCompleter(argparser, self) tokens = [args.command] + args.subcommands # Set end to blank so the help output matches how it looks when "command -h" is used @@ -2838,8 +2838,8 @@ class Cmd(cmd.Cmd): completer_function=settable.completer_function, completer_method=settable.completer_method) - from .argparse_completer import AutoCompleter - completer = AutoCompleter(settable_parser, self) + from .argparse_completer import ArgparseCompleter + completer = ArgparseCompleter(settable_parser, self) # Use raw_tokens since quotes have been preserved _, raw_tokens = self.tokens_for_completion(line, begidx, endidx) @@ -2860,7 +2860,7 @@ class Cmd(cmd.Cmd): set_parser = DEFAULT_ARGUMENT_PARSER(parents=[set_parser_parent]) # Suppress tab-completion hints for this field. The completer method is going to create an - # AutoCompleter based on the actual parameter being completed and we only want that hint printing. + # ArgparseCompleter based on the actual parameter being completed and we only want that hint printing. set_parser.add_argument('value', nargs=argparse.OPTIONAL, help='new value for settable', completer_method=complete_set_value, suppress_tab_hint=True) diff --git a/cmd2/utils.py b/cmd2/utils.py index e324c2f1..b307e0d2 100644 --- a/cmd2/utils.py +++ b/cmd2/utils.py @@ -109,8 +109,8 @@ class Settable: for this argument (See note below) Note: - For choices_method and completer_method, do not set them to a bound method. This is because AutoCompleter - passes the self argument explicitly to these functions. + For choices_method and completer_method, 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. """ diff --git a/tests/test_argparse_completer.py b/tests/test_argparse_completer.py index 97c75ef3..0eb3892f 100644 --- a/tests/test_argparse_completer.py +++ b/tests/test_argparse_completer.py @@ -42,20 +42,20 @@ def completer_function(text: str, line: str, begidx: int, endidx: int) -> List[s def choices_takes_arg_tokens(arg_tokens: argparse.Namespace) -> List[str]: - """Choices function that receives arg_tokens from AutoCompleter""" + """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 AutoCompleter""" + """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 class AutoCompleteTester(cmd2.Cmd): - """Cmd2 app that exercises AutoCompleter class""" + """Cmd2 app that exercises ArgparseCompleter class""" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -421,7 +421,7 @@ def test_autocomp_flag_choices_completion(ac_app, flag, text, completions): else: assert first_match is None - # Numbers will be sorted in ascending order and then converted to strings by AutoCompleter + # Numbers will be sorted in ascending order and then converted to strings by ArgparseCompleter if all(isinstance(x, numbers.Number) for x in completions): completions.sort() completions = [str(x) for x in completions] @@ -496,8 +496,8 @@ def test_autocomp_positional_completers(ac_app, pos, text, completions): def test_autocomp_blank_token(ac_app): - """Force a blank token to make sure AutoCompleter consumes them like argparse does""" - from cmd2.argparse_completer import AutoCompleter + """Force a blank token to make sure ArgparseCompleter consumes them like argparse does""" + from cmd2.argparse_completer import ArgparseCompleter blank = '' @@ -507,7 +507,7 @@ def test_autocomp_blank_token(ac_app): endidx = len(line) begidx = endidx - len(text) - completer = AutoCompleter(ac_app.completer_parser, ac_app) + completer = ArgparseCompleter(ac_app.completer_parser, ac_app) tokens = ['completer', '-f', blank, text] completions = completer.complete_command(tokens, text, line, begidx, endidx) assert completions == completions_from_function @@ -518,7 +518,7 @@ def test_autocomp_blank_token(ac_app): endidx = len(line) begidx = endidx - len(text) - completer = AutoCompleter(ac_app.completer_parser, 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 @@ -867,20 +867,20 @@ def test_looks_like_flag(): def test_complete_command_no_tokens(ac_app): - from cmd2.argparse_completer import AutoCompleter + from cmd2.argparse_completer import ArgparseCompleter parser = Cmd2ArgumentParser() - ac = AutoCompleter(parser, ac_app) + ac = ArgparseCompleter(parser, ac_app) completions = ac.complete_command(tokens=[], text='', line='', begidx=0, endidx=0) assert not completions def test_complete_command_help_no_tokens(ac_app): - from cmd2.argparse_completer import AutoCompleter + from cmd2.argparse_completer import ArgparseCompleter parser = Cmd2ArgumentParser() - ac = AutoCompleter(parser, ac_app) + ac = ArgparseCompleter(parser, ac_app) completions = ac.complete_subcommand_help(tokens=[], text='', line='', begidx=0, endidx=0) assert not completions -- cgit v1.2.1