diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b353cc00..e45fed587 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -110,6 +110,8 @@ prompt is displayed. now relies exclusively on command function docstrings. - Removed `feedback_to_output` settable and changed `cmd2.Cmd.pfeedback` to always print to `self.stdout` + - Removed `Cmd.parseline()` since it was unused and merely wrapped + `StatementParser.parse_command_only()`. - Enhancements - New `cmd2.Cmd` parameters - **auto_suggest**: (boolean) if `True`, provide fish shell style auto-suggestions. These diff --git a/cmd2/argparse_completer.py b/cmd2/argparse_completer.py index 755bc48c2..d12931dab 100644 --- a/cmd2/argparse_completer.py +++ b/cmd2/argparse_completer.py @@ -158,20 +158,20 @@ class ArgparseCompleter: def __init__( self, parser: Cmd2ArgumentParser, - cmd2_app: "Cmd", + cmd_app: "Cmd", *, parent_tokens: Mapping[str, MutableSequence[str]] | None = None, ) -> None: """Create an ArgparseCompleter. :param parser: Cmd2ArgumentParser instance - :param cmd2_app: reference to the Cmd2 application that owns this ArgparseCompleter + :param cmd_app: reference to the cmd2.Cmd instance that owns this ArgparseCompleter :param parent_tokens: optional Mapping of parent parsers' arg names to their tokens This is only used by ArgparseCompleter when recursing on subcommand parsers Defaults to None """ self._parser = parser - self._cmd2_app = cmd2_app + self._cmd_app = cmd_app if parent_tokens is None: parent_tokens = {} @@ -366,8 +366,8 @@ def consume_argument(arg_state: _ArgumentState, arg_token: str) -> None: parent_tokens[action.dest] = [token] parser = self._subcommand_action.choices[token] - completer_type = self._cmd2_app._determine_ap_completer_type(parser) - completer = completer_type(parser, self._cmd2_app, parent_tokens=parent_tokens) + completer_type = self._cmd_app._determine_ap_completer_type(parser) + completer = completer_type(parser, self._cmd_app, parent_tokens=parent_tokens) return completer.complete(text, line, begidx, endidx, tokens[token_index + 1 :], cmd_set=cmd_set) # Invalid subcommand entered, so no way to complete remaining tokens @@ -544,7 +544,7 @@ def _complete_flags(self, text: str, line: str, begidx: int, endidx: int, used_f # Keep flags sorted in the order provided by argparse so our completion # suggestions display the same as argparse help text. - matched_flags = self._cmd2_app.basic_complete(text, line, begidx, endidx, match_against, sort=False) + matched_flags = self._cmd_app.basic_complete(text, line, begidx, endidx, match_against, sort=False) for flag in matched_flags.to_strings(): action = self._flag_to_action[flag] @@ -613,7 +613,7 @@ def _build_completion_table(self, arg_state: _ArgumentState, completions: Comple # Skip table generation if results are outside thresholds or no columns are defined if ( len(completions) < 2 - or len(completions) > self._cmd2_app.max_completion_table_items + or len(completions) > self._cmd_app.max_completion_table_items or table_columns is None ): # fmt: skip return completions @@ -668,13 +668,13 @@ def complete_subcommand_help(self, text: str, line: str, begidx: int, endidx: in for token_index, token in enumerate(tokens): if token in self._subcommand_action.choices: parser = self._subcommand_action.choices[token] - completer_type = self._cmd2_app._determine_ap_completer_type(parser) - completer = completer_type(parser, self._cmd2_app) + completer_type = self._cmd_app._determine_ap_completer_type(parser) + completer = completer_type(parser, self._cmd_app) return completer.complete_subcommand_help(text, line, begidx, endidx, tokens[token_index + 1 :]) if token_index == len(tokens) - 1: # Since this is the last token, we will attempt to complete it - return self._cmd2_app.basic_complete(text, line, begidx, endidx, self._subcommand_action.choices) + return self._cmd_app.basic_complete(text, line, begidx, endidx, self._subcommand_action.choices) break return Completions() @@ -690,8 +690,8 @@ def print_help(self, tokens: Sequence[str], file: IO[str] | None = None) -> None if tokens and self._subcommand_action is not None: parser = self._subcommand_action.choices.get(tokens[0]) if parser is not None: - completer_type = self._cmd2_app._determine_ap_completer_type(parser) - completer = completer_type(parser, self._cmd2_app) + completer_type = self._cmd_app._determine_ap_completer_type(parser) + completer = completer_type(parser, self._cmd_app) completer.print_help(tokens[1:], file) return self._parser.print_help(file) @@ -732,7 +732,7 @@ def _prepare_callable_params( kwargs: dict[str, Any] = {} # Resolve the 'self' instance for the method - self_arg = self._cmd2_app._resolve_func_self(to_call, cmd_set) + self_arg = self._cmd_app._resolve_func_self(to_call, cmd_set) if self_arg is None: raise CompletionError("Could not find CommandSet instance matching defining type") @@ -794,7 +794,7 @@ def _complete_arg( # Filter used values and run basic completion used_values = consumed_arg_values.get(arg_state.action.dest, []) filtered = [choice for choice in all_choices if choice.text not in used_values] - completions = self._cmd2_app.basic_complete(text, line, begidx, endidx, filtered) + completions = self._cmd_app.basic_complete(text, line, begidx, endidx, filtered) return self._build_completion_table(arg_state, completions) diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py index 1363c7f21..599770983 100644 --- a/cmd2/cmd2.py +++ b/cmd2/cmd2.py @@ -239,12 +239,12 @@ class CommandParsers: Parser creation and retrieval are accomplished through the get() method. """ - def __init__(self, cmd: "Cmd") -> None: + def __init__(self, cmd_app: "Cmd") -> None: """Initialize CommandParsers. - :param cmd: the Cmd instance whose parsers are being managed + :param cmd_app: the Cmd instance whose parsers are being managed """ - self._cmd = cmd + self._cmd_app = cmd_app # Keyed by the fully qualified method names. This is more reliable than # the methods themselves, since wrapping a method will change its address. @@ -285,8 +285,8 @@ def get(self, command_method: BoundCommandFunc) -> Cmd2ArgumentParser | None: if parser_builder is None: return None - parent = self._cmd.find_commandset_for_command(command) or self._cmd - parser = self._cmd._build_parser(parent, parser_builder) + parent = self._cmd_app.find_commandset_for_command(command) or self._cmd_app + parser = self._cmd_app._build_parser(parent, parser_builder) # To ensure accurate usage strings, recursively update 'prog' values # within the parser to match the command name. @@ -2883,15 +2883,6 @@ def postloop(self) -> None: See [Hooks](../features/hooks.md) for more information. """ - def parseline(self, line: str) -> tuple[str, str, str]: - """Parse the line into a command name and a string containing the arguments. - - :param line: line read by prompt-toolkit - :return: tuple containing (command, args, line) - """ - partial_statement = self.statement_parser.parse_command_only(line) - return partial_statement.command, partial_statement.args, partial_statement.command_and_args - def onecmd_plus_hooks( self, line: str, diff --git a/cmd2/decorators.py b/cmd2/decorators.py index be42cc230..975d35088 100644 --- a/cmd2/decorators.py +++ b/cmd2/decorators.py @@ -178,13 +178,13 @@ def cmd_wrapper(*args: Any, **kwargs: Any) -> bool | None: """Command function wrapper which translates command line into an argument list and calls actual command function. :param args: All positional arguments to this function. We're expecting there to be: - cmd2_app, statement: Union[Statement, str] + cmd_app, statement: Union[Statement, str] contiguously somewhere in the list :param kwargs: any keyword arguments being passed to command function :return: return value of command function """ - cmd2_app, statement = _parse_positionals(args) - _, command_arg_list = cmd2_app.statement_parser.get_command_arg_list(command_name, statement, preserve_quotes) + cmd_app, statement = _parse_positionals(args) + _, command_arg_list = cmd_app.statement_parser.get_command_arg_list(command_name, statement, preserve_quotes) func_arg_list = _arg_swap(args, statement, command_arg_list) return func(*func_arg_list, **kwargs) @@ -275,19 +275,19 @@ def cmd_wrapper(*args: Any, **kwargs: Any) -> bool | None: """Command function wrapper which translates command line into argparse Namespace and call actual command function. :param args: All positional arguments to this function. We're expecting there to be: - cmd2_app, statement: Union[Statement, str] + cmd_app, statement: Union[Statement, str] contiguously somewhere in the list :param kwargs: any keyword arguments being passed to command function :return: return value of command function :raises Cmd2ArgparseError: if argparse has error parsing command line """ - cmd2_app, statement_arg = _parse_positionals(args) - statement, command_arg_list = cmd2_app.statement_parser.get_command_arg_list( + cmd_app, statement_arg = _parse_positionals(args) + statement, command_arg_list = cmd_app.statement_parser.get_command_arg_list( command_name, statement_arg, preserve_quotes ) # Pass cmd_wrapper instead of func, since it contains the parser info. - arg_parser = cmd2_app.command_parsers.get(cmd_wrapper) + arg_parser = cmd_app.command_parsers.get(cmd_wrapper) if arg_parser is None: # This shouldn't be possible to reach raise ValueError(f"No argument parser found for {command_name}") # pragma: no cover @@ -298,12 +298,12 @@ def cmd_wrapper(*args: Any, **kwargs: Any) -> bool | None: # The namespace provider may or may not be defined in the same class as the command. Since provider # 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 - provider_self = cmd2_app._resolve_func_self(ns_provider, args[0]) - initial_namespace = ns_provider(provider_self if provider_self is not None else cmd2_app) + provider_self = cmd_app._resolve_func_self(ns_provider, args[0]) + initial_namespace = ns_provider(provider_self if provider_self is not None else cmd_app) try: parsing_results: tuple[argparse.Namespace] | tuple[argparse.Namespace, list[str]] - with arg_parser.output_to(cmd2_app.stdout): + with arg_parser.output_to(cmd_app.stdout): if with_unknown_args: parsing_results = arg_parser.parse_known_args(command_arg_list, initial_namespace) else: diff --git a/cmd2/pt_utils.py b/cmd2/pt_utils.py index bd287d7f2..95ed1369c 100644 --- a/cmd2/pt_utils.py +++ b/cmd2/pt_utils.py @@ -132,7 +132,7 @@ def __init__( custom_settings: utils.CustomCompletionSettings | None = None, ) -> None: """Initialize prompt_toolkit based completer class.""" - self.cmd_app = cmd_app + self._cmd_app = cmd_app self.custom_settings = custom_settings def get_completions(self, document: Document, _complete_event: object) -> Iterable[Completion]: @@ -143,7 +143,7 @@ def get_completions(self, document: Document, _complete_event: object) -> Iterab # Define delimiters for completion to match cmd2/readline behavior delimiters = BASE_DELIMITERS - delimiters += "".join(self.cmd_app.statement_parser.terminators) + delimiters += "".join(self._cmd_app.statement_parser.terminators) # Find last delimiter before cursor to determine the word being completed begidx = 0 @@ -155,7 +155,7 @@ def get_completions(self, document: Document, _complete_event: object) -> Iterab endidx = cursor_pos text = line[begidx:endidx] - completions = self.cmd_app.complete( + completions = self._cmd_app.complete( text, line=line, begidx=begidx, endidx=endidx, custom_settings=self.custom_settings ) @@ -165,7 +165,7 @@ def get_completions(self, document: Document, _complete_event: object) -> Iterab # Print completion table if present if completions.table is not None: - console = ru.Cmd2GeneralConsole(file=self.cmd_app.stdout) + console = ru.Cmd2GeneralConsole(file=self._cmd_app.stdout) with console.capture() as capture: console.print(completions.table, end="", soft_wrap=False) print_formatted_text(pt_filter_style("\n" + capture.get())) @@ -252,7 +252,7 @@ def append_string(self, string: str) -> None: super().append_string(string) def store_string(self, string: str) -> None: - """No-op: Persistent history data is stored in cmd_app.history.""" + """No-op: Persistent history data is stored in cmd2.Cmd.history.""" def load_history_strings(self) -> Iterable[str]: """Yield strings from newest to oldest.""" @@ -287,7 +287,7 @@ def __init__( :param cmd_app: cmd2.Cmd instance """ super().__init__() - self.cmd_app = cmd_app + self._cmd_app = cmd_app _lexers.add(self) self.set_colors() @@ -306,7 +306,7 @@ def lex_document(self, document: Document) -> Callable[[int], Any]: """Lex the document.""" # Get redirection tokens and terminators to avoid highlighting them as values exclude_tokens = set(constants.REDIRECTION_TOKENS) - exclude_tokens.update(self.cmd_app.statement_parser.terminators) + exclude_tokens.update(self._cmd_app.statement_parser.terminators) arg_pattern = re.compile(r'(\s+)|(--?[^\s\'"]+)|("[^"]*"?|\'[^\']*\'?)|([^\s\'"]+)') def highlight_args(text: str, tokens: list[tuple[str, str]]) -> None: @@ -337,7 +337,7 @@ def get_line(lineno: int) -> list[tuple[str, str]]: # Only attempt to match a command on the first line if lineno == 0: # Use cmd2's command pattern to find the first word (the command) - match = self.cmd_app.statement_parser._command_pattern.search(line) + match = self._cmd_app.statement_parser._command_pattern.search(line) if match: # Group 1 is the command, Group 2 is the character(s) that terminated the command match command = match.group(1) @@ -351,7 +351,7 @@ def get_line(lineno: int) -> list[tuple[str, str]]: if command: # Determine the style for the command shortcut_found = False - for shortcut, _ in self.cmd_app.statement_parser.shortcuts: + for shortcut, _ in self._cmd_app.statement_parser.shortcuts: if command.startswith(shortcut): # Add the shortcut with the command style tokens.append((self.command_color, shortcut)) @@ -365,11 +365,11 @@ def get_line(lineno: int) -> list[tuple[str, str]]: if not shortcut_found: style = "" - if command in self.cmd_app.get_all_commands(): + if command in self._cmd_app.get_all_commands(): style = self.command_color - elif command in self.cmd_app.aliases: + elif command in self._cmd_app.aliases: style = self.alias_color - elif command in self.cmd_app.macros: + elif command in self._cmd_app.macros: style = self.macro_color # Add the command with the determined style diff --git a/cmd2/py_bridge.py b/cmd2/py_bridge.py index 15caa7fb2..ff78b4c06 100644 --- a/cmd2/py_bridge.py +++ b/cmd2/py_bridge.py @@ -74,14 +74,14 @@ def __bool__(self) -> bool: class PyBridge: """Provides a Python API wrapper for application commands. - :param cmd2_app: app being controlled by this PyBridge. + :param cmd_app: app being controlled by this PyBridge. :param add_to_history: If True, then add all commands run by this PyBridge to history. Defaults to True. """ - def __init__(self, cmd2_app: "Cmd", *, add_to_history: bool = True) -> None: + def __init__(self, cmd_app: "Cmd", *, add_to_history: bool = True) -> None: """Initialize PyBridge instances.""" - self._cmd2_app = cmd2_app + self._cmd_app = cmd_app self._add_to_history = add_to_history self.cmd_echo = False @@ -100,14 +100,14 @@ def __call__(self, command: str, *, echo: bool | None = None) -> CommandResult: ex: app('help') :param command: command line being run :param echo: If provided, this temporarily overrides the value of self.cmd_echo - while the command runs. If True, output will be echoed to _cmd2_app.stdout + while the command runs. If True, output will be echoed to _cmd_app.stdout and sys.stderr. (Defaults to None) """ if echo is None: echo = self.cmd_echo - # This will be used to capture _cmd2_app.stdout - copy_cmd_stdout = StdSim(cast(TextIO | StdSim, self._cmd2_app.stdout), echo=echo) + # This will be used to capture _cmd_app.stdout + copy_cmd_stdout = StdSim(cast(TextIO | StdSim, self._cmd_app.stdout), echo=echo) # Pause the storing of stdout until onecmd_plus_hooks enables it copy_cmd_stdout.pause_storage = True @@ -115,21 +115,21 @@ def __call__(self, command: str, *, echo: bool | None = None) -> CommandResult: # This will be used to capture sys.stderr copy_stderr = StdSim(sys.stderr, echo=echo) - self._cmd2_app.last_result = None + self._cmd_app.last_result = None stop = False try: - self._cmd2_app.stdout = cast(TextIO, copy_cmd_stdout) + self._cmd_app.stdout = cast(TextIO, copy_cmd_stdout) with redirect_stderr(cast(IO[str], copy_stderr)): - stop = self._cmd2_app.onecmd_plus_hooks( + stop = self._cmd_app.onecmd_plus_hooks( command, add_to_history=self._add_to_history, py_bridge_call=True, ) finally: - with self._cmd2_app.sigint_protection: - self._cmd2_app.stdout = cast(TextIO, copy_cmd_stdout.inner_stream) + with self._cmd_app.sigint_protection: + self._cmd_app.stdout = cast(TextIO, copy_cmd_stdout.inner_stream) self.stop = stop or self.stop # Save the result @@ -137,5 +137,5 @@ def __call__(self, command: str, *, echo: bool | None = None) -> CommandResult: stdout=copy_cmd_stdout.getvalue(), stderr=copy_stderr.getvalue(), stop=stop, - data=self._cmd2_app.last_result, + data=self._cmd_app.last_result, ) diff --git a/tests/conftest.py b/tests/conftest.py index 816d00696..3a37e9856 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -22,15 +22,15 @@ T = TypeVar("T") -def verify_help_text(cmd2_app: cmd2.Cmd, help_output: str | list[str], verbose_strings: list[str] | None = None) -> None: +def verify_help_text(cmd_app: cmd2.Cmd, help_output: str | list[str], verbose_strings: list[str] | None = None) -> None: """This function verifies that all expected commands are present in the help text. - :param cmd2_app: instance of cmd2.Cmd + :param cmd_app: instance of cmd2.Cmd :param help_output: output of help, either as a string or list of strings :param verbose_strings: optional list of verbose strings to search for """ help_text = help_output if isinstance(help_output, str) else "".join(help_output) - commands = cmd2_app.get_visible_commands() + commands = cmd_app.get_visible_commands() for command in commands: assert command in help_text diff --git a/tests/test_argparse_completer.py b/tests/test_argparse_completer.py index b15ada148..6e42ad4c3 100644 --- a/tests/test_argparse_completer.py +++ b/tests/test_argparse_completer.py @@ -1271,7 +1271,7 @@ def _complete_flags(self, text: str, line: str, begidx: int, endidx: int, used_f # Find flags which should not be completed and place them in used_flags for flag in self._flags: action = self._flag_to_action[flag] - app: CustomCompleterApp = cast(CustomCompleterApp, self._cmd2_app) + app: CustomCompleterApp = cast(CustomCompleterApp, self._cmd_app) if action.get_complete_when_ready() and not app.is_ready: used_flags.append(flag) diff --git a/tests/test_cmd2.py b/tests/test_cmd2.py index 8fc0230a2..eed345457 100644 --- a/tests/test_cmd2.py +++ b/tests/test_cmd2.py @@ -3527,26 +3527,6 @@ def test_ppretty(base_app: cmd2.Cmd) -> None: ) -# we override cmd.parseline() so we always get consistent -# command parsing by parent methods we don't override -# don't need to test all the parsing logic here, because -# parseline just calls StatementParser.parse_command_only() -def test_parseline_empty(base_app) -> None: - statement = "" - command, args, line = base_app.parseline(statement) - assert not command - assert not args - assert not line - - -def test_parseline_quoted(base_app) -> None: - statement = " command with 'partially completed quotes " - command, args, line = base_app.parseline(statement) - assert command == "command" - assert args == "with 'partially completed quotes " - assert line == statement.lstrip() - - def test_onecmd_raw_str_continue(outsim_app) -> None: line = "help" stop = outsim_app.onecmd(line) diff --git a/tests/test_completion.py b/tests/test_completion.py index b23cb8a3c..c66639773 100644 --- a/tests/test_completion.py +++ b/tests/test_completion.py @@ -205,52 +205,52 @@ def completedefault(self, *ignored) -> Completions: @pytest.fixture -def cmd2_app(): +def cmd_app(): return CompletionsExample() -def test_command_completion(cmd2_app) -> None: +def test_command_completion(cmd_app) -> None: text = "run" line = text endidx = len(line) begidx = endidx - len(text) expected = ["run_pyscript", "run_script"] - completions = cmd2_app.complete(text, line, begidx, endidx) + completions = cmd_app.complete(text, line, begidx, endidx) assert completions.to_strings() == Completions.from_values(expected).to_strings() -def test_command_completion_nomatch(cmd2_app) -> None: +def test_command_completion_nomatch(cmd_app) -> None: text = "fakecommand" line = text endidx = len(line) begidx = endidx - len(text) - completions = cmd2_app.complete(text, line, begidx, endidx) + completions = cmd_app.complete(text, line, begidx, endidx) assert not completions # ArgparseCompleter raises a _NoResultsError in this case assert "Hint" in completions.error -def test_complete_bogus_command(cmd2_app) -> None: +def test_complete_bogus_command(cmd_app) -> None: text = "" line = f"fizbuzz {text}" endidx = len(line) begidx = endidx - len(text) expected = ["default"] - completions = cmd2_app.complete(text, line, begidx, endidx) + completions = cmd_app.complete(text, line, begidx, endidx) assert completions.to_strings() == Completions.from_values(expected).to_strings() -def test_complete_exception(cmd2_app) -> None: +def test_complete_exception(cmd_app) -> None: text = "" line = f"test_raise_exception {text}" endidx = len(line) begidx = endidx - len(text) - completions = cmd2_app.complete(text, line, begidx, endidx) + completions = cmd_app.complete(text, line, begidx, endidx) assert not completions assert "IndexError" in completions.error @@ -275,7 +275,7 @@ def test_complete_macro(base_app, request) -> None: assert completions.to_strings() == Completions.from_values(expected).to_strings() -def test_default_str_sort_key(cmd2_app) -> None: +def test_default_str_sort_key(cmd_app) -> None: text = "" line = f"test_sort_key {text}" endidx = len(line) @@ -287,51 +287,51 @@ def test_default_str_sort_key(cmd2_app) -> None: # First do alphabetical sorting utils.set_default_str_sort_key(utils.ALPHABETICAL_SORT_KEY) expected = ["file1", "file11", "file2"] - completions = cmd2_app.complete(text, line, begidx, endidx) + completions = cmd_app.complete(text, line, begidx, endidx) assert completions.to_strings() == Completions.from_values(expected).to_strings() # Now switch to natural sorting utils.set_default_str_sort_key(utils.NATURAL_SORT_KEY) expected = ["file1", "file2", "file11"] - completions = cmd2_app.complete(text, line, begidx, endidx) + completions = cmd_app.complete(text, line, begidx, endidx) assert completions.to_strings() == Completions.from_values(expected).to_strings() finally: utils.set_default_str_sort_key(saved_sort_key) -def test_help_completion(cmd2_app) -> None: +def test_help_completion(cmd_app) -> None: text = "h" line = f"help {text}" endidx = len(line) begidx = endidx - len(text) expected = ["help", "history"] - completions = cmd2_app.complete(text, line, begidx, endidx) + completions = cmd_app.complete(text, line, begidx, endidx) assert completions.to_strings() == Completions.from_values(expected).to_strings() -def test_help_completion_empty_arg(cmd2_app) -> None: +def test_help_completion_empty_arg(cmd_app) -> None: text = "" line = f"help {text}" endidx = len(line) begidx = endidx - len(text) - expected = cmd2_app.get_visible_commands() - completions = cmd2_app.complete(text, line, begidx, endidx) + expected = cmd_app.get_visible_commands() + completions = cmd_app.complete(text, line, begidx, endidx) assert completions.to_strings() == Completions.from_values(expected).to_strings() -def test_help_completion_nomatch(cmd2_app) -> None: +def test_help_completion_nomatch(cmd_app) -> None: text = "fakecommand" line = f"help {text}" endidx = len(line) begidx = endidx - len(text) - completions = cmd2_app.complete(text, line, begidx, endidx) + completions = cmd_app.complete(text, line, begidx, endidx) assert not completions -def test_set_allow_style_completion(cmd2_app) -> None: +def test_set_allow_style_completion(cmd_app) -> None: """Confirm that completing allow_style presents AllowStyle strings""" text = "" line = "set allow_style" @@ -339,11 +339,11 @@ def test_set_allow_style_completion(cmd2_app) -> None: begidx = endidx - len(text) expected = [val.name.lower() for val in cmd2.rich_utils.AllowStyle] - completions = cmd2_app.complete(text, line, begidx, endidx) + completions = cmd_app.complete(text, line, begidx, endidx) assert completions.to_strings() == Completions.from_values(expected).to_strings() -def test_set_bool_completion(cmd2_app) -> None: +def test_set_bool_completion(cmd_app) -> None: """Confirm that completing a boolean Settable presents true and false strings""" text = "" line = "set debug" @@ -351,11 +351,11 @@ def test_set_bool_completion(cmd2_app) -> None: begidx = endidx - len(text) expected = ["false", "true"] - completions = cmd2_app.complete(text, line, begidx, endidx) + completions = cmd_app.complete(text, line, begidx, endidx) assert completions.to_strings() == Completions.from_values(expected).to_strings() -def test_shell_command_completion_shortcut(cmd2_app) -> None: +def test_shell_command_completion_shortcut(cmd_app) -> None: # Made sure ! runs a shell command and all matches start with ! since there # isn't a space between ! and the shell command. Display matches won't # begin with the !. @@ -372,12 +372,12 @@ def test_shell_command_completion_shortcut(cmd2_app) -> None: endidx = len(line) begidx = 0 - completions = cmd2_app.complete(text, line, begidx, endidx) + completions = cmd_app.complete(text, line, begidx, endidx) assert completions.to_strings() == expected_completions.to_strings() assert [item.display for item in completions] == [item.display for item in expected_completions] -def test_shell_command_completion_does_not_match_wildcards(cmd2_app) -> None: +def test_shell_command_completion_does_not_match_wildcards(cmd_app) -> None: if sys.platform == "win32": text = "c*" else: @@ -387,11 +387,11 @@ def test_shell_command_completion_does_not_match_wildcards(cmd2_app) -> None: endidx = len(line) begidx = endidx - len(text) - completions = cmd2_app.complete(text, line, begidx, endidx) + completions = cmd_app.complete(text, line, begidx, endidx) assert not completions -def test_shell_command_complete(cmd2_app) -> None: +def test_shell_command_complete(cmd_app) -> None: if sys.platform == "win32": text = "c" expected = "calc.exe" @@ -403,31 +403,31 @@ def test_shell_command_complete(cmd2_app) -> None: endidx = len(line) begidx = endidx - len(text) - completions = cmd2_app.complete(text, line, begidx, endidx) + completions = cmd_app.complete(text, line, begidx, endidx) assert expected in completions.to_strings() -def test_shell_command_completion_nomatch(cmd2_app) -> None: +def test_shell_command_completion_nomatch(cmd_app) -> None: text = "zzzz" line = f"shell {text}" endidx = len(line) begidx = endidx - len(text) - completions = cmd2_app.complete(text, line, begidx, endidx) + completions = cmd_app.complete(text, line, begidx, endidx) assert not completions -def test_shell_command_completion_does_not_complete_when_just_shell(cmd2_app) -> None: +def test_shell_command_completion_does_not_complete_when_just_shell(cmd_app) -> None: text = "" line = f"shell {text}" endidx = len(line) begidx = endidx - len(text) - completions = cmd2_app.complete(text, line, begidx, endidx) + completions = cmd_app.complete(text, line, begidx, endidx) assert not completions -def test_shell_command_completion_does_path_completion_when_after_command(cmd2_app, request) -> None: +def test_shell_command_completion_does_path_completion_when_after_command(cmd_app, request) -> None: test_dir = os.path.dirname(request.module.__file__) text = os.path.join(test_dir, "conftest") @@ -437,11 +437,11 @@ def test_shell_command_completion_does_path_completion_when_after_command(cmd2_a begidx = endidx - len(text) expected = [text + ".py"] - completions = cmd2_app.complete(text, line, begidx, endidx) + completions = cmd_app.complete(text, line, begidx, endidx) assert completions.to_strings() == Completions.from_values(expected).to_strings() -def test_shell_command_complete_in_path(cmd2_app, request) -> None: +def test_shell_command_complete_in_path(cmd_app, request) -> None: test_dir = os.path.dirname(request.module.__file__) text = os.path.join(test_dir, "s") @@ -454,11 +454,11 @@ def test_shell_command_complete_in_path(cmd2_app, request) -> None: # we expect to see the scripts dir among the results expected = os.path.join(test_dir, "scripts" + os.path.sep) - completions = cmd2_app.complete(text, line, begidx, endidx) + completions = cmd_app.complete(text, line, begidx, endidx) assert expected in completions.to_strings() -def test_path_completion_files_and_directories(cmd2_app, request) -> None: +def test_path_completion_files_and_directories(cmd_app, request) -> None: """Test that directories include an ending slash and files do not.""" test_dir = os.path.dirname(request.module.__file__) @@ -469,11 +469,11 @@ def test_path_completion_files_and_directories(cmd2_app, request) -> None: begidx = endidx - len(text) expected = [text + "cript.py", text + "cript.txt", text + "cripts" + os.path.sep] - completions = cmd2_app.path_complete(text, line, begidx, endidx) + completions = cmd_app.path_complete(text, line, begidx, endidx) assert completions.to_strings() == Completions.from_values(expected).to_strings() -def test_path_completion_nomatch(cmd2_app, request) -> None: +def test_path_completion_nomatch(cmd_app, request) -> None: test_dir = os.path.dirname(request.module.__file__) text = os.path.join(test_dir, "fakepath") @@ -482,24 +482,24 @@ def test_path_completion_nomatch(cmd2_app, request) -> None: endidx = len(line) begidx = endidx - len(text) - completions = cmd2_app.path_complete(text, line, begidx, endidx) + completions = cmd_app.path_complete(text, line, begidx, endidx) assert not completions -def test_path_completion_no_text(cmd2_app) -> None: +def test_path_completion_no_text(cmd_app) -> None: # Run path complete with no search text which should show what's in cwd text = "" line = f"shell ls {text}" endidx = len(line) begidx = endidx - len(text) - completions_no_text = cmd2_app.path_complete(text, line, begidx, endidx) + completions_no_text = cmd_app.path_complete(text, line, begidx, endidx) # Run path complete with path set to the CWD text = os.getcwd() + os.path.sep line = f"shell ls {text}" endidx = len(line) begidx = endidx - len(text) - completions_cwd = cmd2_app.path_complete(text, line, begidx, endidx) + completions_cwd = cmd_app.path_complete(text, line, begidx, endidx) # To compare matches, strip off the CWD from the front of completions_cwd. stripped_paths = [CompletionItem(value=item.text.replace(text, "", 1)) for item in completions_cwd] @@ -510,20 +510,20 @@ def test_path_completion_no_text(cmd2_app) -> None: assert completions_cwd -def test_path_completion_no_path(cmd2_app) -> None: +def test_path_completion_no_path(cmd_app) -> None: # Run path complete with search text that isn't preceded by a path. This should use CWD as the path. text = "p" line = f"shell ls {text}" endidx = len(line) begidx = endidx - len(text) - completions_no_text = cmd2_app.path_complete(text, line, begidx, endidx) + completions_no_text = cmd_app.path_complete(text, line, begidx, endidx) # Run path complete with path set to the CWD text = os.getcwd() + os.path.sep + text line = f"shell ls {text}" endidx = len(line) begidx = endidx - len(text) - completions_cwd = cmd2_app.path_complete(text, line, begidx, endidx) + completions_cwd = cmd_app.path_complete(text, line, begidx, endidx) # To compare matches, strip off the CWD from the front of completions_cwd (leave the 's'). stripped_paths = [CompletionItem(value=item.text.replace(text[:-1], "", 1)) for item in completions_cwd] @@ -535,7 +535,7 @@ def test_path_completion_no_path(cmd2_app) -> None: @pytest.mark.skipif(sys.platform == "win32", reason="this only applies on systems where the root directory is a slash") -def test_path_completion_cwd_is_root_dir(cmd2_app) -> None: +def test_path_completion_cwd_is_root_dir(cmd_app) -> None: # Change our CWD to root dir cwd = os.getcwd() try: @@ -545,7 +545,7 @@ def test_path_completion_cwd_is_root_dir(cmd2_app) -> None: line = f"shell ls {text}" endidx = len(line) begidx = endidx - len(text) - completions = cmd2_app.path_complete(text, line, begidx, endidx) + completions = cmd_app.path_complete(text, line, begidx, endidx) # No match should start with a slash assert not any(item.text.startswith(os.path.sep) for item in completions) @@ -554,7 +554,7 @@ def test_path_completion_cwd_is_root_dir(cmd2_app) -> None: os.chdir(cwd) -def test_path_completion_does_not_match_wildcards(cmd2_app, request) -> None: +def test_path_completion_does_not_match_wildcards(cmd_app, request) -> None: test_dir = os.path.dirname(request.module.__file__) text = os.path.join(test_dir, "c*") @@ -564,11 +564,11 @@ def test_path_completion_does_not_match_wildcards(cmd2_app, request) -> None: begidx = endidx - len(text) # Currently path completion doesn't accept wildcards, so will always return empty results - completions = cmd2_app.path_complete(text, line, begidx, endidx) + completions = cmd_app.path_complete(text, line, begidx, endidx) assert not completions -def test_path_completion_complete_user(cmd2_app) -> None: +def test_path_completion_complete_user(cmd_app) -> None: import getpass user = getpass.getuser() @@ -579,11 +579,11 @@ def test_path_completion_complete_user(cmd2_app) -> None: begidx = endidx - len(text) expected = text + os.path.sep - completions = cmd2_app.path_complete(text, line, begidx, endidx) + completions = cmd_app.path_complete(text, line, begidx, endidx) assert expected in completions.to_strings() -def test_path_completion_user_path_expansion(cmd2_app) -> None: +def test_path_completion_user_path_expansion(cmd_app) -> None: # Run path with a tilde and a slash if sys.platform.startswith("win"): cmd = "dir" @@ -595,7 +595,7 @@ def test_path_completion_user_path_expansion(cmd2_app) -> None: line = f"shell {cmd} {text}" endidx = len(line) begidx = endidx - len(text) - completions_tilde_slash = cmd2_app.path_complete(text, line, begidx, endidx) + completions_tilde_slash = cmd_app.path_complete(text, line, begidx, endidx) # To compare matches, strip off ~/ from the front of completions_tilde_slash. stripped_paths = [CompletionItem(value=item.text.replace(text, "", 1)) for item in completions_tilde_slash] @@ -606,7 +606,7 @@ def test_path_completion_user_path_expansion(cmd2_app) -> None: line = f"shell {cmd} {text}" endidx = len(line) begidx = endidx - len(text) - completions_home = cmd2_app.path_complete(text, line, begidx, endidx) + completions_home = cmd_app.path_complete(text, line, begidx, endidx) # To compare matches, strip off user's home directory from the front of completions_home. stripped_paths = [CompletionItem(value=item.text.replace(text, "", 1)) for item in completions_home] @@ -615,28 +615,28 @@ def test_path_completion_user_path_expansion(cmd2_app) -> None: assert completions_tilde_slash == completions_home -def test_basic_completion(cmd2_app) -> None: +def test_basic_completion(cmd_app) -> None: text = "P" line = f"list_food -f {text}" endidx = len(line) begidx = endidx - len(text) expected = ["Pizza", "Potato"] - completions = cmd2_app.basic_complete(text, line, begidx, endidx, food_item_strs) + completions = cmd_app.basic_complete(text, line, begidx, endidx, food_item_strs) assert completions.to_strings() == Completions.from_values(expected).to_strings() -def test_basic_completion_nomatch(cmd2_app) -> None: +def test_basic_completion_nomatch(cmd_app) -> None: text = "q" line = f"list_food -f {text}" endidx = len(line) begidx = endidx - len(text) - completions = cmd2_app.basic_complete(text, line, begidx, endidx, food_item_strs) + completions = cmd_app.basic_complete(text, line, begidx, endidx, food_item_strs) assert not completions -def test_delimiter_completion_partial(cmd2_app) -> None: +def test_delimiter_completion_partial(cmd_app) -> None: """Test that a delimiter is added when an item has not been fully completed""" text = "/home/" line = f"command {text}" @@ -649,13 +649,13 @@ def test_delimiter_completion_partial(cmd2_app) -> None: CompletionItem("/home/user/", display="user/"), ] expected_completions = Completions(expected_items) - completions = cmd2_app.delimiter_complete(text, line, begidx, endidx, delimited_strs, "/") + completions = cmd_app.delimiter_complete(text, line, begidx, endidx, delimited_strs, "/") assert completions.to_strings() == expected_completions.to_strings() assert [item.display for item in completions] == [item.display for item in expected_completions] -def test_delimiter_completion_full(cmd2_app) -> None: +def test_delimiter_completion_full(cmd_app) -> None: """Test that no delimiter is added when an item has been fully completed""" text = "/home/other user/" line = f"command {text}" @@ -668,23 +668,23 @@ def test_delimiter_completion_full(cmd2_app) -> None: CompletionItem("/home/other user/tests", display="tests"), ] expected_completions = Completions(expected_items) - completions = cmd2_app.delimiter_complete(text, line, begidx, endidx, delimited_strs, "/") + completions = cmd_app.delimiter_complete(text, line, begidx, endidx, delimited_strs, "/") assert completions.to_strings() == expected_completions.to_strings() assert [item.display for item in completions] == [item.display for item in expected_completions] -def test_delimiter_completion_nomatch(cmd2_app) -> None: +def test_delimiter_completion_nomatch(cmd_app) -> None: text = "/nothing_to_see" line = f"command {text}" endidx = len(line) begidx = endidx - len(text) - completions = cmd2_app.delimiter_complete(text, line, begidx, endidx, delimited_strs, "/") + completions = cmd_app.delimiter_complete(text, line, begidx, endidx, delimited_strs, "/") assert not completions -def test_tokens_for_completion_quoted(cmd2_app) -> None: +def test_tokens_for_completion_quoted(cmd_app) -> None: text = "Pi" line = f'list_food "{text}"' endidx = len(line) @@ -693,12 +693,12 @@ def test_tokens_for_completion_quoted(cmd2_app) -> None: expected_tokens = ["list_food", "Pi", ""] expected_raw_tokens = ["list_food", '"Pi"', ""] - tokens, raw_tokens = cmd2_app.tokens_for_completion(line, begidx, endidx) + tokens, raw_tokens = cmd_app.tokens_for_completion(line, begidx, endidx) assert expected_tokens == tokens assert expected_raw_tokens == raw_tokens -def test_tokens_for_completion_unclosed_quote(cmd2_app) -> None: +def test_tokens_for_completion_unclosed_quote(cmd_app) -> None: text = "Pi" line = f'list_food "{text}' endidx = len(line) @@ -707,12 +707,12 @@ def test_tokens_for_completion_unclosed_quote(cmd2_app) -> None: expected_tokens = ["list_food", "Pi"] expected_raw_tokens = ["list_food", '"Pi'] - tokens, raw_tokens = cmd2_app.tokens_for_completion(line, begidx, endidx) + tokens, raw_tokens = cmd_app.tokens_for_completion(line, begidx, endidx) assert expected_tokens == tokens assert expected_raw_tokens == raw_tokens -def test_tokens_for_completion_punctuation(cmd2_app) -> None: +def test_tokens_for_completion_punctuation(cmd_app) -> None: """Test that redirectors and terminators are word delimiters""" text = "file" line = f"command | < ;>>{text}" @@ -722,12 +722,12 @@ def test_tokens_for_completion_punctuation(cmd2_app) -> None: expected_tokens = ["command", "|", "<", ";", ">>", "file"] expected_raw_tokens = ["command", "|", "<", ";", ">>", "file"] - tokens, raw_tokens = cmd2_app.tokens_for_completion(line, begidx, endidx) + tokens, raw_tokens = cmd_app.tokens_for_completion(line, begidx, endidx) assert expected_tokens == tokens assert expected_raw_tokens == raw_tokens -def test_tokens_for_completion_quoted_punctuation(cmd2_app) -> None: +def test_tokens_for_completion_quoted_punctuation(cmd_app) -> None: """Test that quoted punctuation characters are not word delimiters""" text = ">file" line = f'command "{text}' @@ -737,48 +737,48 @@ def test_tokens_for_completion_quoted_punctuation(cmd2_app) -> None: expected_tokens = ["command", ">file"] expected_raw_tokens = ["command", '">file'] - tokens, raw_tokens = cmd2_app.tokens_for_completion(line, begidx, endidx) + tokens, raw_tokens = cmd_app.tokens_for_completion(line, begidx, endidx) assert expected_tokens == tokens assert expected_raw_tokens == raw_tokens -def test_add_opening_quote_double_quote_added(cmd2_app) -> None: +def test_add_opening_quote_double_quote_added(cmd_app) -> None: text = "Ha" line = f"test_basic {text}" endidx = len(line) begidx = endidx - len(text) # At least one match has a space, so quote them all - completions = cmd2_app.complete(text, line, begidx, endidx) + completions = cmd_app.complete(text, line, begidx, endidx) assert completions._add_opening_quote assert completions._quote_char == '"' -def test_add_opening_quote_single_quote_added(cmd2_app) -> None: +def test_add_opening_quote_single_quote_added(cmd_app) -> None: text = "Ch" line = f"test_basic {text}" endidx = len(line) begidx = endidx - len(text) # At least one match contains a double quote, so quote them all with a single quote - completions = cmd2_app.complete(text, line, begidx, endidx) + completions = cmd_app.complete(text, line, begidx, endidx) assert completions._add_opening_quote assert completions._quote_char == "'" -def test_add_opening_quote_nothing_added(cmd2_app) -> None: +def test_add_opening_quote_nothing_added(cmd_app) -> None: text = "P" line = f"test_basic {text}" endidx = len(line) begidx = endidx - len(text) # No matches have a space so don't quote them - completions = cmd2_app.complete(text, line, begidx, endidx) + completions = cmd_app.complete(text, line, begidx, endidx) assert not completions._add_opening_quote assert not completions._quote_char -def test_word_break_in_quote(cmd2_app) -> None: +def test_word_break_in_quote(cmd_app) -> None: """Test case where search text has a space and is in a quote.""" # Cmd2Completer still performs word breaks after a quote. Since space @@ -795,50 +795,50 @@ def test_word_break_in_quote(cmd2_app) -> None: # anything before the original search text since this is what Cmd2Completer # expects. Therefore the actual match text is 'Sandwich'. expected = ["Sandwich"] - completions = cmd2_app.complete(text, line, begidx, endidx) + completions = cmd_app.complete(text, line, begidx, endidx) assert completions.to_strings() == Completions.from_values(expected).to_strings() -def test_no_completer(cmd2_app) -> None: +def test_no_completer(cmd_app) -> None: text = "" line = f"test_no_completer {text}" endidx = len(line) begidx = endidx - len(text) expected = ["default"] - completions = cmd2_app.complete(text, line, begidx, endidx) + completions = cmd_app.complete(text, line, begidx, endidx) assert completions.to_strings() == Completions.from_values(expected).to_strings() -def test_word_break_in_command(cmd2_app) -> None: +def test_word_break_in_command(cmd_app) -> None: text = "" line = f'"{text}' endidx = len(line) begidx = endidx - len(text) - completions = cmd2_app.complete(text, line, begidx, endidx) + completions = cmd_app.complete(text, line, begidx, endidx) assert not completions -def test_complete_multiline_on_single_line(cmd2_app) -> None: +def test_complete_multiline_on_single_line(cmd_app) -> None: text = "" line = f"test_multiline {text}" endidx = len(line) begidx = endidx - len(text) expected = ["Basket", "Basketball", "Bat", "Football", "Space Ball"] - completions = cmd2_app.complete(text, line, begidx, endidx) + completions = cmd_app.complete(text, line, begidx, endidx) assert completions.to_strings() == Completions.from_values(expected).to_strings() -def test_complete_multiline_on_multiple_lines(cmd2_app) -> None: +def test_complete_multiline_on_multiple_lines(cmd_app) -> None: text = "Ba" line = f"test_multiline\n{text}" endidx = len(line) begidx = endidx - len(text) expected = ["Bat", "Basket", "Basketball"] - completions = cmd2_app.complete(text, line, begidx, endidx) + completions = cmd_app.complete(text, line, begidx, endidx) assert completions.to_strings() == Completions.from_values(expected).to_strings() @@ -1055,9 +1055,9 @@ class RedirCompType(enum.Enum): ("fake > file >>", RedirCompType.NONE), ], ) -def test_redirect_complete(cmd2_app, monkeypatch, line, comp_type) -> None: +def test_redirect_complete(cmd_app, monkeypatch, line, comp_type) -> None: # Test both cases of allow_redirection - cmd2_app.allow_redirection = True + cmd_app.allow_redirection = True for _ in range(2): shell_cmd_complete_mock = mock.MagicMock(name="shell_cmd_complete") monkeypatch.setattr("cmd2.Cmd.shell_cmd_complete", shell_cmd_complete_mock) @@ -1072,7 +1072,7 @@ def test_redirect_complete(cmd2_app, monkeypatch, line, comp_type) -> None: endidx = len(line) begidx = endidx - len(text) - cmd2_app._redirect_complete(text, line, begidx, endidx, default_complete_mock) + cmd_app._redirect_complete(text, line, begidx, endidx, default_complete_mock) if comp_type == RedirCompType.SHELL_CMD: shell_cmd_complete_mock.assert_called_once() @@ -1086,30 +1086,30 @@ def test_redirect_complete(cmd2_app, monkeypatch, line, comp_type) -> None: default_complete_mock.assert_not_called() # Do the next test with allow_redirection as False - cmd2_app.allow_redirection = False + cmd_app.allow_redirection = False if comp_type != RedirCompType.DEFAULT: comp_type = RedirCompType.NONE -def test_complete_set_value(cmd2_app) -> None: +def test_complete_set_value(cmd_app) -> None: text = "" line = f"set foo {text}" endidx = len(line) begidx = endidx - len(text) expected = ["SUCCESS"] - completions = cmd2_app.complete(text, line, begidx, endidx) + completions = cmd_app.complete(text, line, begidx, endidx) assert completions.to_strings() == Completions.from_values(expected).to_strings() assert completions.hint.strip() == "" -def test_complete_set_value_invalid_settable(cmd2_app) -> None: +def test_complete_set_value_invalid_settable(cmd_app) -> None: text = "" line = f"set fake {text}" endidx = len(line) begidx = endidx - len(text) - completions = cmd2_app.complete(text, line, begidx, endidx) + completions = cmd_app.complete(text, line, begidx, endidx) assert not completions assert "fake is not a settable parameter" in completions.error