diff options
author | Todd Leonhardt <todd.leonhardt@gmail.com> | 2021-02-19 21:40:15 -0500 |
---|---|---|
committer | Todd Leonhardt <todd.leonhardt@gmail.com> | 2021-02-19 21:40:15 -0500 |
commit | 486b8c726a7d657ef320e68598077c31fa664790 (patch) | |
tree | 46b53d0f530d6ae273c4379272684ee80026658a | |
parent | 3e180a810e9c4b9d251c135667d1d150b0bbd0dd (diff) | |
download | cmd2-git-486b8c726a7d657ef320e68598077c31fa664790.tar.gz |
Fixed black, isort, flake8, and doc8 issues
-rw-r--r-- | cmd2/argparse_completer.py | 43 | ||||
-rw-r--r-- | cmd2/argparse_custom.py | 30 | ||||
-rw-r--r-- | cmd2/cmd2.py | 230 | ||||
-rw-r--r-- | cmd2/decorators.py | 11 | ||||
-rw-r--r-- | cmd2/exceptions.py | 2 | ||||
-rw-r--r-- | cmd2/utils.py | 19 | ||||
-rwxr-xr-x | examples/arg_decorators.py | 3 | ||||
-rw-r--r-- | examples/argparse_completion.py | 40 | ||||
-rw-r--r-- | examples/modular_commands_main.py | 30 | ||||
-rw-r--r-- | examples/read_input.py | 34 | ||||
-rw-r--r-- | setup.cfg | 2 | ||||
-rw-r--r-- | tests/test_argparse_completer.py | 270 | ||||
-rw-r--r-- | tests/test_argparse_custom.py | 23 | ||||
-rwxr-xr-x | tests/test_cmd2.py | 17 | ||||
-rwxr-xr-x | tests/test_completion.py | 6 | ||||
-rwxr-xr-x | tests/test_history.py | 5 |
16 files changed, 445 insertions, 320 deletions
diff --git a/cmd2/argparse_completer.py b/cmd2/argparse_completer.py index 99378733..f19e9e2c 100644 --- a/cmd2/argparse_completer.py +++ b/cmd2/argparse_completer.py @@ -208,8 +208,9 @@ class ArgparseCompleter: if isinstance(action, argparse._SubParsersAction): self._subcommand_action = action - def complete(self, text: str, line: str, begidx: int, endidx: int, tokens: List[str], *, - cmd_set: Optional[CommandSet] = None) -> List[str]: + def complete( + self, text: str, line: str, begidx: int, endidx: int, tokens: List[str], *, cmd_set: Optional[CommandSet] = None + ) -> List[str]: """ Complete text using argparse metadata @@ -391,10 +392,12 @@ class ArgparseCompleter: if action.dest != argparse.SUPPRESS: parent_tokens[action.dest] = [token] - completer = ArgparseCompleter(self._subcommand_action.choices[token], self._cmd2_app, - parent_tokens=parent_tokens) - return completer.complete(text, line, begidx, endidx, tokens[token_index + 1:], - cmd_set=cmd_set) + completer = ArgparseCompleter( + self._subcommand_action.choices[token], self._cmd2_app, parent_tokens=parent_tokens + ) + return completer.complete( + text, line, begidx, endidx, tokens[token_index + 1 :], cmd_set=cmd_set + ) else: # Invalid subcommand entered, so no way to complete remaining tokens return [] @@ -438,8 +441,9 @@ class ArgparseCompleter: # Check if we are completing a flag's argument if flag_arg_state is not None: - completion_results = self._complete_arg(text, line, begidx, endidx, flag_arg_state, consumed_arg_values, - cmd_set=cmd_set) + completion_results = self._complete_arg( + text, line, begidx, endidx, flag_arg_state, consumed_arg_values, cmd_set=cmd_set + ) # If we have results, then return them if completion_results: @@ -464,8 +468,9 @@ class ArgparseCompleter: action = remaining_positionals.popleft() pos_arg_state = _ArgumentState(action) - completion_results = self._complete_arg(text, line, begidx, endidx, pos_arg_state, consumed_arg_values, - cmd_set=cmd_set) + completion_results = self._complete_arg( + text, line, begidx, endidx, pos_arg_state, consumed_arg_values, cmd_set=cmd_set + ) # If we have results, then return them if completion_results: @@ -587,7 +592,7 @@ class ArgparseCompleter: for token_index, token in enumerate(tokens): if token in self._subcommand_action.choices: completer = ArgparseCompleter(self._subcommand_action.choices[token], self._cmd2_app) - return completer.complete_subcommand_help(text, line, begidx, endidx, tokens[token_index + 1:]) + return completer.complete_subcommand_help(text, line, begidx, endidx, tokens[token_index + 1 :]) elif 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) @@ -607,14 +612,22 @@ class ArgparseCompleter: for token_index, token in enumerate(tokens): if token in self._subcommand_action.choices: completer = ArgparseCompleter(self._subcommand_action.choices[token], self._cmd2_app) - return completer.format_help(tokens[token_index + 1:]) + return completer.format_help(tokens[token_index + 1 :]) else: break return self._parser.format_help() - def _complete_arg(self, text: str, line: str, begidx: int, endidx: int, - arg_state: _ArgumentState, consumed_arg_values: Dict[str, List[str]], *, - cmd_set: Optional[CommandSet] = None) -> List[str]: + def _complete_arg( + self, + text: str, + line: str, + begidx: int, + endidx: int, + arg_state: _ArgumentState, + consumed_arg_values: Dict[str, List[str]], + *, + cmd_set: Optional[CommandSet] = None + ) -> List[str]: """ Tab completion routine for an argparse argument :return: list of completions diff --git a/cmd2/argparse_custom.py b/cmd2/argparse_custom.py index 26921ade..d3f819b2 100644 --- a/cmd2/argparse_custom.py +++ b/cmd2/argparse_custom.py @@ -285,6 +285,7 @@ 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_completer: bool, to_call: Callable): """ Initializer @@ -305,12 +306,13 @@ 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_provider, completer") + err_msg = "None of the following parameters can be used alongside a choices parameter:\n" "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_provider, completer") + err_msg = ( + "None of the following parameters can be used on an action that takes no arguments:\n" + "choices_provider, completer" + ) raise (TypeError(err_msg)) setattr(action, ATTR_CHOICES_CALLABLE, choices_callable) @@ -335,13 +337,16 @@ def set_completer(action: argparse.Action, completer: Callable) -> None: 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_provider: Optional[Callable] = None, - completer: Optional[Callable] = None, - suppress_tab_hint: bool = False, - descriptive_header: Optional[str] = None, - **kwargs) -> argparse.Action: +def _add_argument_wrapper( + self, + *args, + nargs: Union[int, str, Tuple[int], Tuple[int, int], None] = None, + choices_provider: Optional[Callable] = None, + completer: Optional[Callable] = None, + suppress_tab_hint: bool = False, + descriptive_header: Optional[str] = None, + **kwargs +) -> argparse.Action: """ Wrapper around _ActionsContainer.add_argument() which supports more settings used by cmd2 @@ -379,8 +384,7 @@ def _add_argument_wrapper(self, *args, 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_provider, completer") + err_msg = "Only one of the following parameters may be used at a time:\n" "choices_provider, completer" raise (ValueError(err_msg)) # Pre-process special ranged nargs diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py index 88fdcd87..fd757891 100644 --- a/cmd2/cmd2.py +++ b/cmd2/cmd2.py @@ -1208,8 +1208,9 @@ class Cmd(cmd.Cmd): """ 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]: + def delimiter_complete( + self, text: str, line: str, begidx: int, endidx: int, match_against: Iterable, delimiter: str + ) -> List[str]: """ Performs tab completion against a list but each match is split on a delimiter and only the portion of the match being tab completed is shown as the completion suggestions. @@ -1737,8 +1738,9 @@ class Cmd(cmd.Cmd): # Display matches using actual display function. This also redraws the prompt and line. orig_pyreadline_display(matches_to_display) - def _perform_completion(self, text: str, line: str, begidx: int, endidx: int, - custom_settings: Optional[utils.CustomCompletionSettings] = None) -> None: + def _perform_completion( + self, text: str, line: str, begidx: int, endidx: int, custom_settings: Optional[utils.CustomCompletionSettings] = None + ) -> None: """ Helper function for complete() that performs the actual completion @@ -1748,7 +1750,9 @@ class Cmd(cmd.Cmd): :param endidx: the ending index of the prefix text :param custom_settings: optional prepopulated completion settings """ - from .argparse_completer import ArgparseCompleter + from .argparse_completer import ( + ArgparseCompleter, + ) unclosed_quote = '' command = None @@ -1806,9 +1810,9 @@ class Cmd(cmd.Cmd): completer = ArgparseCompleter(argparser, self) preserve_quotes = getattr(func, constants.CMD_ATTR_PRESERVE_QUOTES) - completer_func = functools.partial(completer.complete, - tokens=raw_tokens[1:] if preserve_quotes else tokens[1:], - cmd_set=cmd_set) + completer_func = functools.partial( + completer.complete, tokens=raw_tokens[1:] if preserve_quotes else tokens[1:], cmd_set=cmd_set + ) else: completer_func = self.completedefault @@ -1823,9 +1827,9 @@ class Cmd(cmd.Cmd): # Otherwise we are completing the command token or performing custom completion else: completer = ArgparseCompleter(custom_settings.parser, self) - completer_func = functools.partial(completer.complete, - tokens=raw_tokens if custom_settings.preserve_quotes else tokens, - cmd_set=None) + completer_func = functools.partial( + completer.complete, tokens=raw_tokens if custom_settings.preserve_quotes else tokens, cmd_set=None + ) # Text we need to remove from completions later text_to_remove = '' @@ -1910,8 +1914,9 @@ class Cmd(cmd.Cmd): if len(self.completion_matches) == 1 and self.allow_closing_quote and unclosed_quote: self.completion_matches[0] += unclosed_quote - def complete(self, text: str, state: int, - custom_settings: Optional[utils.CustomCompletionSettings] = None) -> Optional[str]: + def complete( + self, text: str, state: int, custom_settings: Optional[utils.CustomCompletionSettings] = None + ) -> Optional[str]: """Override of cmd2's complete method which returns the next possible completion for 'text' This completer function is called by readline as complete(text, state), for state in 0, 1, 2, …, @@ -1968,8 +1973,12 @@ class Cmd(cmd.Cmd): else: # No shortcut was found. Complete the command token. parser = DEFAULT_ARGUMENT_PARSER(add_help=False) - parser.add_argument('command', metavar="COMMAND", help="command, alias, or macro name", - choices=self._get_commands_aliases_and_macros_for_completion()) + parser.add_argument( + 'command', + metavar="COMMAND", + help="command, alias, or macro name", + choices=self._get_commands_aliases_and_macros_for_completion(), + ) custom_settings = utils.CustomCompletionSettings(parser) self._perform_completion(text, line, begidx, endidx, custom_settings) @@ -2674,14 +2683,18 @@ class Cmd(cmd.Cmd): # Set apply_style to False so default_error's style is not overridden self.perror(err_msg, apply_style=False) - def read_input(self, prompt: str, *, - history: Optional[List[str]] = None, - completion_mode: utils.CompletionMode = utils.CompletionMode.NONE, - preserve_quotes: bool = False, - choices: Iterable = None, - choices_provider: Optional[Callable] = None, - completer: Optional[Callable] = None, - parser: Optional[argparse.ArgumentParser] = None) -> str: + def read_input( + self, + prompt: str, + *, + history: Optional[List[str]] = None, + completion_mode: utils.CompletionMode = utils.CompletionMode.NONE, + preserve_quotes: bool = False, + choices: Iterable = None, + choices_provider: Optional[Callable] = None, + completer: Optional[Callable] = None, + parser: Optional[argparse.ArgumentParser] = None + ) -> str: """ Read input from appropriate stdin value. Also supports tab completion and up-arrow history while input is being entered. @@ -2736,6 +2749,7 @@ class Cmd(cmd.Cmd): # noinspection PyUnusedLocal def complete_none(text: str, state: int): # pragma: no cover return None + complete_func = complete_none # Complete commands @@ -2746,8 +2760,13 @@ class Cmd(cmd.Cmd): else: if parser is None: parser = DEFAULT_ARGUMENT_PARSER(add_help=False) - parser.add_argument('arg', suppress_tab_hint=True, choices=choices, - choices_provider=choices_provider, completer=completer) + parser.add_argument( + 'arg', + suppress_tab_hint=True, + choices=choices, + choices_provider=choices_provider, + completer=completer, + ) custom_settings = utils.CustomCompletionSettings(parser, preserve_quotes=preserve_quotes) complete_func = functools.partial(self.complete, custom_settings=custom_settings) @@ -2976,10 +2995,12 @@ class Cmd(cmd.Cmd): alias_create_parser = DEFAULT_ARGUMENT_PARSER(description=alias_create_description, 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_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=path_complete) + alias_create_parser.add_argument( + 'command', help='what the alias resolves to', 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=path_complete + ) @as_subcommand_to('alias', 'create', alias_create_parser, help=alias_create_description.lower()) def _alias_create(self, args: argparse.Namespace) -> None: @@ -3020,8 +3041,13 @@ class Cmd(cmd.Cmd): alias_delete_parser = DEFAULT_ARGUMENT_PARSER(description=alias_delete_description) alias_delete_parser.add_argument('-a', '--all', action='store_true', help="delete all aliases") - alias_delete_parser.add_argument('names', nargs=argparse.ZERO_OR_MORE, help='alias(es) to delete', - choices_provider=_get_alias_completion_items, descriptive_header='Value') + alias_delete_parser.add_argument( + 'names', + nargs=argparse.ZERO_OR_MORE, + help='alias(es) to delete', + choices_provider=_get_alias_completion_items, + descriptive_header='Value', + ) @as_subcommand_to('alias', 'delete', alias_delete_parser, help=alias_delete_help) def _alias_delete(self, args: argparse.Namespace) -> None: @@ -3049,8 +3075,13 @@ class Cmd(cmd.Cmd): ) alias_list_parser = DEFAULT_ARGUMENT_PARSER(description=alias_list_description) - alias_list_parser.add_argument('names', nargs=argparse.ZERO_OR_MORE, help='alias(es) to list', - choices_provider=_get_alias_completion_items, descriptive_header='Value') + alias_list_parser.add_argument( + 'names', + nargs=argparse.ZERO_OR_MORE, + help='alias(es) to list', + 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: @@ -3146,10 +3177,12 @@ class Cmd(cmd.Cmd): macro_create_parser = DEFAULT_ARGUMENT_PARSER(description=macro_create_description, 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_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=path_complete) + macro_create_parser.add_argument( + 'command', help='what the macro resolves to', 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=path_complete + ) @as_subcommand_to('macro', 'create', macro_create_parser, help=macro_create_help) def _macro_create(self, args: argparse.Namespace) -> None: @@ -3234,8 +3267,13 @@ class Cmd(cmd.Cmd): macro_delete_description = "Delete specified macros or all macros if --all is used" macro_delete_parser = DEFAULT_ARGUMENT_PARSER(description=macro_delete_description) macro_delete_parser.add_argument('-a', '--all', action='store_true', help="delete all macros") - macro_delete_parser.add_argument('names', nargs=argparse.ZERO_OR_MORE, help='macro(s) to delete', - choices_provider=_get_macro_completion_items, descriptive_header='Value') + macro_delete_parser.add_argument( + 'names', + nargs=argparse.ZERO_OR_MORE, + help='macro(s) to delete', + choices_provider=_get_macro_completion_items, + descriptive_header='Value', + ) @as_subcommand_to('macro', 'delete', macro_delete_parser, help=macro_delete_help) def _macro_delete(self, args: argparse.Namespace) -> None: @@ -3263,8 +3301,13 @@ class Cmd(cmd.Cmd): ) macro_list_parser = DEFAULT_ARGUMENT_PARSER(description=macro_list_description) - macro_list_parser.add_argument('names', nargs=argparse.ZERO_OR_MORE, help='macro(s) to list', - choices_provider=_get_macro_completion_items, descriptive_header='Value') + macro_list_parser.add_argument( + 'names', + nargs=argparse.ZERO_OR_MORE, + help='macro(s) to list', + 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: @@ -3323,18 +3366,25 @@ class Cmd(cmd.Cmd): if func is None or argparser is None: return [] - from .argparse_completer import ArgparseCompleter + from .argparse_completer import ( + ArgparseCompleter, + ) + completer = ArgparseCompleter(argparser, self) return completer.complete_subcommand_help(text, line, begidx, endidx, arg_tokens['subcommands']) - help_parser = DEFAULT_ARGUMENT_PARSER(description="List available commands or provide " - "detailed help for a specific command") - help_parser.add_argument('-v', '--verbose', action='store_true', - help="print a list of all commands with descriptions of each") - help_parser.add_argument('command', nargs=argparse.OPTIONAL, help="command to retrieve help for", - completer=complete_help_command) - help_parser.add_argument('subcommands', nargs=argparse.REMAINDER, help="subcommand(s) to retrieve help for", - completer=complete_help_subcommands) + help_parser = DEFAULT_ARGUMENT_PARSER( + description="List available commands or provide " "detailed help for a specific command" + ) + help_parser.add_argument( + '-v', '--verbose', action='store_true', help="print a list of all commands with descriptions of each" + ) + help_parser.add_argument( + 'command', nargs=argparse.OPTIONAL, help="command to retrieve help for", completer=complete_help_command + ) + help_parser.add_argument( + 'subcommands', nargs=argparse.REMAINDER, help="subcommand(s) to retrieve help for", completer=complete_help_subcommands + ) # 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: @@ -3591,12 +3641,19 @@ class Cmd(cmd.Cmd): # Settables with choices list the values of those choices instead of the arg name # in help text and this shows in tab completion hints. Set metavar to avoid this. arg_name = 'value' - settable_parser.add_argument(arg_name, metavar=arg_name, help=settable.description, - choices=settable.choices, - choices_provider=settable.choices_provider, - completer=settable.completer) + settable_parser.add_argument( + arg_name, + metavar=arg_name, + help=settable.description, + choices=settable.choices, + choices_provider=settable.choices_provider, + completer=settable.completer, + ) + + from .argparse_completer import ( + ArgparseCompleter, + ) - from .argparse_completer import ArgparseCompleter completer = ArgparseCompleter(settable_parser, self) # Use raw_tokens since quotes have been preserved @@ -3611,15 +3668,22 @@ class Cmd(cmd.Cmd): "Call with just param to view that parameter's value." ) set_parser_parent = DEFAULT_ARGUMENT_PARSER(description=set_description, add_help=False) - 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_provider=_get_settable_completion_items, descriptive_header='Description') + 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_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=complete_set_value, suppress_tab_hint=True) + set_parser.add_argument( + 'value', nargs=argparse.OPTIONAL, help='new value for settable', completer=complete_set_value, suppress_tab_hint=True + ) # 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) @@ -3680,8 +3744,9 @@ class Cmd(cmd.Cmd): 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=shell_cmd_complete) - shell_parser.add_argument('command_args', nargs=argparse.REMAINDER, help='arguments to pass to command', - completer=path_complete) + shell_parser.add_argument( + 'command_args', nargs=argparse.REMAINDER, help='arguments to pass to command', completer=path_complete + ) # Preserve quotes since we are passing these strings to the shell @with_argparser(shell_parser, preserve_quotes=True) @@ -3979,8 +4044,9 @@ class Cmd(cmd.Cmd): 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=path_complete) - run_pyscript_parser.add_argument('script_arguments', nargs=argparse.REMAINDER, - help='arguments to pass to script', completer=path_complete) + run_pyscript_parser.add_argument( + 'script_arguments', nargs=argparse.REMAINDER, help='arguments to pass to script', completer=path_complete + ) @with_argparser(run_pyscript_parser) def do_run_pyscript(self, args: argparse.Namespace) -> Optional[bool]: @@ -4076,14 +4142,17 @@ class Cmd(cmd.Cmd): history_parser = DEFAULT_ARGUMENT_PARSER(description=history_description) history_action_group = history_parser.add_mutually_exclusive_group() history_action_group.add_argument('-r', '--run', action='store_true', help='run selected history items') - history_action_group.add_argument('-e', '--edit', action='store_true', - 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=path_complete) - history_action_group.add_argument('-t', '--transcript', metavar='TRANSCRIPT_FILE', - help='output commands and results to a transcript file,\nimplies -s', - completer=path_complete) + history_action_group.add_argument('-e', '--edit', action='store_true', 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=path_complete + ) + history_action_group.add_argument( + '-t', + '--transcript', + metavar='TRANSCRIPT_FILE', + help='output commands and results to a transcript file,\nimplies -s', + 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') @@ -4414,8 +4483,9 @@ 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=path_complete) + edit_parser.add_argument( + 'file_path', nargs=argparse.OPTIONAL, 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: @@ -4458,9 +4528,13 @@ 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=path_complete) + run_script_parser.add_argument( + '-t', + '--transcript', + metavar='TRANSCRIPT_FILE', + help='record the output of the script as a transcript file', + completer=path_complete, + ) run_script_parser.add_argument('script_path', help="path to the script file", completer=path_complete) @with_argparser(run_script_parser) diff --git a/cmd2/decorators.py b/cmd2/decorators.py index e3a3eaa1..b18cc046 100644 --- a/cmd2/decorators.py +++ b/cmd2/decorators.py @@ -196,10 +196,13 @@ def _set_parser_prog(parser: argparse.ArgumentParser, prog: str): break -def with_argparser(parser: argparse.ArgumentParser, *, - ns_provider: Optional[Callable[..., argparse.Namespace]] = None, - preserve_quotes: bool = False, - with_unknown_args: bool = False) -> Callable[[argparse.Namespace], Optional[bool]]: +def with_argparser( + parser: argparse.ArgumentParser, + *, + ns_provider: Optional[Callable[..., argparse.Namespace]] = None, + preserve_quotes: bool = False, + with_unknown_args: bool = False +) -> Callable[[argparse.Namespace], Optional[bool]]: """A decorator to alter a cmd2 method to populate its ``args`` argument by parsing arguments with the given instance of argparse.ArgumentParser. diff --git a/cmd2/exceptions.py b/cmd2/exceptions.py index b6e18856..c662d8fa 100644 --- a/cmd2/exceptions.py +++ b/cmd2/exceptions.py @@ -48,6 +48,7 @@ class CompletionError(Exception): - 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 @@ -60,6 +61,7 @@ class CompletionError(Exception): # 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 8dad5f4c..9920ad64 100644 --- a/cmd2/utils.py +++ b/cmd2/utils.py @@ -93,11 +93,18 @@ def str_to_bool(val: str) -> bool: 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_provider: Optional[Callable] = None, - completer: Optional[Callable] = None): + + def __init__( + self, + name: str, + val_type: Callable, + description: str, + *, + onchange_cb: Callable[[str, Any, Any], Any] = None, + choices: Iterable = None, + choices_provider: Optional[Callable] = None, + completer: Optional[Callable] = None + ): """ Settable Initializer @@ -1075,6 +1082,7 @@ def get_defining_class(meth) -> Type: class CompletionMode(Enum): """Enum for what type of tab completion to perform in cmd2.Cmd.read_input()""" + # Tab completion will be disabled during read_input() call # Use of custom up-arrow history supported NONE = 1 @@ -1092,6 +1100,7 @@ class CompletionMode(Enum): class CustomCompletionSettings: """Used by cmd2.Cmd.complete() to tab complete strings other than command arguments""" + def __init__(self, parser: argparse.ArgumentParser, *, preserve_quotes: bool = False): """ Initializer diff --git a/examples/arg_decorators.py b/examples/arg_decorators.py index 49ea9a10..a0d08d43 100755 --- a/examples/arg_decorators.py +++ b/examples/arg_decorators.py @@ -16,8 +16,7 @@ class ArgparsingApp(cmd2.Cmd): fsize_parser = cmd2.Cmd2ArgumentParser(description='Obtain the size of a file') fsize_parser.add_argument('-c', '--comma', action='store_true', 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=cmd2.Cmd.path_complete) + fsize_parser.add_argument('file_path', help='path of file', 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 5355cd87..fa821b77 100644 --- a/examples/argparse_completion.py +++ b/examples/argparse_completion.py @@ -45,12 +45,7 @@ class ArgparseCompletion(Cmd): # 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" - } + 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 @@ -78,29 +73,36 @@ class ArgparseCompletion(Cmd): # want the entire choices list showing in the usage text for this command. example_parser.add_argument('--choices', choices=food_item_strs, metavar="CHOICE", help="tab complete using choices") - example_parser.add_argument('--choices', choices=food_item_strs, metavar="CHOICE", - help="tab complete using choices") + example_parser.add_argument('--choices', choices=food_item_strs, metavar="CHOICE", help="tab complete using choices") # 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") + example_parser.add_argument( + '--choices_provider', choices_provider=choices_provider, help="tab complete using a choices_provider" + ) # Tab complete using a completer - example_parser.add_argument('--completer', completer=Cmd.path_complete, - help="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_provider=choices_completion_error, - help="raise a CompletionError while tab completing if debug is False") + 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_provider=choices_completion_item, metavar="ITEM_ID", - descriptive_header="Description", - help="demonstrate use of CompletionItems") + 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_provider=choices_arg_tokens, - help="demonstrate use of arg_tokens dictionary") + example_parser.add_argument( + '--arg_tokens', choices_provider=choices_arg_tokens, help="demonstrate use of arg_tokens dictionary" + ) @with_argparser(example_parser) def do_example(self, _: argparse.Namespace) -> None: diff --git a/examples/modular_commands_main.py b/examples/modular_commands_main.py index fc0be7b0..3aeb8b2a 100644 --- a/examples/modular_commands_main.py +++ b/examples/modular_commands_main.py @@ -11,20 +11,21 @@ from typing import ( Optional, ) -from cmd2 import ( - Cmd, - Cmd2ArgumentParser, - CommandSet, - with_argparser, -) from modular_commands.commandset_basic import ( # noqa: F401 BasicCompletionCommandSet, ) from modular_commands.commandset_complex import ( # noqa: F401 CommandSetA, ) -from modular_commands.commandset_custominit import ( - CustomInitCommandSet, # noqa: F401 +from modular_commands.commandset_custominit import ( # noqa: F401 + CustomInitCommandSet, +) + +from cmd2 import ( + Cmd, + Cmd2ArgumentParser, + CommandSet, + with_argparser, ) @@ -44,16 +45,17 @@ class WithCommandSets(Cmd): # 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=['some', 'choices', 'here'], metavar="CHOICE", - help="tab complete using choices") + example_parser.add_argument( + '--choices', choices=['some', 'choices', 'here'], metavar="CHOICE", help="tab complete using choices" + ) # 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") + example_parser.add_argument( + '--choices_provider', choices_provider=choices_provider, help="tab complete using a choices_provider" + ) # Tab complete using a completer - example_parser.add_argument('--completer', completer=Cmd.path_complete, - help="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/examples/read_input.py b/examples/read_input.py index e772a106..f45fc7c8 100644 --- a/examples/read_input.py +++ b/examples/read_input.py @@ -3,7 +3,9 @@ """ A simple example demonstrating the various ways to call cmd2.Cmd.read_input() for input history and tab completion """ -from typing import List +from typing import ( + List, +) import cmd2 @@ -50,8 +52,12 @@ class ReadInputApp(cmd2.Cmd): """Call read_input to use custom history and choices""" self.poutput("Tab completing with static choices list and using custom history") try: - input_str = self.read_input("> ", history=self.custom_history, completion_mode=cmd2.CompletionMode.CUSTOM, - choices=['choice_1', 'choice_2', 'choice_3']) + input_str = self.read_input( + "> ", + history=self.custom_history, + completion_mode=cmd2.CompletionMode.CUSTOM, + choices=['choice_1', 'choice_2', 'choice_3'], + ) except EOFError: pass else: @@ -67,8 +73,12 @@ class ReadInputApp(cmd2.Cmd): """Call read_input to use custom history and choices provider function""" self.poutput("Tab completing with choices from provider function and using custom history") try: - input_str = self.read_input("> ", history=self.custom_history, completion_mode=cmd2.CompletionMode.CUSTOM, - choices_provider=ReadInputApp.choices_provider) + input_str = self.read_input( + "> ", + history=self.custom_history, + completion_mode=cmd2.CompletionMode.CUSTOM, + choices_provider=ReadInputApp.choices_provider, + ) except EOFError: pass else: @@ -79,8 +89,9 @@ class ReadInputApp(cmd2.Cmd): """all read_input to use custom history and completer function""" self.poutput("Tab completing paths and using custom history") try: - input_str = self.read_input("> ", history=self.custom_history, completion_mode=cmd2.CompletionMode.CUSTOM, - completer=cmd2.Cmd.path_complete) + input_str = self.read_input( + "> ", history=self.custom_history, completion_mode=cmd2.CompletionMode.CUSTOM, completer=cmd2.Cmd.path_complete + ) self.custom_history.append(input_str) except EOFError: pass @@ -90,16 +101,16 @@ class ReadInputApp(cmd2.Cmd): """Call read_input to use a custom history and an argument parser""" parser = cmd2.Cmd2ArgumentParser(prog='', description="An example parser") parser.add_argument('-o', '--option', help="an optional arg") - parser.add_argument('arg_1', help="a choice for this arg", metavar='arg_1', - choices=['my_choice', 'your_choice']) + parser.add_argument('arg_1', help="a choice for this arg", metavar='arg_1', choices=['my_choice', 'your_choice']) parser.add_argument('arg_2', help="path of something", completer=cmd2.Cmd.path_complete) self.poutput("Tab completing with argument parser and using custom history") self.poutput(parser.format_usage()) try: - input_str = self.read_input("> ", history=self.custom_history, completion_mode=cmd2.CompletionMode.CUSTOM, - parser=parser) + input_str = self.read_input( + "> ", history=self.custom_history, completion_mode=cmd2.CompletionMode.CUSTOM, parser=parser + ) except EOFError: pass else: @@ -108,5 +119,6 @@ class ReadInputApp(cmd2.Cmd): if __name__ == '__main__': import sys + app = ReadInputApp() sys.exit(app.cmdloop()) @@ -35,5 +35,5 @@ use_parentheses = true [doc8] ignore-path=docs/_build,.git,.idea,.pytest_cache,.tox,.nox,.venv,.vscode,build,cmd2,examples,tests,cmd2.egg-info,dist,htmlcov,__pycache__,*.egg,plugins -max-line-length=117 +max-line-length=120 verbose=0 diff --git a/tests/test_argparse_completer.py b/tests/test_argparse_completer.py index f3f37c4e..efda7660 100644 --- a/tests/test_argparse_completer.py +++ b/tests/test_argparse_completer.py @@ -122,30 +122,43 @@ class ArgparseCompleterTester(cmd2.Cmd): choices_parser = Cmd2ArgumentParser() # 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("-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_provider=completion_item_method, - descriptive_header=CUSTOM_DESC_HEADER) - choices_parser.add_argument('-n', "--no_header", help='this arg has no descriptive header', - choices_provider=completion_item_method, metavar=STR_METAVAR) - choices_parser.add_argument('-t', "--tuple_metavar", help='this arg has tuple for a 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=int_choices) + choices_parser.add_argument("-l", "--list", help="a flag populated with a choices list", choices=static_choices_list) + 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_provider=completion_item_method, + descriptive_header=CUSTOM_DESC_HEADER, + ) + choices_parser.add_argument( + '-n', + "--no_header", + help='this arg has no descriptive header', + choices_provider=completion_item_method, + metavar=STR_METAVAR, + ) + choices_parser.add_argument( + '-t', + "--tuple_metavar", + help='this arg has tuple for a 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=int_choices) # 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("method_pos", help="a positional populated with a choices provider", - choices_provider=choices_provider) - choices_parser.add_argument('non_negative_int', type=int, help='a positional with non-negative int choices', - choices=non_negative_int_choices) - choices_parser.add_argument('empty_choices', help='a positional with empty choices', - choices=[]) + choices_parser.add_argument("list_pos", help="a positional populated with a choices list", choices=static_choices_list) + choices_parser.add_argument( + "method_pos", help="a positional populated with a choices provider", choices_provider=choices_provider + ) + choices_parser.add_argument( + 'non_negative_int', type=int, help='a positional with non-negative int choices', choices=non_negative_int_choices + ) + choices_parser.add_argument('empty_choices', help='a positional with empty choices', choices=[]) @with_argparser(choices_parser) def do_choices(self, args: argparse.Namespace) -> None: @@ -170,14 +183,11 @@ class ArgparseCompleterTester(cmd2.Cmd): completer_parser = Cmd2ArgumentParser() # Flag args for completer command - completer_parser.add_argument("-c", "--completer", help="a flag using a completer", - completer=flag_completer) + completer_parser.add_argument("-c", "--completer", help="a flag using a completer", completer=flag_completer) # Positional args for completer command - 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) + 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: @@ -245,10 +255,8 @@ class ArgparseCompleterTester(cmd2.Cmd): raise CompletionError('choice broke something') comp_error_parser = Cmd2ArgumentParser() - comp_error_parser.add_argument('completer_pos', help='positional arg', - completer=completer_raise_error) - comp_error_parser.add_argument('--choice', help='flag arg', - choices_provider=choice_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_provider=choice_raise_error) @with_argparser(comp_error_parser) def do_raise_completion_error(self, args: argparse.Namespace) -> None: @@ -261,8 +269,9 @@ class ArgparseCompleterTester(cmd2.Cmd): """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]: + 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) @@ -516,20 +525,24 @@ def test_autcomp_flag_completion(ac_app, command_and_args, text, completion_matc else: assert first_match is None - assert (ac_app.completion_matches == sorted(completion_matches, key=ac_app.default_sort_key) and - ac_app.display_matches == sorted(display_matches, key=ac_app.default_sort_key)) + assert ac_app.completion_matches == sorted( + completion_matches, key=ac_app.default_sort_key + ) and ac_app.display_matches == sorted(display_matches, key=ac_app.default_sort_key) -@pytest.mark.parametrize('flag, text, completions', [ - ('-l', '', ArgparseCompleterTester.static_choices_list), - ('--list', 's', ['static', 'stop']), - ('-p', '', ArgparseCompleterTester.choices_from_provider), - ('--provider', 'pr', ['provider', 'probably']), - ('-i', '', ArgparseCompleterTester.int_choices), - ('--int', '1', ['1 ']), - ('--int', '-', [-1, -2, -12]), - ('--int', '-1', [-1, -12]) -]) +@pytest.mark.parametrize( + 'flag, text, completions', + [ + ('-l', '', ArgparseCompleterTester.static_choices_list), + ('--list', 's', ['static', 'stop']), + ('-p', '', ArgparseCompleterTester.choices_from_provider), + ('--provider', 'pr', ['provider', 'probably']), + ('-i', '', ArgparseCompleterTester.int_choices), + ('--int', '1', ['1 ']), + ('--int', '-', [-1, -2, -12]), + ('--int', '-1', [-1, -12]), + ], +) def test_autocomp_flag_choices_completion(ac_app, flag, text, completions): line = 'choices {} {}'.format(flag, text) endidx = len(line) @@ -551,15 +564,18 @@ def test_autocomp_flag_choices_completion(ac_app, flag, text, completions): assert ac_app.completion_matches == completions -@pytest.mark.parametrize('pos, text, completions', [ - (1, '', ArgparseCompleterTester.static_choices_list), - (1, 's', ['static', 'stop']), - (2, '', ArgparseCompleterTester.choices_from_provider), - (2, 'pr', ['provider', 'probably']), - (3, '', ArgparseCompleterTester.non_negative_int_choices), - (3, '2', [2, 22]), - (4, '', []), -]) +@pytest.mark.parametrize( + 'pos, text, completions', + [ + (1, '', ArgparseCompleterTester.static_choices_list), + (1, 's', ['static', 'stop']), + (2, '', ArgparseCompleterTester.choices_from_provider), + (2, 'pr', ['provider', 'probably']), + (3, '', ArgparseCompleterTester.non_negative_int_choices), + (3, '2', [2, 22]), + (4, '', []), + ], +) def test_autocomp_positional_choices_completion(ac_app, pos, text, completions): # Generate line were preceding positionals are already filled line = 'choices {} {}'.format('foo ' * (pos - 1), text) @@ -603,10 +619,10 @@ def test_flag_sorting(ac_app): assert first_match is not None and ac_app.completion_matches == option_strings -@pytest.mark.parametrize('flag, text, completions', [ - ('-c', '', ArgparseCompleterTester.completions_for_flag), - ('--completer', 'f', ['flag', 'fairly']) -]) +@pytest.mark.parametrize( + 'flag, text, completions', + [('-c', '', ArgparseCompleterTester.completions_for_flag), ('--completer', 'f', ['flag', 'fairly'])], +) def test_autocomp_flag_completers(ac_app, flag, text, completions): line = 'completer {} {}'.format(flag, text) endidx = len(line) @@ -621,12 +637,15 @@ def test_autocomp_flag_completers(ac_app, flag, text, completions): assert ac_app.completion_matches == sorted(completions, key=ac_app.default_sort_key) -@pytest.mark.parametrize('pos, text, completions', [ - (1, '', ArgparseCompleterTester.completions_for_pos_1), - (1, 'p', ['positional_1', 'probably']), - (2, '', ArgparseCompleterTester.completions_for_pos_2), - (2, 'm', ['missed', 'me']), -]) +@pytest.mark.parametrize( + 'pos, text, completions', + [ + (1, '', ArgparseCompleterTester.completions_for_pos_1), + (1, 'p', ['positional_1', 'probably']), + (2, '', ArgparseCompleterTester.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 line = 'completer {} {}'.format('foo ' * (pos - 1), text) @@ -705,71 +724,57 @@ def test_completion_items(ac_app, num_aliases, show_description): assert ('help' in ac_app.display_matches[0]) == show_description -@pytest.mark.parametrize('args, completions', [ - # Flag with nargs = 2 - ('--set_value', ArgparseCompleterTester.set_value_choices), - ('--set_value set', ['value', 'choices']), - - # Both args are filled. At positional arg now. - ('--set_value set value', ArgparseCompleterTester.positional_choices), - - # Using the flag again will reset the choices available - ('--set_value set value --set_value', ArgparseCompleterTester.set_value_choices), - - # Flag with nargs = ONE_OR_MORE - ('--one_or_more', ArgparseCompleterTester.one_or_more_choices), - ('--one_or_more one', ['or', 'more', 'choices']), - - # Flag with nargs = OPTIONAL - ('--optional', ArgparseCompleterTester.optional_choices), - - # Only one arg allowed for an OPTIONAL. At positional now. - ('--optional optional', ArgparseCompleterTester.positional_choices), - - # Flag with nargs range (1, 2) - ('--range', ArgparseCompleterTester.range_choices), - ('--range some', ['range', 'choices']), - - # Already used 2 args so at positional - ('--range some range', ArgparseCompleterTester.positional_choices), - - # Flag with nargs = REMAINDER - ('--remainder', ArgparseCompleterTester.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 --', ArgparseCompleterTester.positional_choices), - - # Double dash ends a REMAINDER flag - ('--remainder remainder --', ArgparseCompleterTester.positional_choices), - - # No more flags after a double dash - ('-- --one_or_more ', ArgparseCompleterTester.positional_choices), - - # Consume positional - ('', ArgparseCompleterTester.positional_choices), - ('positional', ['the', 'choices']), - - # Intermixed flag and positional - ('positional --set_value', ArgparseCompleterTester.set_value_choices), - ('positional --set_value set', ['choices', 'value']), - - # Intermixed flag and positional with flag finishing - ('positional --set_value set value', ['the', 'choices']), - ('positional --range choice --', ['the', 'choices']), - - # REMAINDER positional - ('the positional', ArgparseCompleterTester.remainder_choices), - ('the positional remainder', ['choices ']), - ('the positional remainder choices', []), - - # REMAINDER positional. Flags don't work in REMAINDER - ('the positional --set_value', ArgparseCompleterTester.remainder_choices), - ('the positional remainder --set_value', ['choices ']) -]) +@pytest.mark.parametrize( + 'args, completions', + [ + # Flag with nargs = 2 + ('--set_value', ArgparseCompleterTester.set_value_choices), + ('--set_value set', ['value', 'choices']), + # Both args are filled. At positional arg now. + ('--set_value set value', ArgparseCompleterTester.positional_choices), + # Using the flag again will reset the choices available + ('--set_value set value --set_value', ArgparseCompleterTester.set_value_choices), + # Flag with nargs = ONE_OR_MORE + ('--one_or_more', ArgparseCompleterTester.one_or_more_choices), + ('--one_or_more one', ['or', 'more', 'choices']), + # Flag with nargs = OPTIONAL + ('--optional', ArgparseCompleterTester.optional_choices), + # Only one arg allowed for an OPTIONAL. At positional now. + ('--optional optional', ArgparseCompleterTester.positional_choices), + # Flag with nargs range (1, 2) + ('--range', ArgparseCompleterTester.range_choices), + ('--range some', ['range', 'choices']), + # Already used 2 args so at positional + ('--range some range', ArgparseCompleterTester.positional_choices), + # Flag with nargs = REMAINDER + ('--remainder', ArgparseCompleterTester.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 --', ArgparseCompleterTester.positional_choices), + # Double dash ends a REMAINDER flag + ('--remainder remainder --', ArgparseCompleterTester.positional_choices), + # No more flags after a double dash + ('-- --one_or_more ', ArgparseCompleterTester.positional_choices), + # Consume positional + ('', ArgparseCompleterTester.positional_choices), + ('positional', ['the', 'choices']), + # Intermixed flag and positional + ('positional --set_value', ArgparseCompleterTester.set_value_choices), + ('positional --set_value set', ['choices', 'value']), + # Intermixed flag and positional with flag finishing + ('positional --set_value set value', ['the', 'choices']), + ('positional --range choice --', ['the', 'choices']), + # REMAINDER positional + ('the positional', ArgparseCompleterTester.remainder_choices), + ('the positional remainder', ['choices ']), + ('the positional remainder choices', []), + # REMAINDER positional. Flags don't work in REMAINDER + ('the positional --set_value', ArgparseCompleterTester.remainder_choices), + ('the positional remainder --set_value', ['choices ']), + ], +) def test_autcomp_nargs(ac_app, args, completions): text = '' line = 'nargs {} {}'.format(args, text) @@ -1105,10 +1110,7 @@ def test_complete_command_help_no_tokens(ac_app): assert not completions -@pytest.mark.parametrize('flag, completions', [ - ('--provider', standalone_choices), - ('--completer', standalone_completions) -]) +@pytest.mark.parametrize('flag, completions', [('--provider', standalone_choices), ('--completer', standalone_completions)]) def test_complete_standalone(ac_app, flag, completions): text = '' line = 'standalone {} {}'.format(flag, text) diff --git a/tests/test_argparse_custom.py b/tests/test_argparse_custom.py index 2f2ee777..dcfb7d9f 100644 --- a/tests/test_argparse_custom.py +++ b/tests/test_argparse_custom.py @@ -48,11 +48,14 @@ def fake_func(): pass -@pytest.mark.parametrize('kwargs, is_valid', [ - ({'choices_provider': fake_func}, True), - ({'completer': fake_func}, True), - ({'choices_provider': fake_func, 'completer': fake_func}, False), -]) +@pytest.mark.parametrize( + 'kwargs, is_valid', + [ + ({'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() try: @@ -63,10 +66,7 @@ def test_apcustom_choices_callable_count(kwargs, is_valid): assert 'Only one of the following parameters' in str(ex) -@pytest.mark.parametrize('kwargs', [ - ({'choices_provider': fake_func}), - ({'completer': fake_func}) -]) +@pytest.mark.parametrize('kwargs', [({'choices_provider': fake_func}), ({'completer': fake_func})]) def test_apcustom_no_choices_callables_alongside_choices(kwargs): with pytest.raises(TypeError) as excinfo: parser = Cmd2ArgumentParser() @@ -74,10 +74,7 @@ def test_apcustom_no_choices_callables_alongside_choices(kwargs): assert 'None of the following parameters can be used alongside a choices parameter' in str(excinfo.value) -@pytest.mark.parametrize('kwargs', [ - ({'choices_provider': fake_func}), - ({'completer': fake_func}) -]) +@pytest.mark.parametrize('kwargs', [({'choices_provider': fake_func}), ({'completer': fake_func})]) def test_apcustom_no_choices_callables_when_nargs_is_0(kwargs): with pytest.raises(TypeError) as excinfo: parser = Cmd2ArgumentParser() diff --git a/tests/test_cmd2.py b/tests/test_cmd2.py index f94c0fb0..e719851e 100755 --- a/tests/test_cmd2.py +++ b/tests/test_cmd2.py @@ -1590,6 +1590,7 @@ def test_read_input_rawinput_true(capsys, monkeypatch): # Run custom history code import readline + readline.add_history('old_history') custom_history = ['cmd1', 'cmd2'] line = app.read_input(prompt_str, history=custom_history, completion_mode=cmd2.CompletionMode.NONE) @@ -1605,23 +1606,21 @@ def test_read_input_rawinput_true(capsys, monkeypatch): # custom choices custom_choices = ['choice1', 'choice2'] - line = app.read_input(prompt_str, completion_mode=cmd2.CompletionMode.CUSTOM, - choices=custom_choices) + line = app.read_input(prompt_str, completion_mode=cmd2.CompletionMode.CUSTOM, choices=custom_choices) assert line == input_str # custom choices_provider - line = app.read_input(prompt_str, completion_mode=cmd2.CompletionMode.CUSTOM, - choices_provider=cmd2.Cmd.get_all_commands) + line = app.read_input( + prompt_str, completion_mode=cmd2.CompletionMode.CUSTOM, choices_provider=cmd2.Cmd.get_all_commands + ) assert line == input_str # custom completer - line = app.read_input(prompt_str, completion_mode=cmd2.CompletionMode.CUSTOM, - completer=cmd2.Cmd.path_complete) + line = app.read_input(prompt_str, completion_mode=cmd2.CompletionMode.CUSTOM, completer=cmd2.Cmd.path_complete) assert line == input_str # custom parser - line = app.read_input(prompt_str, completion_mode=cmd2.CompletionMode.CUSTOM, - parser=cmd2.Cmd2ArgumentParser()) + line = app.read_input(prompt_str, completion_mode=cmd2.CompletionMode.CUSTOM, parser=cmd2.Cmd2ArgumentParser()) assert line == input_str # isatty is False @@ -1832,6 +1831,7 @@ def test_alias_create(base_app): out, err = run_cmd(base_app, 'alias list fake') assert out == normalize('alias create fake help') + def test_alias_create_with_quoted_tokens(base_app): """Demonstrate that quotes in alias value will be preserved""" create_command = 'alias create fake help ">" "out file.txt" ";"' @@ -1947,6 +1947,7 @@ def test_macro_create(base_app): out, err = run_cmd(base_app, 'macro list fake') assert out == normalize('macro create fake help') + def test_macro_create_with_quoted_tokens(base_app): """Demonstrate that quotes in macro value will be preserved""" create_command = 'macro create fake help ">" "out file.txt" ";"' diff --git a/tests/test_completion.py b/tests/test_completion.py index 1a8e5c4d..6a6e2eff 100755 --- a/tests/test_completion.py +++ b/tests/test_completion.py @@ -69,8 +69,9 @@ class CompletionsExample(cmd2.Cmd): def __init__(self): cmd2.Cmd.__init__(self, multiline_commands=['test_multiline']) self.foo = 'bar' - self.add_settable(utils.Settable('foo', str, description="a settable param", - completer=CompletionsExample.complete_foo_val)) + self.add_settable( + utils.Settable('foo', str, description="a settable param", completer=CompletionsExample.complete_foo_val) + ) def do_test_basic(self, args): pass @@ -913,6 +914,7 @@ def test_no_completer(cmd2_app): first_match = complete_tester(text, line, begidx, endidx, cmd2_app) assert first_match is not None and cmd2_app.completion_matches == expected + def test_wordbreak_in_command(cmd2_app): text = '' line = '"{}'.format(text) diff --git a/tests/test_history.py b/tests/test_history.py index 57bd0d7b..6f1336a8 100755 --- a/tests/test_history.py +++ b/tests/test_history.py @@ -34,7 +34,10 @@ except ImportError: # readline tests # def test_readline_remove_history_item(base_app): - from cmd2.rl_utils import readline + from cmd2.rl_utils import ( + readline, + ) + readline.clear_history() assert readline.get_current_history_length() == 0 readline.add_history('this is a test') |