diff --git a/cmd2/__init__.py b/cmd2/__init__.py index 3505a0ed1..5f786414b 100644 --- a/cmd2/__init__.py +++ b/cmd2/__init__.py @@ -65,56 +65,56 @@ ) __all__: list[str] = [ # noqa: RUF022 - 'COMMAND_NAME', - 'DEFAULT_SHORTCUTS', + "COMMAND_NAME", + "DEFAULT_SHORTCUTS", # Argparse Exports - 'Cmd2ArgumentParser', - 'register_argparse_argument_parameter', - 'set_default_ap_completer_type', - 'set_default_argument_parser_type', + "Cmd2ArgumentParser", + "register_argparse_argument_parameter", + "set_default_ap_completer_type", + "set_default_argument_parser_type", # Cmd2 - 'Cmd', - 'CommandResult', - 'CommandSet', - 'Statement', + "Cmd", + "CommandResult", + "CommandSet", + "Statement", # Colors - 'Color', + "Color", # Completion - 'Choices', - 'CompletionItem', - 'Completions', + "Choices", + "CompletionItem", + "Completions", # Decorators - 'with_argument_list', - 'with_argparser', - 'with_category', - 'as_subcommand_to', + "with_argument_list", + "with_argparser", + "with_category", + "as_subcommand_to", # Exceptions - 'Cmd2ArgparseError', - 'CommandSetRegistrationError', - 'CompletionError', - 'PassThroughException', - 'SkipPostcommandHooks', + "Cmd2ArgparseError", + "CommandSetRegistrationError", + "CompletionError", + "PassThroughException", + "SkipPostcommandHooks", # modules - 'plugin', - 'rich_utils', - 'string_utils', + "plugin", + "rich_utils", + "string_utils", # Rich Utils - 'ArgumentDefaultsCmd2HelpFormatter', - 'Cmd2HelpFormatter', - 'get_theme', - 'MetavarTypeCmd2HelpFormatter', - 'RawDescriptionCmd2HelpFormatter', - 'RawTextCmd2HelpFormatter', - 'RichPrintKwargs', - 'set_theme', - 'TextGroup', + "ArgumentDefaultsCmd2HelpFormatter", + "Cmd2HelpFormatter", + "get_theme", + "MetavarTypeCmd2HelpFormatter", + "RawDescriptionCmd2HelpFormatter", + "RawTextCmd2HelpFormatter", + "RichPrintKwargs", + "set_theme", + "TextGroup", # String Utils - 'stylize', + "stylize", # Styles, - 'Cmd2Style', + "Cmd2Style", # Utilities - 'categorize', - 'CustomCompletionSettings', - 'Settable', - 'set_default_str_sort_key', + "categorize", + "CustomCompletionSettings", + "Settable", + "set_default_str_sort_key", ] diff --git a/cmd2/argparse_completer.py b/cmd2/argparse_completer.py index 6ecb2b255..ec1dcdd02 100644 --- a/cmd2/argparse_completer.py +++ b/cmd2/argparse_completer.py @@ -46,7 +46,7 @@ # Name of the choice/completer function argument that, if present, will be passed a dictionary of # command line tokens up through the token being completed mapped to their argparse destination name. -ARG_TOKENS = 'arg_tokens' +ARG_TOKENS = "arg_tokens" def _build_hint(parser: Cmd2ArgumentParser, arg_action: argparse.Action) -> str: @@ -54,7 +54,7 @@ def _build_hint(parser: Cmd2ArgumentParser, arg_action: argparse.Action) -> str: # Check if hinting is disabled for this argument suppress_hint = arg_action.get_suppress_tab_hint() # type: ignore[attr-defined] if suppress_hint or arg_action.help == argparse.SUPPRESS: - return '' + return "" # Use the parser's help formatter to display just this action's help text formatter = parser._get_formatter() @@ -90,7 +90,7 @@ def _looks_like_flag(token: str, parser: Cmd2ArgumentParser) -> bool: return False # Flags can't have a space - return ' ' not in token + return " " not in token class _ArgumentState: @@ -133,8 +133,8 @@ def __init__(self, flag_arg_state: _ArgumentState) -> None: :param flag_arg_state: information about the unfinished flag action. """ - arg = f'{argparse._get_action_name(flag_arg_state.action)}' - err = f'{build_range_error(flag_arg_state.min, flag_arg_state.max)}' + arg = f"{argparse._get_action_name(flag_arg_state.action)}" + err = f"{build_range_error(flag_arg_state.min, flag_arg_state.max)}" error = f"Error: argument {arg}: {err} ({flag_arg_state.count} entered)" super().__init__(error) @@ -158,7 +158,7 @@ class ArgparseCompleter: def __init__( self, parser: Cmd2ArgumentParser, - cmd2_app: 'Cmd', + cmd2_app: "Cmd", *, parent_tokens: Mapping[str, MutableSequence[str]] | None = None, ) -> None: @@ -269,14 +269,14 @@ def consume_argument(arg_state: _ArgumentState, arg_token: str) -> None: # If we're in a flag REMAINDER arg, force all future tokens to go to that until a double dash is hit if flag_arg_state is not None and flag_arg_state.is_remainder: - if token == '--': # noqa: S105 + if token == "--": # noqa: S105 flag_arg_state = None else: consume_argument(flag_arg_state, token) continue # Handle '--' which tells argparse all remaining arguments are non-flags - if token == '--' and not skip_remaining_flags: # noqa: S105 + if token == "--" and not skip_remaining_flags: # noqa: S105 # Check if there is an unfinished flag if ( flag_arg_state is not None @@ -437,8 +437,8 @@ def _update_mutex_groups( if arg_action == completer_action: return - arg_str = f'{argparse._get_action_name(arg_action)}' - completer_str = f'{argparse._get_action_name(completer_action)}' + arg_str = f"{argparse._get_action_name(arg_action)}" + completer_str = f"{argparse._get_action_name(completer_action)}" error = f"Error: argument {arg_str}: not allowed with argument {completer_str}" raise CompletionError(error) @@ -566,18 +566,18 @@ def _complete_flags(self, text: str, line: str, begidx: int, endidx: int, used_f # For completion suggestions, group matched flags by action items: list[CompletionItem] = [] for action, option_strings in matched_actions.items(): - flag_text = ', '.join(option_strings) + flag_text = ", ".join(option_strings) # Mark optional flags with brackets if not action.required: - flag_text = '[' + flag_text + ']' + flag_text = "[" + flag_text + "]" # Use the first option string as the completion result for this action items.append( CompletionItem( option_strings[0], display=flag_text, - display_meta=action.help or '', + display_meta=action.help or "", ) ) @@ -720,10 +720,10 @@ def _choices_to_items(self, arg_state: _ArgumentState) -> list[CompletionItem]: for action in arg_state.action._choices_actions: if action.dest in arg_state.action.choices: subparser = arg_state.action.choices[action.dest] - parser_help[subparser] = action.help or '' + parser_help[subparser] = action.help or "" return [ - CompletionItem(name, display_meta=parser_help.get(subparser, '')) + CompletionItem(name, display_meta=parser_help.get(subparser, "")) for name, subparser in arg_state.action.choices.items() ] diff --git a/cmd2/argparse_utils.py b/cmd2/argparse_utils.py index a6a029b92..a4219e35a 100644 --- a/cmd2/argparse_utils.py +++ b/cmd2/argparse_utils.py @@ -262,10 +262,10 @@ def build_range_error(range_min: int, range_max: float) -> str: err_msg = "expected " if range_max == constants.INFINITY: - plural = '' if range_min == 1 else 's' + plural = "" if range_min == 1 else "s" err_msg += f"at least {range_min}" else: - plural = '' if range_max == 1 else 's' + plural = "" if range_max == 1 else "s" if range_min == range_max: err_msg += f"{range_min}" else: @@ -309,8 +309,8 @@ def register_argparse_argument_parameter( raise KeyError(f"'{param_name}' conflicts with an existing attribute on argparse.Action") # Check if accessors already exist (e.g., from manual patching or previous registration) - getter_name = f'get_{param_name}' - setter_name = f'set_{param_name}' + getter_name = f"get_{param_name}" + setter_name = f"set_{param_name}" if hasattr(argparse.Action, getter_name) or hasattr(argparse.Action, setter_name): raise KeyError(f"Accessor methods for '{param_name}' already exist on argparse.Action") @@ -355,11 +355,11 @@ def _validate_completion_callable(self: argparse.Action, value: Any) -> Any: # Add new attributes to argparse.Action. # See _ActionsContainer_add_argument() for details on these attributes. -register_argparse_argument_parameter('choices_provider', validator=_validate_completion_callable) -register_argparse_argument_parameter('completer', validator=_validate_completion_callable) -register_argparse_argument_parameter('table_columns') -register_argparse_argument_parameter('nargs_range') -register_argparse_argument_parameter('suppress_tab_hint') +register_argparse_argument_parameter("choices_provider", validator=_validate_completion_callable) +register_argparse_argument_parameter("completer", validator=_validate_completion_callable) +register_argparse_argument_parameter("table_columns") +register_argparse_argument_parameter("nargs_range") +register_argparse_argument_parameter("suppress_tab_hint") ############################################################################################################ @@ -431,11 +431,11 @@ def _ActionsContainer_add_argument( # noqa: N802 or not isinstance(nargs[0], int) or not (isinstance(nargs[1], int) or nargs[1] == constants.INFINITY) ): - raise ValueError('Ranged values for nargs must be a tuple of 1 or 2 integers') + raise ValueError("Ranged values for nargs must be a tuple of 1 or 2 integers") if nargs[0] >= nargs[1]: - raise ValueError('Invalid nargs range. The first value must be less than the second') + raise ValueError("Invalid nargs range. The first value must be less than the second") if nargs[0] < 0: - raise ValueError('Negative numbers are invalid for nargs range') + raise ValueError("Negative numbers are invalid for nargs range") # Save the nargs tuple as our range setting nargs_range = nargs @@ -465,7 +465,7 @@ def _ActionsContainer_add_argument( # noqa: N802 nargs_adjusted = nargs # Add the argparse-recognized version of nargs to kwargs - kwargs['nargs'] = nargs_adjusted + kwargs["nargs"] = nargs_adjusted # Extract registered custom keyword arguments custom_attribs = {keyword: value for keyword, value in kwargs.items() if keyword in _CUSTOM_ACTION_ATTRIBS} @@ -484,7 +484,7 @@ def _ActionsContainer_add_argument( # noqa: N802 # Set other registered custom attributes for keyword, value in custom_attribs.items(): - attr_setter = getattr(new_arg, f'set_{keyword}', None) + attr_setter = getattr(new_arg, f"set_{keyword}", None) if attr_setter is not None: attr_setter(value) @@ -506,17 +506,17 @@ def __init__( epilog: RenderableType | None = None, parents: Sequence[argparse.ArgumentParser] = (), formatter_class: type[Cmd2HelpFormatter] = Cmd2HelpFormatter, - prefix_chars: str = '-', + prefix_chars: str = "-", fromfile_prefix_chars: str | None = None, argument_default: str | None = None, - conflict_handler: str = 'error', + conflict_handler: str = "error", add_help: bool = True, allow_abbrev: bool = True, exit_on_error: bool = True, suggest_on_error: bool = False, color: bool = False, *, - ap_completer_type: type['ArgparseCompleter'] | None = None, + ap_completer_type: type["ArgparseCompleter"] | None = None, ) -> None: """Initialize the Cmd2ArgumentParser instance. @@ -651,7 +651,7 @@ def update_prog(self, prog: str) -> None: subcmd_parser.update_prog(subcmd_prog) updated_parsers.add(subcmd_parser) - def _find_parser(self, subcommand_path: Iterable[str]) -> 'Cmd2ArgumentParser': + def _find_parser(self, subcommand_path: Iterable[str]) -> "Cmd2ArgumentParser": """Find a parser in the hierarchy based on a sequence of subcommand names. :param subcommand_path: sequence of subcommand names leading to the target parser @@ -670,7 +670,7 @@ def attach_subcommand( self, subcommand_path: Iterable[str], subcommand: str, - subcommand_parser: 'Cmd2ArgumentParser', + subcommand_parser: "Cmd2ArgumentParser", **add_parser_kwargs: Any, ) -> None: """Attach a parser as a subcommand to a command at the specified path. @@ -719,7 +719,7 @@ def attach_subcommand( for alias in add_parser_kwargs.get("aliases", ()): subparsers_action._name_parser_map[alias] = subcommand_parser - def detach_subcommand(self, subcommand_path: Iterable[str], subcommand: str) -> 'Cmd2ArgumentParser': + def detach_subcommand(self, subcommand_path: Iterable[str], subcommand: str) -> "Cmd2ArgumentParser": """Detach a subcommand from a command at the specified path. :param subcommand_path: sequence of subcommand names leading to the parser hosting the @@ -753,13 +753,13 @@ def detach_subcommand(self, subcommand_path: Iterable[str], subcommand: str) -> def error(self, message: str) -> NoReturn: """Override that applies custom formatting to the error message.""" - lines = message.split('\n') - formatted_message = '' + lines = message.split("\n") + formatted_message = "" for linum, line in enumerate(lines): if linum == 0: - formatted_message = 'Error: ' + line + formatted_message = "Error: " + line else: - formatted_message += '\n ' + line + formatted_message += "\n " + line self.print_usage(sys.stderr) @@ -769,7 +769,7 @@ def error(self, message: str) -> NoReturn: console.print(formatted_message, style=Cmd2Style.ERROR) formatted_message = f"{capture.get()}" - self.exit(2, f'{formatted_message}\n') + self.exit(2, f"{formatted_message}\n") def _get_formatter(self, **kwargs: Any) -> Cmd2HelpFormatter: """Override with customizations for Cmd2HelpFormatter.""" @@ -777,19 +777,19 @@ def _get_formatter(self, **kwargs: Any) -> Cmd2HelpFormatter: def format_help(self) -> str: """Override to add a newline.""" - return super().format_help() + '\n' + return super().format_help() + "\n" def _get_nargs_pattern(self, action: argparse.Action) -> str: """Override to support nargs ranges.""" nargs_range = action.get_nargs_range() # type: ignore[attr-defined] if nargs_range: - range_max = '' if nargs_range[1] == constants.INFINITY else nargs_range[1] - nargs_pattern = f'(-*A{{{nargs_range[0]},{range_max}}}-*)' + range_max = "" if nargs_range[1] == constants.INFINITY else nargs_range[1] + nargs_pattern = f"(-*A{{{nargs_range[0]},{range_max}}}-*)" # if this is an optional action, -- is not allowed if action.option_strings: - nargs_pattern = nargs_pattern.replace('-*', '') - nargs_pattern = nargs_pattern.replace('-', '') + nargs_pattern = nargs_pattern.replace("-*", "") + nargs_pattern = nargs_pattern.replace("-", "") return nargs_pattern return super()._get_nargs_pattern(action) @@ -823,8 +823,8 @@ def _check_value(self, action: argparse.Action, value: Any) -> None: if action.choices is not None and value not in action.choices: # If any choice is a CompletionItem, then display its value property. choices = [c.value if isinstance(c, CompletionItem) else c for c in action.choices] - args = {'value': value, 'choices': ', '.join(map(repr, choices))} - msg = _('invalid choice: %(value)r (choose from %(choices)s)') + args = {"value": value, "choices": ", ".join(map(repr, choices))} + msg = _("invalid choice: %(value)r (choose from %(choices)s)") raise ArgumentError(action, msg % args) diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py index 90d4e4d0e..0a5880e3a 100644 --- a/cmd2/cmd2.py +++ b/cmd2/cmd2.py @@ -183,7 +183,7 @@ class NoConsoleScreenBufferError(Exception): # type: ignore[no-redef] """Dummy exception to use when prompt_toolkit.output.win32.NoConsoleScreenBufferError is not available.""" - def __init__(self, msg: str = '') -> None: + def __init__(self, msg: str = "") -> None: """Initialize NoConsoleScreenBufferError custom exception instance.""" super().__init__(msg) @@ -220,7 +220,7 @@ def __init__(self) -> None: # Contains data about a disabled command which is used to restore its original functions when the command is enabled -DisabledCommand = namedtuple('DisabledCommand', ['command_function', 'help_function', 'completer_function']) # noqa: PYI024 +DisabledCommand = namedtuple("DisabledCommand", ["command_function", "help_function", "completer_function"]) # noqa: PYI024 class _CommandParsers: @@ -229,7 +229,7 @@ class _CommandParsers: Parser creation and retrieval are accomplished through the get() method. """ - def __init__(self, cmd: 'Cmd') -> None: + def __init__(self, cmd: "Cmd") -> None: self._cmd = cmd # Keyed by the fully qualified method names. This is more reliable than @@ -357,13 +357,13 @@ def __init__( command_sets: Iterable[CommandSet[Any]] | None = None, include_ipy: bool = False, include_py: bool = False, - intro: RenderableType = '', + intro: RenderableType = "", multiline_commands: Iterable[str] | None = None, - persistent_history_file: str = '', + persistent_history_file: str = "", persistent_history_length: int = 1000, shortcuts: Mapping[str, str] | None = None, silence_startup_script: bool = False, - startup_script: str = '', + startup_script: str = "", suggest_similar_command: bool = False, terminators: Iterable[str] | None = None, ) -> None: @@ -417,9 +417,9 @@ def __init__( """ # Check if py or ipy need to be disabled in this instance if not include_py: - setattr(self, 'do_py', None) # noqa: B010 + setattr(self, "do_py", None) # noqa: B010 if not include_ipy: - setattr(self, 'do_ipy', None) # noqa: B010 + setattr(self, "do_ipy", None) # noqa: B010 # initialize plugin system # needs to be done before we most of the other stuff below @@ -487,16 +487,16 @@ def __init__( self.build_settables() # Use as prompt for multiline commands on the 2nd+ line of input - self.continuation_prompt: str = '> ' + self.continuation_prompt: str = "> " # Allow access to your application in embedded Python shells and pyscripts via self self.self_in_py = False # Commands to exclude from the help menu and completion - self.hidden_commands = ['_eof', '_relative_run_script'] + self.hidden_commands = ["_eof", "_relative_run_script"] # Initialize history from a persistent history file (if present) - self.persistent_history_file = '' + self.persistent_history_file = "" self._persistent_history_length = persistent_history_length self._initialize_history(persistent_history_file) @@ -510,7 +510,7 @@ def __init__( self.active_session = self.main_session # Commands to exclude from the history command - self.exclude_from_history = ['_eof', 'history'] + self.exclude_from_history = ["_eof", "history"] # Dictionary of macro names and their values self.macros: dict[str, Macro] = {} @@ -519,7 +519,7 @@ def __init__( self._py_history: list[str] = [] # The name by which Python environments refer to the PyBridge to call app commands - self.py_bridge_name = 'app' + self.py_bridge_name = "app" # Defines app-specific variables/functions available in Python shells and pyscripts self.py_locals: dict[str, Any] = {} @@ -557,7 +557,7 @@ def __init__( self.default_error = "{} is not a recognized command, alias, or macro." # If non-empty, this string will be displayed if a broken pipe error occurs - self.broken_pipe_warning = '' + self.broken_pipe_warning = "" # Commands that will run at the beginning of the command loop self._startup_commands: list[str] = [] @@ -565,7 +565,7 @@ def __init__( # Store initial termios settings to restore after each command. # This is a faster way of accomplishing what "stty sane" does. self._initial_termios_settings = None - if not sys.platform.startswith('win') and self.stdin.isatty(): + if not sys.platform.startswith("win") and self.stdin.isatty(): try: import io import termios @@ -594,16 +594,16 @@ def __init__( self._startup_commands.extend(callargs) # Set the pager(s) for use when displaying output using a pager - if sys.platform.startswith('win'): - self.pager = self.pager_chop = 'more' + if sys.platform.startswith("win"): + self.pager = self.pager_chop = "more" else: # Here is the meaning of the various flags we are using with the less command: # -S causes lines longer than the screen width to be chopped (truncated) rather than wrapped # -R causes ANSI "style" escape sequences to be output in raw form (i.e. colors are displayed) # -X disables sending the termcap initialization and deinitialization strings to the terminal # -F causes less to automatically exit if the entire file can be displayed on the first screen - self.pager = 'less -RXF' - self.pager_chop = 'less -SRXF' + self.pager = "less -RXF" + self.pager_chop = "less -SRXF" # This boolean flag stores whether cmd2 will allow clipboard related features self.allow_clipboard = allow_clipboard @@ -703,7 +703,7 @@ def _create_main_session(self, auto_suggest: bool, completekey: str) -> PromptSe # Add a binding for 'enter' that triggers only when a completion is selected. # This allows accepting a completion without submitting the command. - @key_bindings.add('enter', filter=filters.completion_is_selected) + @key_bindings.add("enter", filter=filters.completion_is_selected) def _(event: Any) -> None: # pragma: no cover event.current_buffer.complete_state = None @@ -799,7 +799,7 @@ def load_commandset_by_type(commandset_types: list[type[CommandSet[Any]]]) -> No if not ( cmdset_type in existing_commandset_types or len(init_sig.parameters) != 1 - or 'self' not in init_sig.parameters + or "self" not in init_sig.parameters ): cmdset = cmdset_type() self.register_command_set(cmdset) @@ -813,21 +813,21 @@ def register_command_set(self, cmdset: CommandSet[Any]) -> None: """ existing_commandset_types = [type(command_set) for command_set in self._installed_command_sets] if type(cmdset) in existing_commandset_types: - raise CommandSetRegistrationError('CommandSet ' + type(cmdset).__name__ + ' is already installed') + raise CommandSetRegistrationError("CommandSet " + type(cmdset).__name__ + " is already installed") all_settables = self.settables if self.always_prefix_settables: if not cmdset.settable_prefix.strip(): - raise CommandSetRegistrationError('CommandSet settable prefix must not be empty') + raise CommandSetRegistrationError("CommandSet settable prefix must not be empty") for key in cmdset.settables: - prefixed_name = f'{cmdset.settable_prefix}.{key}' + prefixed_name = f"{cmdset.settable_prefix}.{key}" if prefixed_name in all_settables: - raise CommandSetRegistrationError(f'Duplicate settable: {key}') + raise CommandSetRegistrationError(f"Duplicate settable: {key}") else: for key in cmdset.settables: if key in all_settables: - raise CommandSetRegistrationError(f'Duplicate settable {key} is already registered') + raise CommandSetRegistrationError(f"Duplicate settable {key} is already registered") cmdset.on_register(self) methods = cast( @@ -836,7 +836,7 @@ def register_command_set(self, cmdset: CommandSet[Any]) -> None: cmdset, predicate=lambda meth: ( # type: ignore[arg-type] isinstance(meth, Callable) # type: ignore[arg-type] - and hasattr(meth, '__name__') + and hasattr(meth, "__name__") and meth.__name__.startswith(COMMAND_FUNC_PREFIX) ), ), @@ -921,7 +921,7 @@ def _build_parser( return parser - def _install_command_function(self, command_func_name: str, command_method: BoundCommandFunc, context: str = '') -> None: + def _install_command_function(self, command_func_name: str, command_method: BoundCommandFunc, context: str = "") -> None: """Install a new command function into the CLI. :param command_func_name: name of command function to add @@ -943,7 +943,7 @@ def _install_command_function(self, command_func_name: str, command_method: Boun # Make sure command function doesn't share name with existing attribute if hasattr(self, command_func_name): - raise CommandSetRegistrationError(f'Attribute already exists: {command_func_name} ({context})') + raise CommandSetRegistrationError(f"Attribute already exists: {command_func_name} ({context})") # Check if command has an invalid name valid, errmsg = self.statement_parser.is_valid_command(command) @@ -966,14 +966,14 @@ def _install_completer_function(self, cmd_name: str, cmd_completer: BoundComplet completer_func_name = COMPLETER_FUNC_PREFIX + cmd_name if hasattr(self, completer_func_name): - raise CommandSetRegistrationError(f'Attribute already exists: {completer_func_name}') + raise CommandSetRegistrationError(f"Attribute already exists: {completer_func_name}") setattr(self, completer_func_name, cmd_completer) def _install_help_function(self, cmd_name: str, cmd_help: Callable[..., None]) -> None: help_func_name = HELP_FUNC_PREFIX + cmd_name if hasattr(self, help_func_name): - raise CommandSetRegistrationError(f'Attribute already exists: {help_func_name}') + raise CommandSetRegistrationError(f"Attribute already exists: {help_func_name}") setattr(self, help_func_name, cmd_help) def unregister_command_set(self, cmdset: CommandSet[Any]) -> None: @@ -990,7 +990,7 @@ def unregister_command_set(self, cmdset: CommandSet[Any]) -> None: cmdset, predicate=lambda meth: ( # type: ignore[arg-type] isinstance(meth, Callable) # type: ignore[arg-type] - and hasattr(meth, '__name__') + and hasattr(meth, "__name__") and meth.__name__.startswith(COMMAND_FUNC_PREFIX) ), ) @@ -1050,7 +1050,7 @@ def check_parser_uninstallable(parser: Cmd2ArgumentParser) -> None: cmdset, predicate=lambda meth: ( # type: ignore[arg-type] isinstance(meth, Callable) # type: ignore[arg-type] - and hasattr(meth, '__name__') + and hasattr(meth, "__name__") and meth.__name__.startswith(COMMAND_FUNC_PREFIX) ), ) @@ -1069,7 +1069,7 @@ def _register_subcommands(self, cmdset: CmdOrSet) -> None: :param cmdset: CommandSet or cmd2.Cmd subclass containing subcommands """ if not (cmdset is self or cmdset in self._installed_command_sets): - raise CommandSetRegistrationError('Cannot register subcommands with an unregistered CommandSet') + raise CommandSetRegistrationError("Cannot register subcommands with an unregistered CommandSet") # find methods that have the required attributes necessary to be recognized as a sub-command methods = inspect.getmembers( @@ -1090,7 +1090,7 @@ def _register_subcommands(self, cmdset: CmdOrSet) -> None: subcommand_valid, errmsg = self.statement_parser.is_valid_command(subcommand_name, is_subcommand=True) if not subcommand_valid: - raise CommandSetRegistrationError(f'Subcommand {subcommand_name} is not valid: {errmsg}') + raise CommandSetRegistrationError(f"Subcommand {subcommand_name} is not valid: {errmsg}") # Create the subcommand parser and configure it subcmd_parser = self._build_parser(cmdset, subcmd_parser_builder) @@ -1119,7 +1119,7 @@ def _unregister_subcommands(self, cmdset: CmdOrSet) -> None: :param cmdset: CommandSet containing subcommands """ if not (cmdset is self or cmdset in self._installed_command_sets): - raise CommandSetRegistrationError('Cannot unregister subcommands with an unregistered CommandSet') + raise CommandSetRegistrationError("Cannot unregister subcommands with an unregistered CommandSet") # find methods that have the required attributes necessary to be recognized as a sub-command methods = inspect.getmembers( @@ -1229,8 +1229,8 @@ def always_prefix_settables(self, new_value: bool) -> None: for cmd_set in self._installed_command_sets: if not cmd_set.settable_prefix: raise ValueError( - f'Cannot force settable prefixes. CommandSet {cmd_set.__class__.__name__} does ' - f'not have a settable prefix defined.' + f"Cannot force settable prefixes. CommandSet {cmd_set.__class__.__name__} does " + f"not have a settable prefix defined." ) self._always_prefix_settables = new_value @@ -1245,7 +1245,7 @@ def settables(self) -> Mapping[str, Settable]: cmdset_settables = cmd_set.settables for settable_name, settable in cmdset_settables.items(): if self.always_prefix_settables: - all_settables[f'{cmd_set.settable_prefix}.{settable_name}'] = settable + all_settables[f"{cmd_set.settable_prefix}.{settable_name}"] = settable else: all_settables[settable_name] = settable return all_settables @@ -1256,7 +1256,7 @@ def add_settable(self, settable: Settable) -> None: :param settable: Settable object being added """ if not self.always_prefix_settables and settable.name in self.settables and settable.name not in self._settables: - raise KeyError(f'Duplicate settable: {settable.name}') + raise KeyError(f"Duplicate settable: {settable.name}") self._settables[settable.name] = settable def remove_settable(self, name: str) -> None: @@ -1288,7 +1288,7 @@ def allow_style_type(value: str) -> ru.AllowStyle: ) from ex settable_description = Text.assemble( - 'Allow styled text in output (Options: ', + "Allow styled text in output (Options: ", (str(ru.AllowStyle.ALWAYS), Style(bold=True)), ", ", (str(ru.AllowStyle.NEVER), Style(bold=True)), @@ -1298,7 +1298,7 @@ def allow_style_type(value: str) -> ru.AllowStyle: ) self.add_settable( Settable( - 'allow_style', + "allow_style", allow_style_type, ru.rich_text_to_string(settable_description), self, @@ -1307,15 +1307,15 @@ def allow_style_type(value: str) -> ru.AllowStyle: ) self.add_settable( - Settable('always_show_hint', bool, 'Display completion hint even when completion suggestions print', self) + Settable("always_show_hint", bool, "Display completion hint even when completion suggestions print", self) ) - self.add_settable(Settable('debug', bool, "Show full traceback on exception", self)) - self.add_settable(Settable('echo', bool, "Echo command issued into output", self)) - self.add_settable(Settable('editor', str, "Program used by 'edit'", self)) - self.add_settable(Settable('feedback_to_output', bool, "Include nonessentials in '|' and '>' results", self)) + self.add_settable(Settable("debug", bool, "Show full traceback on exception", self)) + self.add_settable(Settable("echo", bool, "Echo command issued into output", self)) + self.add_settable(Settable("editor", str, "Program used by 'edit'", self)) + self.add_settable(Settable("feedback_to_output", bool, "Include nonessentials in '|' and '>' results", self)) self.add_settable( Settable( - 'max_completion_table_items', + "max_completion_table_items", int, "Max results allowed to display a table", self, @@ -1323,15 +1323,15 @@ def allow_style_type(value: str) -> ru.AllowStyle: ) self.add_settable( Settable( - 'max_column_completion_results', + "max_column_completion_results", int, "Max results to display in a single column", self, ) ) - self.add_settable(Settable('quiet', bool, "Don't print nonessential feedback", self)) - self.add_settable(Settable('scripts_add_to_history', bool, 'Scripts and pyscripts add commands to history', self)) - self.add_settable(Settable('timing', bool, "Report execution times", self)) + self.add_settable(Settable("quiet", bool, "Don't print nonessential feedback", self)) + self.add_settable(Settable("scripts_add_to_history", bool, "Scripts and pyscripts add commands to history", self)) + self.add_settable(Settable("timing", bool, "Report execution times", self)) # ----- Methods related to presenting output to the user ----- @@ -1644,7 +1644,7 @@ def format_exception(self, exception: BaseException) -> str: # If not in debug mode and the 'debug' setting is available, # inform the user how to enable full tracebacks. - if not self.debug and 'debug' in self.settables: + if not self.debug and "debug" in self.settables: help_msg = Text.assemble( "\n\n", ("To enable full traceback, run the following command: ", Cmd2Style.WARNING), @@ -1761,7 +1761,7 @@ def ppaged( functional_terminal = ( self.stdin.isatty() and self.stdout.isatty() - and (sys.platform.startswith('win') or os.environ.get('TERM') is not None) + and (sys.platform.startswith("win") or os.environ.get("TERM") is not None) ) # A pager application blocks, so only run one if not redirecting or running a script (either text or Python). @@ -1790,7 +1790,7 @@ def ppaged( soft_wrap=soft_wrap, **(rich_print_kwargs if rich_print_kwargs is not None else {}), ) - output_bytes = capture.get().encode('utf-8', 'replace') + output_bytes = capture.get().encode("utf-8", "replace") # Prevent KeyboardInterrupts while in the pager. The pager application will # still receive the SIGINT since it is in the same process group as us. @@ -1813,7 +1813,7 @@ def ppaged( import termios # Ensure we are in the foreground process group - if hasattr(os, 'tcsetpgrp') and hasattr(os, 'getpgrp'): + if hasattr(os, "tcsetpgrp") and hasattr(os, "getpgrp"): # Ignore SIGTTOU to avoid getting stopped when calling tcsetpgrp from background old_handler = signal.signal(signal.SIGTTOU, signal.SIG_IGN) try: @@ -1908,7 +1908,7 @@ def get_bottom_toolbar(self) -> list[str | tuple[str, str]] | None: # Get the current time in ISO format with 0.01s precision dt = datetime.datetime.now(datetime.timezone.utc).astimezone() - now = dt.strftime('%Y-%m-%dT%H:%M:%S.%f')[:-4] + dt.strftime('%z') + now = dt.strftime("%Y-%m-%dT%H:%M:%S.%f")[:-4] + dt.strftime("%z") left_text = sys.argv[0] # Get terminal width to calculate padding for right-alignment @@ -1916,13 +1916,13 @@ def get_bottom_toolbar(self) -> list[str | tuple[str, str]] | None: padding_size = cols - len(left_text) - len(now) - 1 if padding_size < 1: padding_size = 1 - padding = ' ' * padding_size + padding = " " * padding_size # Return formatted text for prompt-toolkit return [ - ('ansigreen', left_text), - ('', padding), - ('ansicyan', now), + ("ansigreen", left_text), + ("", padding), + ("ansicyan", now), ] return None @@ -1955,7 +1955,7 @@ def tokens_for_completion(self, line: str, begidx: int, endidx: int) -> tuple[li """ import copy - unclosed_quote = '' + unclosed_quote = "" quotes_to_try = copy.copy(constants.QUOTES) tmp_line = line[:endidx] @@ -1969,7 +1969,7 @@ def tokens_for_completion(self, line: str, begidx: int, endidx: int) -> tuple[li # If the cursor is at an empty token outside of a quoted string, # then that is the token being completed. Add it to the list. if not unclosed_quote and begidx == tmp_endidx: - initial_tokens.append('') + initial_tokens.append("") break except ValueError as ex: # Make sure the exception was due to an unclosed quote and @@ -2128,7 +2128,7 @@ def _complete_users(text: str, add_trailing_sep_if_dir: bool) -> Completions: # Windows lacks the pwd module so we can't get a list of users. # Instead we will return a result once the user enters text that # resolves to an existing home directory. - if sys.platform.startswith('win'): + if sys.platform.startswith("win"): expanded_path = os.path.expanduser(text) if os.path.isdir(expanded_path): user = text @@ -2143,7 +2143,7 @@ def _complete_users(text: str, add_trailing_sep_if_dir: bool) -> Completions: # Check if the user has an existing home dir if os.path.isdir(cur_pw.pw_dir): # Add a ~ to the user to match against text - cur_user = '~' + cur_pw.pw_name + cur_user = "~" + cur_pw.pw_name if cur_user.startswith(text): if add_trailing_sep_if_dir: cur_user += os.path.sep @@ -2183,25 +2183,25 @@ def path_complete( cwd_added = False # Used to replace expanded user path in final result - orig_tilde_path = '' - expanded_tilde_path = '' + orig_tilde_path = "" + expanded_tilde_path = "" # If the search text is blank, then search in the CWD for * if not text: - search_str = os.path.join(os.getcwd(), '*') + search_str = os.path.join(os.getcwd(), "*") cwd_added = True else: # Purposely don't match any path containing wildcards - wildcards = ['*', '?'] + wildcards = ["*", "?"] for wildcard in wildcards: if wildcard in text: return Completions() # Start the search string - search_str = text + '*' + search_str = text + "*" # Handle tilde expansion and completion - if text.startswith('~'): + if text.startswith("~"): sep_index = text.find(os.path.sep, 1) # If there is no slash, then the user is still completing the user after the tilde @@ -2247,7 +2247,7 @@ def path_complete( # Remove cwd if it was added to match the text prompt-toolkit expects if cwd_added: to_replace = cwd if cwd == os.path.sep else cwd + os.path.sep - matches = [cur_path.replace(to_replace, '', 1) for cur_path in matches] + matches = [cur_path.replace(to_replace, "", 1) for cur_path in matches] # Restore the tilde string if we expanded one to match the text prompt-toolkit expects if expanded_tilde_path: @@ -2281,7 +2281,7 @@ def shell_cmd_complete( return Completions() # If there are no path characters in the search text, then do shell command completion in the user's path - if not text.startswith('~') and os.path.sep not in text: + if not text.startswith("~") and os.path.sep not in text: items = [CompletionItem(exe) for exe in utils.get_exes_in_path(text)] return Completions(items=items) @@ -2397,7 +2397,7 @@ def _perform_completion( """ # If custom_settings is None, then we are completing a command's argument. # Parse the command line to get the command token. - command = '' + command = "" if custom_settings is None: partial_statement = self.statement_parser.parse_command_only(line) command = partial_statement.command @@ -2415,7 +2415,7 @@ def _perform_completion( # of what type of whitespace (' ', \n) was stripped, just append spaces # since shlex treats whitespace characters the same when splitting. rstripped_len = len(line) - len(line.rstrip()) - expanded_line += ' ' * rstripped_len + expanded_line += " " * rstripped_len # Fix the index values if expanded_line has a different size than line if len(expanded_line) != len(line): @@ -2480,13 +2480,13 @@ def _perform_completion( ) # Text we need to remove from completions later - text_to_remove = '' + text_to_remove = "" # Get the token being completed with any opening quote preserved raw_completion_token = raw_tokens[-1] # Used for adding quotes to the completion token - completion_token_quote = '' + completion_token_quote = "" # Check if the token being completed has an opening quote if raw_completion_token and raw_completion_token[0] in constants.QUOTES: @@ -2522,7 +2522,7 @@ def _perform_completion( if not completion_token_quote: matches = completions.to_strings() - if any(' ' in match for match in matches): + if any(" " in match for match in matches): _add_opening_quote = True # Determine best quote (single vs double) based on text content @@ -2533,7 +2533,7 @@ def _perform_completion( new_items = [ dataclasses.replace( item, - text=item.text.replace(text_to_remove, '', 1), + text=item.text.replace(text_to_remove, "", 1), ) for item in completions ] @@ -2572,7 +2572,7 @@ def complete( # Shortcuts are not word break characters when completing. Therefore, shortcuts become part # of the text variable if there isn't a word break, like a space, after it. We need to remove it # from text and update the indexes. This only applies if we are at the beginning of the command line. - shortcut_to_restore = '' + shortcut_to_restore = "" if begidx == 0 and custom_settings is None: for shortcut, _ in self.statement_parser.shortcuts: if text.startswith(shortcut): @@ -2587,7 +2587,7 @@ def complete( # No shortcut was found. Complete the command token. parser = argparse_utils.DEFAULT_ARGUMENT_PARSER(add_help=False) parser.add_argument( - 'command', + "command", metavar="COMMAND", help="command, alias, or macro name", choices=self._get_commands_aliases_and_macros_choices(), @@ -2719,7 +2719,7 @@ def _get_commands_aliases_and_macros_choices(self) -> Choices: for command in self.get_visible_commands(): # Get the command method func = getattr(self, constants.COMMAND_FUNC_PREFIX + command) - description = strip_doc_annotations(func.__doc__).splitlines()[0] if func.__doc__ else '' + description = strip_doc_annotations(func.__doc__).splitlines()[0] if func.__doc__ else "" items.append(CompletionItem(command, display_meta=description)) # Add aliases @@ -2927,7 +2927,7 @@ def onecmd_plus_hooks( stop = self.postcmd(stop, statement) if self.timing: - self.pfeedback(f'Elapsed: {datetime.datetime.now(tz=datetime.timezone.utc) - timestart}') + self.pfeedback(f"Elapsed: {datetime.datetime.now(tz=datetime.timezone.utc) - timestart}") finally: # Get sigint protection while we restore stuff with self.sigint_protection: @@ -3014,7 +3014,7 @@ def runcmds_plus_hooks( line = line.raw # noqa: PLW2901 if self.echo: - self.poutput(f'{self.prompt}{line}') + self.poutput(f"{self.prompt}{line}") try: if self.onecmd_plus_hooks( @@ -3076,13 +3076,13 @@ def _complete_statement(self, line: str) -> Statement: nextline = self._read_command_line(self.continuation_prompt) except EOFError: # Add a blank line, which serves as a command terminator. - nextline = '\n' + nextline = "\n" self.poutput(nextline) - line += f'\n{nextline}' + line += f"\n{nextline}" except KeyboardInterrupt: - self.poutput('^C') + self.poutput("^C") raise EmptyStatement from None def _input_line_to_statement(self, line: str) -> Statement: @@ -3140,7 +3140,7 @@ def _resolve_macro(self, statement: Statement) -> str: # Make sure enough arguments were passed in if len(statement.arg_list) < macro.minimum_arg_count: - plural = '' if macro.minimum_arg_count == 1 else 's' + plural = "" if macro.minimum_arg_count == 1 else "s" raise MacroError(f"The macro '{statement.command}' expects at least {macro.minimum_arg_count} argument{plural}") # Resolve the arguments in reverse and read their values from statement.argv since those @@ -3150,10 +3150,10 @@ def _resolve_macro(self, statement: Statement) -> str: for macro_arg in reverse_arg_list: if macro_arg.is_escaped: - to_replace = '{{' + macro_arg.number_str + '}}' - replacement = '{' + macro_arg.number_str + '}' + to_replace = "{{" + macro_arg.number_str + "}}" + replacement = "{" + macro_arg.number_str + "}" else: - to_replace = '{' + macro_arg.number_str + '}' + to_replace = "{" + macro_arg.number_str + "}" replacement = statement.argv[int(macro_arg.number_str)] parts = resolved.rsplit(to_replace, maxsplit=1) @@ -3161,7 +3161,7 @@ def _resolve_macro(self, statement: Statement) -> str: # Append extra arguments and use statement.arg_list since these arguments need their quotes preserved for stmt_arg in statement.arg_list[macro.minimum_arg_count :]: - resolved += ' ' + stmt_arg + resolved += " " + stmt_arg # Restore any terminator, suffix, redirection, etc. return resolved + statement.post_command @@ -3196,21 +3196,21 @@ def _redirect_output(self, statement: Statement) -> utils.RedirectionSavedState: # Open each side of the pipe subproc_stdin = open(read_fd) # noqa: SIM115 - new_stdout: TextIO = cast(TextIO, open(write_fd, 'w')) # noqa: SIM115 + new_stdout: TextIO = cast(TextIO, open(write_fd, "w")) # noqa: SIM115 # Create pipe process in a separate group to isolate our signals from it. If a Ctrl-C event occurs, # our sigint handler will forward it only to the most recent pipe process. This makes sure pipe # processes close in the right order (most recent first). kwargs: dict[str, Any] = {} - if sys.platform == 'win32': - kwargs['creationflags'] = subprocess.CREATE_NEW_PROCESS_GROUP + if sys.platform == "win32": + kwargs["creationflags"] = subprocess.CREATE_NEW_PROCESS_GROUP else: - kwargs['start_new_session'] = True + kwargs["start_new_session"] = True # Attempt to run the pipe process in the user's preferred shell instead of the default behavior of using sh. shell = os.environ.get("SHELL") if shell: - kwargs['executable'] = shell + kwargs["executable"] = shell # For any stream that is a StdSim, we will use a pipe so we can capture its output proc = subprocess.Popen( # noqa: S602 @@ -3233,7 +3233,7 @@ def _redirect_output(self, statement: Statement) -> utils.RedirectionSavedState: if proc.returncode is not None: subproc_stdin.close() new_stdout.close() - raise RedirectionError(f'Pipe process exited with code {proc.returncode} before command could run') + raise RedirectionError(f"Pipe process exited with code {proc.returncode} before command could run") redir_saved_state.redirecting = True cmd_pipe_proc_reader = utils.ProcReader(proc, self.stdout, sys.stderr) @@ -3245,12 +3245,12 @@ def _redirect_output(self, statement: Statement) -> utils.RedirectionSavedState: if statement.redirect_to: # redirecting to a file # statement.output can only contain REDIRECTION_APPEND or REDIRECTION_OUTPUT - mode = 'a' if statement.redirector == constants.REDIRECTION_APPEND else 'w' + mode = "a" if statement.redirector == constants.REDIRECTION_APPEND else "w" try: # Use line buffering new_stdout = cast(TextIO, open(su.strip_quotes(statement.redirect_to), mode=mode, buffering=1)) # noqa: SIM115 except OSError as ex: - raise RedirectionError('Failed to redirect output') from ex + raise RedirectionError("Failed to redirect output") from ex redir_saved_state.redirecting = True @@ -3344,7 +3344,7 @@ def _get_command_category(self, func: BoundCommandFunc) -> str: # Otherwise get the category from its defining class. else: defining_cls = get_defining_class(func) - category = getattr(defining_cls, 'DEFAULT_CATEGORY', self.DEFAULT_CATEGORY) + category = getattr(defining_cls, "DEFAULT_CATEGORY", self.DEFAULT_CATEGORY) return category @@ -3459,7 +3459,7 @@ def _read_raw_input( # If this is an interactive pipe, then display the prompt first if self.interactive_pipe: - self.poutput(prompt_str, end='') + self.poutput(prompt_str, end="") self.stdout.flush() # Wait for the next line of input @@ -3473,11 +3473,11 @@ def _read_raw_input( # live session. Print the prompt and the command so they appear in the # output stream before the results. if not self.interactive_pipe and self.echo: - end = "" if line.endswith('\n') else "\n" + end = "" if line.endswith("\n") else "\n" - self.poutput(f'{prompt_str}{line}', end=end) + self.poutput(f"{prompt_str}{line}", end=end) - return line.rstrip('\r\n') + return line.rstrip("\r\n") def _resolve_completer( self, @@ -3498,7 +3498,7 @@ def _resolve_completer( if parser is None: parser = argparse_utils.DEFAULT_ARGUMENT_PARSER(add_help=False) parser.add_argument( - 'arg', + "arg", suppress_tab_hint=True, choices=choices, choices_provider=choices_provider, @@ -3510,7 +3510,7 @@ def _resolve_completer( def read_input( self, - prompt: str = '', + prompt: str = "", *, history: Sequence[str] | None = None, preserve_quotes: bool = False, @@ -3564,7 +3564,7 @@ def read_input( def read_secret( self, - prompt: str = '', + prompt: str = "", ) -> str: """Read a secret from stdin without displaying the value on the screen. @@ -3692,8 +3692,8 @@ def _cmdloop(self) -> None: try: line = self._read_command_line(self.prompt) except KeyboardInterrupt: - self.poutput('^C') - line = '' + self.poutput("^C") + line = "" except EOFError: line = "_eof" @@ -3751,7 +3751,7 @@ def _build_alias_create_parser(cls) -> Cmd2ArgumentParser: alias_create_notes = Text.assemble( "If you want to use redirection, pipes, or terminators in the value of the alias, then quote them.", "\n\n", - (" alias create save_results print_results \">\" out.txt\n", Cmd2Style.COMMAND_LINE), + (' alias create save_results print_results ">" out.txt\n', Cmd2Style.COMMAND_LINE), "\n\n", ( "Since aliases are resolved during parsing, completion will function as it would " @@ -3761,22 +3761,22 @@ def _build_alias_create_parser(cls) -> Cmd2ArgumentParser: alias_create_parser.epilog = TextGroup("Notes", alias_create_notes) # Add arguments - alias_create_parser.add_argument('name', help='name of this alias') + alias_create_parser.add_argument("name", help="name of this alias") alias_create_parser.add_argument( - 'command', - help='command, alias, or macro to run', + "command", + help="command, alias, or macro to run", choices_provider=cls._get_commands_aliases_and_macros_choices, ) alias_create_parser.add_argument( - 'command_args', + "command_args", nargs=argparse.REMAINDER, - help='arguments to pass to command', + help="arguments to pass to command", completer=cls.path_complete, ) return alias_create_parser - @as_subcommand_to('alias', 'create', _build_alias_create_parser, help="create or overwrite an alias") + @as_subcommand_to("alias", "create", _build_alias_create_parser, help="create or overwrite an alias") def _alias_create(self, args: argparse.Namespace) -> None: """Create or overwrite an alias.""" self.last_result = False @@ -3803,7 +3803,7 @@ def _alias_create(self, args: argparse.Namespace) -> None: # Build the alias value string value = args.command if args.command_args: - value += ' ' + ' '.join(args.command_args) + value += " " + " ".join(args.command_args) # Set the alias result = "overwritten" if args.name in self.aliases else "created" @@ -3818,18 +3818,18 @@ def _build_alias_delete_parser(cls) -> Cmd2ArgumentParser: alias_delete_description = "Delete specified aliases or all aliases if --all is used." alias_delete_parser = argparse_utils.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("-a", "--all", action="store_true", help="delete all aliases") alias_delete_parser.add_argument( - 'names', + "names", nargs=argparse.ZERO_OR_MORE, - help='alias(es) to delete', + help="alias(es) to delete", choices_provider=cls._get_alias_choices, table_columns=["Value"], ) return alias_delete_parser - @as_subcommand_to('alias', 'delete', _build_alias_delete_parser, help="delete aliases") + @as_subcommand_to("alias", "delete", _build_alias_delete_parser, help="delete aliases") def _alias_delete(self, args: argparse.Namespace) -> None: """Delete aliases.""" self.last_result = True @@ -3862,16 +3862,16 @@ def _build_alias_list_parser(cls) -> Cmd2ArgumentParser: alias_list_parser = argparse_utils.DEFAULT_ARGUMENT_PARSER(description=alias_list_description) alias_list_parser.add_argument( - 'names', + "names", nargs=argparse.ZERO_OR_MORE, - help='alias(es) to list', + help="alias(es) to list", choices_provider=cls._get_alias_choices, table_columns=["Value"], ) return alias_list_parser - @as_subcommand_to('alias', 'list', _build_alias_list_parser, help="list aliases") + @as_subcommand_to("alias", "list", _build_alias_list_parser, help="list aliases") def _alias_list(self, args: argparse.Namespace) -> None: """List some or all aliases as 'alias create' commands.""" self.last_result = {} # dict[alias_name, alias_value] @@ -3902,7 +3902,7 @@ def _alias_list(self, args: argparse.Namespace) -> None: val = command if command_args: - val += ' ' + ' '.join(command_args) + val += " " + " ".join(command_args) self.poutput(f"alias create {name} {val}") self.last_result[name] = val @@ -3994,11 +3994,11 @@ def _build_macro_create_parser(cls) -> Cmd2ArgumentParser: "\n\n", "To quote an argument in the resolved command, quote it during creation.", "\n\n", - (" macro create backup !cp \"{1}\" \"{1}.orig\"", Cmd2Style.COMMAND_LINE), + (' macro create backup !cp "{1}" "{1}.orig"', Cmd2Style.COMMAND_LINE), "\n\n", "If you want to use redirection, pipes, or terminators in the value of the macro, then quote them.", "\n\n", - (" macro create show_results print_results -type {1} \"|\" less", Cmd2Style.COMMAND_LINE), + (' macro create show_results print_results -type {1} "|" less', Cmd2Style.COMMAND_LINE), "\n\n", ( "Since macros don't resolve until after you press Enter, their arguments complete as paths. " @@ -4008,22 +4008,22 @@ def _build_macro_create_parser(cls) -> Cmd2ArgumentParser: macro_create_parser.epilog = TextGroup("Notes", macro_create_notes) # Add arguments - macro_create_parser.add_argument('name', help='name of this macro') + macro_create_parser.add_argument("name", help="name of this macro") macro_create_parser.add_argument( - 'command', - help='command, alias, or macro to run', + "command", + help="command, alias, or macro to run", choices_provider=cls._get_commands_aliases_and_macros_choices, ) macro_create_parser.add_argument( - 'command_args', + "command_args", nargs=argparse.REMAINDER, - help='arguments to pass to command', + help="arguments to pass to command", completer=cls.path_complete, ) return macro_create_parser - @as_subcommand_to('macro', 'create', _build_macro_create_parser, help="create or overwrite a macro") + @as_subcommand_to("macro", "create", _build_macro_create_parser, help="create or overwrite a macro") def _macro_create(self, args: argparse.Namespace) -> None: """Create or overwrite a macro.""" self.last_result = False @@ -4050,7 +4050,7 @@ def _macro_create(self, args: argparse.Namespace) -> None: # Build the macro value string value = args.command if args.command_args: - value += ' ' + ' '.join(args.command_args) + value += " " + " ".join(args.command_args) # Find all normal arguments macro_args = [] @@ -4108,18 +4108,18 @@ def _build_macro_delete_parser(cls) -> Cmd2ArgumentParser: macro_delete_description = "Delete specified macros or all macros if --all is used." macro_delete_parser = argparse_utils.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("-a", "--all", action="store_true", help="delete all macros") macro_delete_parser.add_argument( - 'names', + "names", nargs=argparse.ZERO_OR_MORE, - help='macro(s) to delete', + help="macro(s) to delete", choices_provider=cls._get_macro_choices, table_columns=["Value"], ) return macro_delete_parser - @as_subcommand_to('macro', 'delete', _build_macro_delete_parser, help="delete macros") + @as_subcommand_to("macro", "delete", _build_macro_delete_parser, help="delete macros") def _macro_delete(self, args: argparse.Namespace) -> None: """Delete macros.""" self.last_result = True @@ -4152,16 +4152,16 @@ def _build_macro_list_parser(cls) -> Cmd2ArgumentParser: macro_list_parser = argparse_utils.DEFAULT_ARGUMENT_PARSER(description=macro_list_description) macro_list_parser.add_argument( - 'names', + "names", nargs=argparse.ZERO_OR_MORE, - help='macro(s) to list', + help="macro(s) to list", choices_provider=cls._get_macro_choices, table_columns=["Value"], ) return macro_list_parser - @as_subcommand_to('macro', 'list', _build_macro_list_parser, help="list macros") + @as_subcommand_to("macro", "list", _build_macro_list_parser, help="list macros") def _macro_list(self, args: argparse.Namespace) -> None: """List macros.""" self.last_result = {} # dict[macro_name, macro_value] @@ -4192,7 +4192,7 @@ def _macro_list(self, args: argparse.Namespace) -> None: val = command if command_args: - val += ' ' + ' '.join(command_args) + val += " " + " ".join(command_args) self.poutput(f"macro create {name} {val}") self.last_result[name] = val @@ -4213,7 +4213,7 @@ def complete_help_subcommands( ) -> Completions: """Completes the subcommands argument of help.""" # Make sure we have a command whose subcommands we will complete - command = arg_tokens['command'][0] + command = arg_tokens["command"][0] if not command: return Completions() @@ -4222,7 +4222,7 @@ def complete_help_subcommands( return Completions() completer = argparse_completer.DEFAULT_AP_COMPLETER(argparser, self) - return completer.complete_subcommand_help(text, line, begidx, endidx, arg_tokens['subcommands']) + return completer.complete_subcommand_help(text, line, begidx, endidx, arg_tokens["subcommands"]) def _build_command_info(self) -> tuple[dict[str, list[str]], list[str]]: """Categorizes and sorts visible commands and help topics for display. @@ -4256,19 +4256,19 @@ def _build_help_parser(cls) -> Cmd2ArgumentParser: description="List available commands or provide detailed help for a specific command." ) help_parser.add_argument( - '-v', - '--verbose', - action='store_true', + "-v", + "--verbose", + action="store_true", help="print a list of all commands with descriptions of each", ) help_parser.add_argument( - 'command', + "command", nargs=argparse.OPTIONAL, help="command to retrieve help for", completer=cls.complete_help_command, ) help_parser.add_argument( - 'subcommands', + "subcommands", nargs=argparse.REMAINDER, help="subcommand(s) to retrieve help for", completer=cls.complete_help_subcommands, @@ -4413,7 +4413,7 @@ def _print_documented_command_topics(self, header: str, cmds: Sequence[str], ver doc = cmd_func.__doc__ # Attempt to locate the first documentation block - cmd_desc = strip_doc_annotations(doc) if doc else '' + cmd_desc = strip_doc_annotations(doc) if doc else "" # Add this command to the table topic_table.add_row(command, cmd_desc) @@ -4505,7 +4505,7 @@ def do_shortcuts(self, _: argparse.Namespace) -> None: """List available shortcuts.""" # Sort the shortcut tuples by name sorted_shortcuts = sorted(self.statement_parser.shortcuts, key=lambda x: utils.DEFAULT_STR_SORT_KEY(x[0])) - result = "\n".join(f'{sc[0]}: {sc[1]}' for sc in sorted_shortcuts) + result = "\n".join(f"{sc[0]}: {sc[1]}" for sc in sorted_shortcuts) self.poutput(f"Shortcuts for other commands:\n{result}") self.last_result = True @@ -4528,7 +4528,7 @@ def do__eof(self, _: argparse.Namespace) -> bool | None: self.poutput() # self.last_result will be set by do_quit() - return self.do_quit('') + return self.do_quit("") @staticmethod def _build_quit_parser() -> Cmd2ArgumentParser: @@ -4541,7 +4541,7 @@ def do_quit(self, _: argparse.Namespace) -> bool | None: self.last_result = True return True - def select(self, opts: str | Iterable[str] | Iterable[tuple[Any, str | None]], prompt: str = 'Your choice? ') -> Any: + def select(self, opts: str | Iterable[str] | Iterable[tuple[Any, str | None]], prompt: str = "Your choice? ") -> Any: """Present a menu to the user. Modeled after the bash shell's SELECT. Returns the item chosen. @@ -4579,21 +4579,21 @@ def select(self, opts: str | Iterable[str] | Iterable[tuple[Any, str | None]], p if result is not None: return result except KeyboardInterrupt: - self.poutput('^C') + self.poutput("^C") raise # Non-interactive fallback for idx, (_, text) in enumerate(fulloptions): - self.poutput(' %2d. %s' % (idx + 1, text)) # noqa: UP031 + self.poutput(" %2d. %s" % (idx + 1, text)) # noqa: UP031 while True: try: response = self.read_input(prompt) except EOFError: - response = '' + response = "" self.poutput() except KeyboardInterrupt: - self.poutput('^C') + self.poutput("^C") raise if not response: @@ -4621,9 +4621,9 @@ def _build_base_set_parser(cls) -> Cmd2ArgumentParser: ) base_set_parser = argparse_utils.DEFAULT_ARGUMENT_PARSER(description=set_description) base_set_parser.add_argument( - 'param', + "param", nargs=argparse.OPTIONAL, - help='parameter to set or view', + help="parameter to set or view", choices_provider=cls._get_settable_choices, table_columns=["Value", "Description"], ) @@ -4634,7 +4634,7 @@ def complete_set_value( self, text: str, line: str, begidx: int, endidx: int, arg_tokens: Mapping[str, Sequence[str]] ) -> Completions: """Completes the value argument of set.""" - param = arg_tokens['param'][0] + param = arg_tokens["param"][0] try: settable = self.settables[param] except KeyError as ex: @@ -4645,7 +4645,7 @@ def complete_set_value( # Settables with choices list the values of those choices instead of the arg name # in help text and this shows in completion hints. Set metavar to avoid this. - arg_name = 'value' + arg_name = "value" settable_parser.add_argument( arg_name, metavar=arg_name, @@ -4666,9 +4666,9 @@ def _build_set_parser(cls) -> Cmd2ArgumentParser: # Create the parser for the set command set_parser = cls._build_base_set_parser() set_parser.add_argument( - 'value', + "value", nargs=argparse.OPTIONAL, - help='new value for settable', + help="new value for settable", completer=cls.complete_set_value, suppress_tab_hint=True, ) @@ -4735,9 +4735,9 @@ def do_set(self, args: argparse.Namespace) -> None: @classmethod def _build_shell_parser(cls) -> Cmd2ArgumentParser: shell_parser = argparse_utils.DEFAULT_ARGUMENT_PARSER(description="Execute a command as if at the OS prompt.") - shell_parser.add_argument('command', help='the command to run', completer=cls.shell_cmd_complete) + shell_parser.add_argument("command", help="the command to run", completer=cls.shell_cmd_complete) shell_parser.add_argument( - 'command_args', nargs=argparse.REMAINDER, help='arguments to pass to command', completer=cls.path_complete + "command_args", nargs=argparse.REMAINDER, help="arguments to pass to command", completer=cls.path_complete ) return shell_parser @@ -4752,7 +4752,7 @@ def do_shell(self, args: argparse.Namespace) -> None: kwargs: dict[str, Any] = {} # Set OS-specific parameters - if sys.platform.startswith('win'): + if sys.platform.startswith("win"): # Windows returns STATUS_CONTROL_C_EXIT when application stopped by Ctrl-C ctrl_c_ret_code = 0xC000013A else: @@ -4768,14 +4768,14 @@ def do_shell(self, args: argparse.Namespace) -> None: # to run builtin commands of their preferred shell. shell = os.environ.get("SHELL") if shell: - kwargs['executable'] = shell + kwargs["executable"] = shell # Create a list of arguments to shell tokens = [args.command, *args.command_args] # Expand ~ where needed utils.expand_user_in_tokens(tokens) - expanded_command = ' '.join(tokens) + expanded_command = " ".join(tokens) # Prevent KeyboardInterrupts while in the shell process. The shell process will # still receive the SIGINT since it is in the same process group as us. @@ -4813,7 +4813,7 @@ def _reset_py_display() -> None: is run. Therefore, this method only needs to be called before creating a Python console. """ # Delete any prompts that have been set - attributes = ['ps1', 'ps2', 'ps3'] + attributes = ["ps1", "ps2", "ps3"] for cur_attr in attributes: with contextlib.suppress(KeyError): del sys.__dict__[cur_attr] @@ -4833,7 +4833,7 @@ def _set_up_py_shell_env(self, interp: InteractiveConsole) -> _SavedCmd2Env: self._reset_py_display() # Enable completion if readline is available - if not sys.platform.startswith('win'): + if not sys.platform.startswith("win"): import readline import rlcompleter @@ -4844,7 +4844,7 @@ def _set_up_py_shell_env(self, interp: InteractiveConsole) -> _SavedCmd2Env: readline.set_completer(rlcompleter.Completer(interp.locals).complete) # type: ignore[arg-type] # Use the correct binding based on whether LibEdit or Readline is being used - if 'libedit' in (readline.__doc__ or ''): + if "libedit" in (readline.__doc__ or ""): readline.parse_and_bind("bind ^I rl_complete") else: readline.parse_and_bind("tab: complete") @@ -4857,7 +4857,7 @@ def _restore_cmd2_env(self, cmd2_env: _SavedCmd2Env) -> None: :param cmd2_env: the environment settings to restore """ # Restore the readline completer - if not sys.platform.startswith('win'): + if not sys.platform.startswith("win"): import readline readline.set_completer(cmd2_env.completer) @@ -4894,7 +4894,7 @@ def py_quit() -> None: try: self._in_py = True - py_code_to_run = '' + py_code_to_run = "" # Make a copy of self.py_locals for the locals dictionary in the Python environment we are creating. # This is to prevent pyscripts from editing it. (e.g. locals().clear()). It also ensures a pyscript's @@ -4902,11 +4902,11 @@ def py_quit() -> None: # it's OK for py_locals to contain objects which are editable in a pyscript. local_vars = self.py_locals.copy() local_vars[self.py_bridge_name] = py_bridge - local_vars['quit'] = py_quit - local_vars['exit'] = py_quit + local_vars["quit"] = py_quit + local_vars["exit"] = py_quit if self.self_in_py: - local_vars['self'] = self + local_vars["self"] = self # Handle case where we were called by do_run_pyscript() if pyscript is not None: @@ -4920,8 +4920,8 @@ def py_quit() -> None: self.perror(f"Error reading script file '{expanded_filename}': {ex}") return None - local_vars['__name__'] = '__main__' - local_vars['__file__'] = expanded_filename + local_vars["__name__"] = "__main__" + local_vars["__file__"] = expanded_filename # Place the script's directory at sys.path[0] just as Python does when executing a script saved_sys_path = list(sys.path) @@ -4929,7 +4929,7 @@ def py_quit() -> None: else: # This is the default name chosen by InteractiveConsole when no locals are passed in - local_vars['__name__'] = '__console__' + local_vars["__name__"] = "__console__" # Create the Python interpreter self.last_result = True @@ -4947,7 +4947,7 @@ def py_quit() -> None: else: cprt = 'Type "help", "copyright", "credits" or "license" for more information.' instructions = ( - 'Use `Ctrl-D` (Unix) / `Ctrl-Z` (Windows), `quit()`, `exit()` to exit.\n' + "Use `Ctrl-D` (Unix) / `Ctrl-Z` (Windows), `quit()`, `exit()` to exit.\n" f'Run CLI commands with: {self.py_bridge_name}("command ...")' ) banner = f"Python {sys.version} on {sys.platform}\n{cprt}\n\n{instructions}\n" @@ -4961,7 +4961,7 @@ def py_quit() -> None: # Since quit() or exit() raise an EmbeddedConsoleExit, interact() exits before printing # the exitmsg. Therefore, we will not provide it one and print it manually later. - interp.interact(banner=banner, exitmsg='') + interp.interact(banner=banner, exitmsg="") except BaseException: # noqa: BLE001, S110 # We don't care about any exception that happened in the interactive console pass @@ -4998,9 +4998,9 @@ def _build_run_pyscript_parser(cls) -> Cmd2ArgumentParser: run_pyscript_parser = argparse_utils.DEFAULT_ARGUMENT_PARSER( description="Run Python script within this application's environment." ) - run_pyscript_parser.add_argument('script_path', help='path to the script file', completer=cls.path_complete) + run_pyscript_parser.add_argument("script_path", help="path to the script file", completer=cls.path_complete) run_pyscript_parser.add_argument( - 'script_arguments', nargs=argparse.REMAINDER, help='arguments to pass to script', completer=cls.path_complete + "script_arguments", nargs=argparse.REMAINDER, help="arguments to pass to script", completer=cls.path_complete ) return run_pyscript_parser @@ -5018,10 +5018,10 @@ def do_run_pyscript(self, args: argparse.Namespace) -> bool | None: # Add some protection against accidentally running a non-Python file. The happens when users # mix up run_script and run_pyscript. - if not args.script_path.endswith('.py'): + if not args.script_path.endswith(".py"): self.pwarning(f"'{args.script_path}' does not have a .py extension") - selection = self.select('Yes No', 'Continue to try to run it as a Python script? ') - if selection != 'Yes': + selection = self.select("Yes No", "Continue to try to run it as a Python script? ") + if selection != "Yes": return None # Save current command line arguments @@ -5091,12 +5091,12 @@ def do_ipy(self, _: argparse.Namespace) -> bool | None: # pragma: no cover local_vars = self.py_locals.copy() local_vars[self.py_bridge_name] = py_bridge if self.self_in_py: - local_vars['self'] = self + local_vars["self"] = self # Configure IPython config = traitlets_loader.Config() config.InteractiveShell.banner2 = ( - 'Entering an IPython shell. Type exit, quit, or Ctrl-D to exit.\n' + "Entering an IPython shell. Type exit, quit, or Ctrl-D to exit.\n" f'Run CLI commands with: {self.py_bridge_name}("command ...")\n' ) @@ -5123,41 +5123,41 @@ def _build_history_parser(cls) -> Cmd2ArgumentParser: description=history_description, formatter_class=ru.RawTextCmd2HelpFormatter ) 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("-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', + "-o", + "--output-file", + metavar="FILE", + help="output commands to a script file, implies -s", completer=cls.path_complete, ) - history_action_group.add_argument('-c', '--clear', action='store_true', help='clear all history') + history_action_group.add_argument("-c", "--clear", action="store_true", help="clear all history") - history_format_group = history_parser.add_argument_group(title='formatting') + history_format_group = history_parser.add_argument_group(title="formatting") history_format_group.add_argument( - '-s', - '--script', - action='store_true', - help='output commands in script format, i.e. without command numbers', + "-s", + "--script", + action="store_true", + help="output commands in script format, i.e. without command numbers", ) history_format_group.add_argument( - '-x', - '--expanded', - action='store_true', - help='output fully parsed commands with shortcuts, aliases, and macros expanded', + "-x", + "--expanded", + action="store_true", + help="output fully parsed commands with shortcuts, aliases, and macros expanded", ) history_format_group.add_argument( - '-v', - '--verbose', - action='store_true', - help='display history and include expanded commands if they differ from the typed command', + "-v", + "--verbose", + action="store_true", + help="display history and include expanded commands if they differ from the typed command", ) history_format_group.add_argument( - '-a', - '--all', - action='store_true', - help='display all commands, including ones persisted from previous sessions', + "-a", + "--all", + action="store_true", + help="display all commands, including ones persisted from previous sessions", ) history_arg_help = ( @@ -5167,7 +5167,7 @@ def _build_history_parser(cls) -> Cmd2ArgumentParser: "string items containing string\n" "/regex/ items matching regular expression" ) - history_parser.add_argument('arg', nargs=argparse.OPTIONAL, help=history_arg_help) + history_parser.add_argument("arg", nargs=argparse.OPTIONAL, help=history_arg_help) return history_parser @@ -5221,14 +5221,14 @@ def do_history(self, args: argparse.Namespace) -> bool | None: self.last_result = True return stop elif args.edit: - fd, fname = tempfile.mkstemp(suffix='.txt', text=True) + fd, fname = tempfile.mkstemp(suffix=".txt", text=True) fobj: TextIO - with os.fdopen(fd, 'w') as fobj: + with os.fdopen(fd, "w") as fobj: for command in history.values(): if command.statement.multiline_command: - fobj.write(f'{command.expanded}\n') + fobj.write(f"{command.expanded}\n") else: - fobj.write(f'{command.raw}\n') + fobj.write(f"{command.raw}\n") try: self.run_editor(fname) @@ -5239,13 +5239,13 @@ def do_history(self, args: argparse.Namespace) -> bool | None: elif args.output_file: full_path = os.path.abspath(os.path.expanduser(args.output_file)) try: - with open(full_path, 'w') as fobj: + with open(full_path, "w") as fobj: for item in history.values(): if item.statement.multiline_command: fobj.write(f"{item.expanded}\n") else: fobj.write(f"{item.raw}\n") - plural = '' if len(history) == 1 else 's' + plural = "" if len(history) == 1 else "s" except OSError as ex: self.perror(f"Error saving history file '{full_path}': {ex}") else: @@ -5270,16 +5270,16 @@ def _get_history(self, args: argparse.Namespace) -> dict[int, HistoryItem]: except ValueError: pass - if '..' in args.arg or ':' in args.arg: + if ".." in args.arg or ":" in args.arg: # Get a slice of history history = self.history.span(args.arg, args.all) - elif args.arg.startswith(r'/') and args.arg.endswith(r'/'): + elif args.arg.startswith(r"/") and args.arg.endswith(r"/"): history = self.history.regex_search(args.arg, args.all) else: history = self.history.str_search(args.arg, args.all) else: # Get a copy of the history so it doesn't get mutated while we are using it - history = self.history.span(':', args.all) + history = self.history.span(":", args.all) return history def _initialize_history(self, hist_file: str) -> None: @@ -5314,7 +5314,7 @@ def _initialize_history(self, hist_file: str) -> None: # Read history file try: - with open(hist_file, 'rb') as fobj: + with open(hist_file, "rb") as fobj: compressed_bytes = fobj.read() except FileNotFoundError: compressed_bytes = b"" @@ -5343,7 +5343,7 @@ def _initialize_history(self, hist_file: str) -> None: decompress_exceptions: tuple[type[Exception]] = (OSError, ValueError) # type: ignore[no-redef] try: - history_json = decompress_lib.decompress(compressed_bytes).decode(encoding='utf-8') + history_json = decompress_lib.decompress(compressed_bytes).decode(encoding="utf-8") except decompress_exceptions as ex: self.perror( f"Error decompressing persistent history data '{hist_file}': {ex}\n" @@ -5377,10 +5377,10 @@ def _persist_history(self) -> None: self.history.truncate(self._persistent_history_length) history_json = self.history.to_json() - compressed_bytes = compress_lib.compress(history_json.encode(encoding='utf-8')) + compressed_bytes = compress_lib.compress(history_json.encode(encoding="utf-8")) try: - with open(self.persistent_history_file, 'wb') as fobj: + with open(self.persistent_history_file, "wb") as fobj: fobj.write(compressed_bytes) except OSError as ex: self.perror(f"Cannot write persistent history file '{self.persistent_history_file}': {ex}") @@ -5398,7 +5398,7 @@ def _build_edit_parser(cls) -> Cmd2ArgumentParser: ) edit_parser.add_argument( - 'file_path', + "file_path", nargs=argparse.OPTIONAL, help="optional path to a file to open in editor", completer=cls.path_complete, @@ -5443,7 +5443,7 @@ def _build_base_run_script_parser(cls) -> Cmd2ArgumentParser: run_script_parser = argparse_utils.DEFAULT_ARGUMENT_PARSER(description=run_script_description) run_script_parser.add_argument( - 'script_path', + "script_path", help="path to the script file", completer=cls.path_complete, ) @@ -5465,10 +5465,10 @@ def do_run_script(self, args: argparse.Namespace) -> bool | None: # Add some protection against accidentally running a Python file. The happens when users # mix up run_script and run_pyscript. - if expanded_path.endswith('.py'): + if expanded_path.endswith(".py"): self.pwarning(f"'{expanded_path}' appears to be a Python file") - selection = self.select('Yes No', 'Continue to try to run it as a text script? ') - if selection != 'Yes': + selection = self.select("Yes No", "Continue to try to run it as a text script? ") + if selection != "Yes": return None try: @@ -5483,7 +5483,7 @@ def do_run_script(self, args: argparse.Namespace) -> bool | None: return None # Read all lines of the script - with open(expanded_path, encoding='utf-8') as target: + with open(expanded_path, encoding="utf-8") as target: script_commands = target.read().splitlines() except OSError as ex: self.perror(f"Problem accessing script from '{expanded_path}': {ex}") @@ -5537,7 +5537,7 @@ def do__relative_run_script(self, args: argparse.Namespace) -> bool | None: """ script_path = args.script_path # NOTE: Relative path is an absolute path, it is just relative to the current script directory - relative_path = os.path.join(self._current_script_dir or '', script_path) + relative_path = os.path.join(self._current_script_dir or "", script_path) # self.last_result will be set by do_run_script() return self.do_run_script(su.quote(relative_path)) @@ -5689,7 +5689,7 @@ def _report_disabled_command_usage(self, *_args: Any, message_to_print: str, **_ """ self.perror(message_to_print, style=None) - def cmdloop(self, intro: RenderableType = '') -> int: + def cmdloop(self, intro: RenderableType = "") -> int: """Deal with extra features provided by cmd2, this is an outer wrapper around _cmdloop(). _cmdloop() provides the main loop. This provides the following extra features provided by cmd2: @@ -5710,7 +5710,7 @@ def cmdloop(self, intro: RenderableType = '') -> int: original_sigint_handler = signal.getsignal(signal.SIGINT) signal.signal(signal.SIGINT, self.sigint_handler) - if not sys.platform.startswith('win'): + if not sys.platform.startswith("win"): original_sighup_handler = signal.getsignal(signal.SIGHUP) signal.signal(signal.SIGHUP, self.termination_signal_handler) @@ -5741,7 +5741,7 @@ def cmdloop(self, intro: RenderableType = '') -> int: # Restore original signal handlers signal.signal(signal.SIGINT, original_sigint_handler) - if not sys.platform.startswith('win'): + if not sys.platform.startswith("win"): signal.signal(signal.SIGHUP, original_sighup_handler) signal.signal(signal.SIGTERM, original_sigterm_handler) @@ -5768,8 +5768,8 @@ def _validate_callable_param_count(cls, func: Callable[..., Any], count: int) -> # validate that the callable has the right number of parameters nparam = len(signature.parameters) if nparam != count: - plural = '' if nparam == 1 else 's' - raise TypeError(f'{func.__name__} has {nparam} positional argument{plural}, expected {count}') + plural = "" if nparam == 1 else "s" + raise TypeError(f"{func.__name__} has {nparam} positional argument{plural}, expected {count}") @classmethod def _validate_prepostloop_callable(cls, func: Callable[[], None]) -> None: @@ -5808,7 +5808,7 @@ def register_postparsing_hook(self, func: Callable[[plugin.PostparsingData], plu self._validate_postparsing_callable(func) self._postparsing_hooks.append(func) - CommandDataType = TypeVar('CommandDataType') + CommandDataType = TypeVar("CommandDataType") @classmethod def _validate_prepostcmd_hook( @@ -5824,12 +5824,12 @@ def _validate_prepostcmd_hook( _param_name, par_ann = next(iter(type_hints.items())) # validate the parameter has the right annotation if par_ann != data_type: - raise TypeError(f'argument 1 of {func.__name__} has incompatible type {par_ann}, expected {data_type}') + raise TypeError(f"argument 1 of {func.__name__} has incompatible type {par_ann}, expected {data_type}") # validate the return value has the right annotation if ret_ann is None: - raise TypeError(f'{func.__name__} does not have a declared return type, expected {data_type}') + raise TypeError(f"{func.__name__} does not have a declared return type, expected {data_type}") if ret_ann != data_type: - raise TypeError(f'{func.__name__} has incompatible return type {ret_ann}, expected {data_type}') + raise TypeError(f"{func.__name__} has incompatible return type {ret_ann}, expected {data_type}") def register_precmd_hook(self, func: Callable[[plugin.PrecommandData], plugin.PrecommandData]) -> None: """Register a hook to be called before the command function.""" diff --git a/cmd2/command_set.py b/cmd2/command_set.py index 773b676a6..33f0e88c4 100644 --- a/cmd2/command_set.py +++ b/cmd2/command_set.py @@ -56,7 +56,7 @@ class MyCommandSet(CommandSet[MyCustomApp]): :raises CommandSetRegistrationError: if CommandSet is not registered. """ if self._cmd_internal is None: - raise CommandSetRegistrationError('This CommandSet is not registered') + raise CommandSetRegistrationError("This CommandSet is not registered") return self._cmd_internal def on_register(self, cmd: CmdT) -> None: @@ -70,7 +70,7 @@ def on_register(self, cmd: CmdT) -> None: :raises CommandSetRegistrationError: if CommandSet is already registered. """ if self._cmd_internal is not None: - raise CommandSetRegistrationError('This CommandSet has already been registered') + raise CommandSetRegistrationError("This CommandSet has already been registered") self._cmd_internal = cmd def on_registered(self) -> None: @@ -110,10 +110,10 @@ def add_settable(self, settable: Settable) -> None: """ if (cmd := self._cmd_internal) is not None: # Determine the name to check for collisions in the main app - check_name = settable.name if not cmd.always_prefix_settables else f'{self._settable_prefix}.{settable.name}' + check_name = settable.name if not cmd.always_prefix_settables else f"{self._settable_prefix}.{settable.name}" if check_name in cmd.settables and settable.name not in self._settables: - raise KeyError(f'Duplicate settable: {settable.name}') + raise KeyError(f"Duplicate settable: {settable.name}") self._settables[settable.name] = settable diff --git a/cmd2/completion.py b/cmd2/completion.py index 8065f5a47..f770bb5a0 100644 --- a/cmd2/completion.py +++ b/cmd2/completion.py @@ -37,7 +37,7 @@ class CompletionItem: # Regular expression to identify whitespace characters that are rendered as # control sequences (like ^J or ^I) in the completion menu. - _CONTROL_WHITESPACE_RE = re.compile(r'\r\n|[\n\r\t\f\v]') + _CONTROL_WHITESPACE_RE = re.compile(r"\r\n|[\n\r\t\f\v]") # The underlying object this completion represents (e.g., str, int, Path). # This is used to support argparse choices validation. @@ -74,7 +74,7 @@ def _clean_display(cls, val: str) -> str: :param val: string to be cleaned :return: the cleaned string """ - return cls._CONTROL_WHITESPACE_RE.sub(' ', val) + return cls._CONTROL_WHITESPACE_RE.sub(" ", val) def __post_init__(self) -> None: """Finalize the object after initialization.""" @@ -101,7 +101,7 @@ def __post_init__(self) -> None: # Convert strings containing ANSI style sequences to Rich Text objects for correct display width. object.__setattr__( self, - 'table_data', + "table_data", ru.prepare_objects_for_rendering(*renderable_data), ) diff --git a/cmd2/constants.py b/cmd2/constants.py index b33be71f8..dc92be4b5 100644 --- a/cmd2/constants.py +++ b/cmd2/constants.py @@ -3,39 +3,39 @@ # Unless documented in https://cmd2.readthedocs.io/en/latest/api/index.html # nothing here should be considered part of the public API of this module -INFINITY = float('inf') +INFINITY = float("inf") # Used for command parsing, output redirection, completion, and word breaks. Do not change. QUOTES = ['"', "'"] -REDIRECTION_PIPE = '|' -REDIRECTION_OVERWRITE = '>' -REDIRECTION_APPEND = '>>' +REDIRECTION_PIPE = "|" +REDIRECTION_OVERWRITE = ">" +REDIRECTION_APPEND = ">>" REDIRECTION_CHARS = [REDIRECTION_PIPE, REDIRECTION_OVERWRITE] REDIRECTION_TOKENS = [REDIRECTION_PIPE, REDIRECTION_OVERWRITE, REDIRECTION_APPEND] -COMMENT_CHAR = '#' -MULTILINE_TERMINATOR = ';' +COMMENT_CHAR = "#" +MULTILINE_TERMINATOR = ";" -LINE_FEED = '\n' +LINE_FEED = "\n" -DEFAULT_SHORTCUTS = {'?': 'help', '!': 'shell', '@': 'run_script', '@@': '_relative_run_script'} +DEFAULT_SHORTCUTS = {"?": "help", "!": "shell", "@": "run_script", "@@": "_relative_run_script"} # Used as the command name placeholder in disabled command messages. COMMAND_NAME = "" # All command functions start with this -COMMAND_FUNC_PREFIX = 'do_' +COMMAND_FUNC_PREFIX = "do_" # All help functions start with this -HELP_FUNC_PREFIX = 'help_' +HELP_FUNC_PREFIX = "help_" # All command completer functions start with this -COMPLETER_FUNC_PREFIX = 'complete_' +COMPLETER_FUNC_PREFIX = "complete_" # Prefix for private attributes injected by cmd2 -PRIVATE_ATTR_PREFIX = '_cmd2_' +PRIVATE_ATTR_PREFIX = "_cmd2_" # Prefix for public attributes injected by cmd2 -PUBLIC_ATTR_PREFIX = 'cmd2_' +PUBLIC_ATTR_PREFIX = "cmd2_" def cmd2_private_attr_name(name: str) -> str: @@ -44,7 +44,7 @@ def cmd2_private_attr_name(name: str) -> str: :param name: the name of the attribute :return: the prefixed attribute name """ - return f'{PRIVATE_ATTR_PREFIX}{name}' + return f"{PRIVATE_ATTR_PREFIX}{name}" def cmd2_public_attr_name(name: str) -> str: @@ -53,7 +53,7 @@ def cmd2_public_attr_name(name: str) -> str: :param name: the name of the attribute :return: the prefixed attribute name """ - return f'{PUBLIC_ATTR_PREFIX}{name}' + return f"{PUBLIC_ATTR_PREFIX}{name}" ################################################################################################## @@ -70,31 +70,31 @@ def cmd2_public_attr_name(name: str) -> str: # --- Private Internal Attributes --- # Attached to a command function; defines its argument parser -CMD_ATTR_ARGPARSER = cmd2_private_attr_name('argparser') +CMD_ATTR_ARGPARSER = cmd2_private_attr_name("argparser") # Attached to a command function; defines its help section category -CMD_ATTR_HELP_CATEGORY = cmd2_private_attr_name('help_category') +CMD_ATTR_HELP_CATEGORY = cmd2_private_attr_name("help_category") # Attached to a command function; defines whether tokens are unquoted before reaching argparse -CMD_ATTR_PRESERVE_QUOTES = cmd2_private_attr_name('preserve_quotes') +CMD_ATTR_PRESERVE_QUOTES = cmd2_private_attr_name("preserve_quotes") # Attached to a subcommand function; defines the full command path to the parent (e.g., "foo" or "foo bar") -SUBCMD_ATTR_COMMAND = cmd2_private_attr_name('parent_command') +SUBCMD_ATTR_COMMAND = cmd2_private_attr_name("parent_command") # Attached to a subcommand function; defines the name of this specific subcommand (e.g., "bar") -SUBCMD_ATTR_NAME = cmd2_private_attr_name('subcommand_name') +SUBCMD_ATTR_NAME = cmd2_private_attr_name("subcommand_name") # Attached to a subcommand function; specifies kwargs passed to add_parser() -SUBCMD_ATTR_ADD_PARSER_KWARGS = cmd2_private_attr_name('subcommand_add_parser_kwargs') +SUBCMD_ATTR_ADD_PARSER_KWARGS = cmd2_private_attr_name("subcommand_add_parser_kwargs") # Attached to an argparse parser; identifies the CommandSet instance it belongs to -PARSER_ATTR_COMMANDSET_ID = cmd2_private_attr_name('command_set_id') +PARSER_ATTR_COMMANDSET_ID = cmd2_private_attr_name("command_set_id") # --- Public Developer Attributes --- # Attached to an argparse Namespace; contains the Statement object created during parsing -NS_ATTR_STATEMENT = cmd2_public_attr_name('statement') +NS_ATTR_STATEMENT = cmd2_public_attr_name("statement") # Attached to an argparse Namespace; the function to handle the subcommand (or None) -NS_ATTR_SUBCMD_HANDLER = cmd2_public_attr_name('subcmd_handler') +NS_ATTR_SUBCMD_HANDLER = cmd2_public_attr_name("subcmd_handler") diff --git a/cmd2/decorators.py b/cmd2/decorators.py index 961d673a1..9561bc997 100644 --- a/cmd2/decorators.py +++ b/cmd2/decorators.py @@ -71,7 +71,7 @@ def cat_decorator(func: F) -> F: # in cmd2 command functions/callables. As long as the 2-ple of arguments we expect to be there can be # found we can swap out the statement with each decorator's specific parameters ########################## -def _parse_positionals(args: tuple[Any, ...]) -> tuple['Cmd', Statement | str]: +def _parse_positionals(args: tuple[Any, ...]) -> tuple["Cmd", Statement | str]: """Inspect the positional arguments until the cmd2.Cmd argument is found. Assumes that we will find cmd2.Cmd followed by the command statement object or string. @@ -91,7 +91,7 @@ def _parse_positionals(args: tuple[Any, ...]) -> tuple['Cmd', Statement | str]: # This shouldn't happen unless we forget to pass statement in `Cmd.onecmd` or # somehow call the unbound class method. - raise TypeError('Expected arguments: cmd: cmd2.Cmd, statement: Union[Statement, str] Not found') + raise TypeError("Expected arguments: cmd: cmd2.Cmd, statement: Union[Statement, str] Not found") def _arg_swap(args: Sequence[Any], search_arg: Any, *replace_arg: Any) -> list[Any]: @@ -290,7 +290,7 @@ def cmd_wrapper(*args: Any, **kwargs: Any) -> bool | None: arg_parser = cmd2_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 + raise ValueError(f"No argument parser found for {command_name}") # pragma: no cover if ns_provider is None: initial_namespace = None @@ -394,9 +394,9 @@ def arg_decorator(func: F) -> F: # Keyword arguments for subparsers.add_parser() final_kwargs: dict[str, Any] = dict(add_parser_kwargs) if help is not None: - final_kwargs['help'] = help + final_kwargs["help"] = help if aliases: - final_kwargs['aliases'] = tuple(aliases) + final_kwargs["aliases"] = tuple(aliases) setattr(func, constants.SUBCMD_ATTR_ADD_PARSER_KWARGS, final_kwargs) diff --git a/cmd2/history.py b/cmd2/history.py index 599bd13f2..c99a8aac5 100644 --- a/cmd2/history.py +++ b/cmd2/history.py @@ -60,11 +60,11 @@ def single_line_format(statement: Statement) -> str: class HistoryItem: """Class used to represent one command in the history list.""" - _listformat = ' {:>4} {}' - _ex_listformat = ' {:>4}x {}' + _listformat = " {:>4} {}" + _ex_listformat = " {:>4}x {}" # Used in JSON dictionaries - _statement_field = 'statement' + _statement_field = "statement" statement: Statement @@ -105,7 +105,7 @@ def pr(self, idx: int, script: bool = False, expanded: bool = False, verbose: bo ret_str = self._listformat.format(idx, raw) if raw != expanded_command: - ret_str += '\n' + self._ex_listformat.format(idx, expanded_command) + ret_str += "\n" + self._ex_listformat.format(idx, expanded_command) else: ret_str = self.expanded if expanded else single_line_format(self.statement).rstrip() @@ -120,7 +120,7 @@ def to_dict(self) -> dict[str, Any]: return {HistoryItem._statement_field: self.statement.to_dict()} @staticmethod - def from_dict(source_dict: dict[str, Any]) -> 'HistoryItem': + def from_dict(source_dict: dict[str, Any]) -> "HistoryItem": """Restore a HistoryItem from a dictionary. :param source_dict: source data dictionary (generated using to_dict()) @@ -145,9 +145,9 @@ class to gain access to the historical record. """ # Used in JSON dictionaries - _history_version = '4.0.0' - _history_version_field = 'history_version' - _history_items_field = 'history_items' + _history_version = "4.0.0" + _history_version_field = "history_version" + _history_items_field = "history_items" def __init__(self, seq: Iterable[HistoryItem] = ()) -> None: """Initialize History instances.""" @@ -192,7 +192,7 @@ def get(self, index: int) -> HistoryItem: :return: a single [cmd2.history.HistoryItem][] """ if index == 0: - raise IndexError('The first command in history is command 1.') + raise IndexError("The first command in history is command 1.") if index < 0: return self[index] return self[index - 1] @@ -221,9 +221,9 @@ def get(self, index: int) -> HistoryItem: # \s*$ match any whitespace at the end of the input. This is here so # you don't have to trim the input # - spanpattern = re.compile(r'^\s*(?P-?[1-9]\d*)?(?P:|(\.{2,}))(?P-?[1-9]\d*)?\s*$') + spanpattern = re.compile(r"^\s*(?P-?[1-9]\d*)?(?P:|(\.{2,}))(?P-?[1-9]\d*)?\s*$") - def span(self, span: str, include_persisted: bool = False) -> dict[int, 'HistoryItem']: + def span(self, span: str, include_persisted: bool = False) -> dict[int, "HistoryItem"]: """Return a slice of the History list. :param span: string containing an index or a slice @@ -252,9 +252,9 @@ def span(self, span: str, include_persisted: bool = False) -> dict[int, 'History results = self.spanpattern.search(span) if not results: # our regex doesn't match the input, bail out - raise ValueError('History indices must be positive or negative integers, and may not be zero.') + raise ValueError("History indices must be positive or negative integers, and may not be zero.") - start_token = results.group('start') + start_token = results.group("start") if start_token: start = min(self._zero_based_index(start_token), len(self) - 1) if start < 0: @@ -262,7 +262,7 @@ def span(self, span: str, include_persisted: bool = False) -> dict[int, 'History else: start = 0 if include_persisted else self.session_start_index - end_token = results.group('end') + end_token = results.group("end") if end_token: end = min(int(end_token), len(self)) if end < 0: @@ -272,7 +272,7 @@ def span(self, span: str, include_persisted: bool = False) -> dict[int, 'History return self._build_result_dictionary(start, end) - def str_search(self, search: str, include_persisted: bool = False) -> dict[int, 'HistoryItem']: + def str_search(self, search: str, include_persisted: bool = False) -> dict[int, "HistoryItem"]: """Find history items which contain a given string. :param search: the string to search for @@ -291,7 +291,7 @@ def isin(history_item: HistoryItem) -> bool: start = 0 if include_persisted else self.session_start_index return self._build_result_dictionary(start, len(self), isin) - def regex_search(self, regex: str, include_persisted: bool = False) -> dict[int, 'HistoryItem']: + def regex_search(self, regex: str, include_persisted: bool = False) -> dict[int, "HistoryItem"]: """Find history items which match a given regular expression. :param regex: the regular expression to search for. @@ -300,7 +300,7 @@ def regex_search(self, regex: str, include_persisted: bool = False) -> dict[int, or an empty dictionary if the regex was not matched """ regex = regex.strip() - if regex.startswith(r'/') and regex.endswith(r'/'): + if regex.startswith(r"/") and regex.endswith(r"/"): regex = regex[1:-1] finder = re.compile(regex, re.DOTALL | re.MULTILINE) @@ -327,7 +327,7 @@ def truncate(self, max_length: int) -> None: def _build_result_dictionary( self, start: int, end: int, filter_func: Callable[[HistoryItem], bool] | None = None - ) -> dict[int, 'HistoryItem']: + ) -> dict[int, "HistoryItem"]: """Build history search results. :param start: start index to search from @@ -348,7 +348,7 @@ def to_json(self) -> str: return json.dumps(json_dict, ensure_ascii=False, indent=2) @staticmethod - def from_json(history_json: str) -> 'History': + def from_json(history_json: str) -> "History": """Restore History from a JSON string. :param history_json: history data as JSON string (generated using to_json()) diff --git a/cmd2/parsing.py b/cmd2/parsing.py index b0f059c54..6a4a75c7b 100644 --- a/cmd2/parsing.py +++ b/cmd2/parsing.py @@ -64,14 +64,14 @@ class MacroArg: # (? Match '{' not preceded by '{' # \d+ -> Match digits # }(?!}) -> Match '}' not followed by '}' - macro_normal_arg_pattern: ClassVar[re.Pattern[str]] = re.compile(r'(? None: """Finalize the object after initialization.""" # Convert args to an immutable tuple. if not isinstance(self.args, tuple): - object.__setattr__(self, 'args', tuple(self.args)) + object.__setattr__(self, "args", tuple(self.args)) @dataclass(frozen=True) @@ -129,13 +129,13 @@ class Statement(str): # noqa: SLOT000 # Note: If a terminator is present, characters that would otherwise be # redirectors (like '>') are treated as literal arguments if they appear # before the terminator. - args: str = '' + args: str = "" # The original, unmodified input string - raw: str = '' + raw: str = "" # The resolved command name (after shortcut/alias expansion) - command: str = '' + command: str = "" # Whether the command is recognized as a multiline-capable command multiline_command: bool = False @@ -143,17 +143,17 @@ class Statement(str): # noqa: SLOT000 # The character which terminates the command/arguments portion of the input. # While primarily used to signal the end of multiline commands, its presence # defines the boundary between arguments and any subsequent redirection. - terminator: str = '' + terminator: str = "" # Characters appearing after the terminator but before output redirection - suffix: str = '' + suffix: str = "" # The operator used to redirect output (e.g. '>', '>>', or '|'). - redirector: str = '' + redirector: str = "" # The destination for the redirected output (a file path or a shell command). # Quotes are preserved. - redirect_to: str = '' + redirect_to: str = "" def __new__(cls, value: object, *_pos_args: Any, **_kw_args: Any) -> Self: """Create a new instance of Statement. @@ -192,13 +192,13 @@ def post_command(self) -> str: if self.redirect_to: parts.append(self.redirect_to) - return ' '.join(parts) + return " ".join(parts) @property def expanded_command_line(self) -> str: """Concatenate [cmd2.parsing.Statement.command_and_args]() and [cmd2.parsing.Statement.post_command]().""" # Use a space if there is a post_command that doesn't start with a terminator - sep = ' ' if self.post_command and not self.terminator else '' + sep = " " if self.post_command and not self.terminator else "" return f"{self.command_and_args}{sep}{self.post_command}" @property @@ -240,7 +240,7 @@ def from_dict(cls, source_dict: dict[str, Any]) -> Self: raise KeyError("Statement dictionary is missing 'args' field") from None # Filter out 'args' so it isn't passed twice - kwargs = {k: v for k, v in source_dict.items() if k != 'args'} + kwargs = {k: v for k, v in source_dict.items() if k != "args"} return cls(value, **kwargs) @@ -338,11 +338,11 @@ def __init__( second_group_items = [re.escape(x) for x in invalid_command_chars] # add the whitespace and end of string, not escaped because they # are not literals - second_group_items.extend([r'\s', r'\Z']) + second_group_items.extend([r"\s", r"\Z"]) # join them up with a pipe - second_group = '|'.join(second_group_items) + second_group = "|".join(second_group_items) # build the regular expression - expr = rf'\A\s*(\S*?)({second_group})' + expr = rf"\A\s*(\S*?)({second_group})" self._command_pattern = re.compile(expr) def is_valid_command(self, word: str, *, is_subcommand: bool = False) -> tuple[bool, str]: @@ -367,32 +367,32 @@ def is_valid_command(self, word: str, *, is_subcommand: bool = False) -> tuple[b valid = False if not isinstance(word, str): - return False, f'must be a string. Received {type(word)!s} instead' # type: ignore[unreachable] + return False, f"must be a string. Received {type(word)!s} instead" # type: ignore[unreachable] if not word: - return False, 'cannot be an empty string' + return False, "cannot be an empty string" if word.startswith(constants.COMMENT_CHAR): - return False, 'cannot start with the comment character' + return False, "cannot start with the comment character" if not is_subcommand: for shortcut, _ in self.shortcuts: if word.startswith(shortcut): # Build an error string with all shortcuts listed - errmsg = 'cannot start with a shortcut: ' - errmsg += ', '.join(shortcut for (shortcut, _) in self.shortcuts) + errmsg = "cannot start with a shortcut: " + errmsg += ", ".join(shortcut for (shortcut, _) in self.shortcuts) return False, errmsg - errmsg = 'cannot contain: whitespace, quotes, ' + errmsg = "cannot contain: whitespace, quotes, " errchars = [] errchars.extend(constants.REDIRECTION_CHARS) errchars.extend(self.terminators) - errmsg += ', '.join([shlex.quote(x) for x in errchars]) + errmsg += ", ".join([shlex.quote(x) for x in errchars]) match = self._command_pattern.search(word) if match and word == match.group(1): valid = True - errmsg = '' + errmsg = "" return valid, errmsg def tokenize(self, line: str) -> list[str]: @@ -430,12 +430,12 @@ def parse(self, line: str) -> Statement: # handle the special case/hardcoded terminator of a blank line # we have to do this before we tokenize because tokenizing # destroys all unquoted whitespace in the input - terminator = '' + terminator = "" if line[-1:] == constants.LINE_FEED: terminator = constants.LINE_FEED - command = '' - args = '' + command = "" + args = "" # lex the input into a list of tokens tokens = self.tokenize(line) @@ -478,8 +478,8 @@ def parse(self, line: str) -> Statement: args = testargs tokens = [] - redirector = '' - redirect_to = '' + redirector = "" + redirect_to = "" # Find which redirector character appears first in the command try: @@ -506,7 +506,7 @@ def parse(self, line: str) -> Statement: utils.expand_user_in_tokens(pipe_to_tokens) # Build the pipe command line string - redirect_to = ' '.join(pipe_to_tokens) + redirect_to = " ".join(pipe_to_tokens) # remove all the tokens after the pipe tokens = tokens[:pipe_index] @@ -533,10 +533,10 @@ def parse(self, line: str) -> Statement: if terminator: # whatever is left is the suffix - suffix = ' '.join(tokens) + suffix = " ".join(tokens) else: # no terminator, so whatever is left is the command and the args - suffix = '' + suffix = "" if not command: # command could already have been set, if so, don't set it again (command, args) = self._command_and_args(tokens) @@ -581,8 +581,8 @@ def parse_command_only(self, rawinput: str) -> PartialStatement: # Expand shortcuts and aliases line = self._expand(rawinput) - command = '' - args = '' + command = "" + args = "" match = self._command_pattern.search(line) if match: @@ -638,7 +638,7 @@ def get_command_arg_list( """ # Check if to_parse needs to be converted to a Statement if not isinstance(to_parse, Statement): - to_parse = self.parse(command_name + ' ' + to_parse) + to_parse = self.parse(command_name + " " + to_parse) if preserve_quotes: return to_parse, to_parse.arg_list @@ -672,8 +672,8 @@ def _expand(self, line: str) -> str: # If the next character after the shortcut isn't a space, then insert one shortcut_len = len(shortcut) effective_expansion = expansion - if len(line) == shortcut_len or line[shortcut_len] != ' ': - effective_expansion += ' ' + if len(line) == shortcut_len or line[shortcut_len] != " ": + effective_expansion += " " # Expand the shortcut line = line.replace(shortcut, effective_expansion, 1) @@ -683,14 +683,14 @@ def _expand(self, line: str) -> str: @staticmethod def _command_and_args(tokens: list[str]) -> tuple[str, str]: """Given a list of tokens, return a tuple of the command and the args as a string.""" - command = '' - args = '' + command = "" + args = "" if tokens: command = tokens[0] if len(tokens) > 1: - args = ' '.join(tokens[1:]) + args = " ".join(tokens[1:]) return command, args @@ -721,7 +721,7 @@ def split_on_punctuation(self, tokens: list[str]) -> list[str]: cur_char = cur_initial_token[cur_index] # Keep track of the token we are building - new_token = '' + new_token = "" while True: if cur_char not in punctuation: @@ -748,7 +748,7 @@ def split_on_punctuation(self, tokens: list[str]) -> list[str]: # Save the new token punctuated_tokens.append(new_token) - new_token = '' + new_token = "" # Check if we've viewed all characters if cur_index >= len(cur_initial_token): diff --git a/cmd2/pt_utils.py b/cmd2/pt_utils.py index 54c1fd62d..8ebdb9f3e 100644 --- a/cmd2/pt_utils.py +++ b/cmd2/pt_utils.py @@ -55,7 +55,7 @@ class Cmd2Completer(Completer): def __init__( self, - cmd_app: 'Cmd', + cmd_app: "Cmd", custom_settings: utils.CustomCompletionSettings | None = None, ) -> None: """Initialize prompt_toolkit based completer class.""" @@ -196,12 +196,12 @@ class Cmd2Lexer(Lexer): def __init__( self, - cmd_app: 'Cmd', - command_color: str = 'ansigreen', - alias_color: str = 'ansicyan', - macro_color: str = 'ansimagenta', - flag_color: str = 'ansired', - argument_color: str = 'ansiyellow', + cmd_app: "Cmd", + command_color: str = "ansigreen", + alias_color: str = "ansicyan", + macro_color: str = "ansimagenta", + flag_color: str = "ansired", + argument_color: str = "ansiyellow", ) -> None: """Initialize the Lexer. @@ -234,13 +234,13 @@ def highlight_args(text: str, tokens: list[tuple[str, str]]) -> None: match_text = m.group(0) if space: - tokens.append(('', match_text)) + tokens.append(("", match_text)) elif flag: tokens.append((self.flag_color, match_text)) elif (quoted or word) and match_text not in exclude_tokens: tokens.append((self.argument_color, match_text)) else: - tokens.append(('', match_text)) + tokens.append(("", match_text)) def get_line(lineno: int) -> list[tuple[str, str]]: """Return the tokens for the given line number.""" @@ -249,7 +249,7 @@ def get_line(lineno: int) -> list[tuple[str, str]]: # No syntax highlighting if styles are disallowed if ru.ALLOW_STYLE == ru.AllowStyle.NEVER: - tokens.append(('', line)) + tokens.append(("", line)) return tokens # Only attempt to match a command on the first line @@ -264,7 +264,7 @@ def get_line(lineno: int) -> list[tuple[str, str]]: # Add any leading whitespace if cmd_start > 0: - tokens.append(('', line[:cmd_start])) + tokens.append(("", line[:cmd_start])) if command: # Determine the style for the command @@ -282,7 +282,7 @@ def get_line(lineno: int) -> list[tuple[str, str]]: break if not shortcut_found: - style = '' + style = "" if command in self.cmd_app.get_all_commands(): style = self.command_color elif command in self.cmd_app.aliases: @@ -298,7 +298,7 @@ def get_line(lineno: int) -> list[tuple[str, str]]: highlight_args(line[cmd_end:], tokens) else: # No command match found on the first line - tokens.append(('', line)) + tokens.append(("", line)) else: # All other lines are treated as arguments highlight_args(line, tokens) diff --git a/cmd2/py_bridge.py b/cmd2/py_bridge.py index 224aa06da..3eca37a73 100644 --- a/cmd2/py_bridge.py +++ b/cmd2/py_bridge.py @@ -56,8 +56,8 @@ class CommandResult(NamedTuple): not for modification. """ - stdout: str = '' - stderr: str = '' + stdout: str = "" + stderr: str = "" stop: bool = False data: Any = None @@ -79,7 +79,7 @@ class PyBridge: Defaults to True. """ - def __init__(self, cmd2_app: 'Cmd', *, add_to_history: bool = True) -> None: + def __init__(self, cmd2_app: "Cmd", *, add_to_history: bool = True) -> None: """Initialize PyBridge instances.""" self._cmd2_app = cmd2_app self._add_to_history = add_to_history @@ -91,7 +91,7 @@ def __init__(self, cmd2_app: 'Cmd', *, add_to_history: bool = True) -> None: def __dir__(self) -> list[str]: """Return a custom set of attribute names.""" attributes: list[str] = [] - attributes.insert(0, 'cmd_echo') + attributes.insert(0, "cmd_echo") return attributes def __call__(self, command: str, *, echo: bool | None = None) -> CommandResult: diff --git a/cmd2/rich_utils.py b/cmd2/rich_utils.py index 0169244d0..ce632b69c 100644 --- a/cmd2/rich_utils.py +++ b/cmd2/rich_utils.py @@ -177,7 +177,7 @@ def _format_args(self, action: argparse.Action, default_metavar: str) -> str: # Handle nargs specified as a range nargs_range = action.get_nargs_range() # type: ignore[attr-defined] if nargs_range is not None: - arg_str = '%s' % get_metavar(1) # noqa: UP031 + arg_str = "%s" % get_metavar(1) # noqa: UP031 range_str = self._build_nargs_range_str(nargs_range) return f"{arg_str}{range_str}" @@ -187,7 +187,7 @@ def _format_args(self, action: argparse.Action, default_metavar: str) -> str: # Do not customize the output when metavar is a tuple of strings. Allow argparse's # formatter to handle that instead. if not isinstance(action.metavar, tuple) and isinstance(action.nargs, int) and action.nargs > 1: - arg_str = '%s' % get_metavar(1) # noqa: UP031 + arg_str = "%s" % get_metavar(1) # noqa: UP031 return f"{arg_str}{{{action.nargs}}}" # Fallback to parent for all other cases @@ -598,12 +598,12 @@ def __init__(self, *, file: IO[str] | None = None) -> None: self._thread_local = threading.local() @property - def help_formatter(self) -> 'Cmd2HelpFormatter | None': + def help_formatter(self) -> "Cmd2HelpFormatter | None": """Return the active help formatter for this thread.""" - return getattr(self._thread_local, 'help_formatter', None) + return getattr(self._thread_local, "help_formatter", None) @help_formatter.setter - def help_formatter(self, value: 'Cmd2HelpFormatter | None') -> None: + def help_formatter(self, value: "Cmd2HelpFormatter | None") -> None: """Set the active help formatter for this thread.""" self._thread_local.help_formatter = value diff --git a/cmd2/string_utils.py b/cmd2/string_utils.py index 89ae054f3..acb4ee347 100644 --- a/cmd2/string_utils.py +++ b/cmd2/string_utils.py @@ -143,7 +143,7 @@ def quote(val: str) -> str: def quote_if_needed(val: str) -> str: """Quote a string if it contains spaces and isn't already quoted.""" - if is_quoted(val) or ' ' not in val: + if is_quoted(val) or " " not in val: return val return quote(val) diff --git a/cmd2/utils.py b/cmd2/utils.py index d612f0e74..41df14db7 100644 --- a/cmd2/utils.py +++ b/cmd2/utils.py @@ -40,7 +40,7 @@ else: PopenTextIO = subprocess.Popen -T = TypeVar('T') +T = TypeVar("T") def to_bool(val: Any) -> bool: @@ -118,7 +118,7 @@ def __init__( def get_bool_choices(_cmd2_self: CmdOrSet) -> Choices: """Tab complete lowercase boolean values.""" - return Choices.from_values(['true', 'false']) + return Choices.from_values(["true", "false"]) val_type = to_bool choices_provider = get_bool_choices @@ -149,7 +149,7 @@ def value(self, value: Any) -> None: # Make sure new_value is a valid choice if self.choices is not None and new_value not in self.choices: - choices_str = ', '.join(map(repr, self.choices)) + choices_str = ", ".join(map(repr, self.choices)) raise ValueError(f"invalid choice: {new_value!r} (choose from {choices_str})") # Try to update the settable's value @@ -173,7 +173,7 @@ def is_text_file(file_path: str) -> bool: # Only need to check for utf-8 compliance since that covers ASCII, too try: - with open(expanded_path, encoding='utf-8', errors='strict') as f: + with open(expanded_path, encoding="utf-8", errors="strict") as f: # Make sure the file has only utf-8 text and is not empty if sum(1 for _ in f) > 0: valid_text_file = True @@ -229,7 +229,7 @@ def natural_keys(input_str: str) -> list[int | str]: :param input_str: string to convert :return: list of strings and integers """ - return [try_int_or_force_to_lower_case(substr) for substr in re.split(r'(\d+)', input_str)] + return [try_int_or_force_to_lower_case(substr) for substr in re.split(r"(\d+)", input_str)] def natural_sort(list_to_sort: Iterable[str]) -> list[str]: @@ -280,7 +280,7 @@ def expand_user(token: str) -> str: quote_char = token[0] token = su.strip_quotes(token) else: - quote_char = '' + quote_char = "" token = os.path.expanduser(token) @@ -306,21 +306,21 @@ def find_editor() -> str | None: Otherwise the function will look for a known editor in directories specified by PATH env variable. :return: Default editor or None. """ - editor = os.environ.get('EDITOR') + editor = os.environ.get("EDITOR") if not editor: - if sys.platform[:3] == 'win': - editors = ['edit', 'code.cmd', 'notepad++.exe', 'notepad.exe'] + if sys.platform[:3] == "win": + editors = ["edit", "code.cmd", "notepad++.exe", "notepad.exe"] else: - editors = ['vim', 'vi', 'emacs', 'nano', 'pico', 'joe', 'code', 'subl', 'gedit', 'kate'] + editors = ["vim", "vi", "emacs", "nano", "pico", "joe", "code", "subl", "gedit", "kate"] # Get a list of every directory in the PATH environment variable and ignore symbolic links - env_path = os.getenv('PATH') + env_path = os.getenv("PATH") paths = [] if env_path is None else [p for p in env_path.split(os.path.pathsep) if not os.path.islink(p)] for possible_editor, path in itertools.product(editors, paths): editor_path = os.path.join(path, possible_editor) if os.path.isfile(editor_path) and os.access(editor_path, os.X_OK): - if sys.platform[:3] == 'win': + if sys.platform[:3] == "win": # Remove extension from Windows file names editor = os.path.splitext(possible_editor)[0] else: @@ -367,13 +367,13 @@ def get_exes_in_path(starts_with: str) -> list[str]: :return: a list of matching exe names """ # Purposely don't match any executable containing wildcards - wildcards = ['*', '?'] + wildcards = ["*", "?"] for wildcard in wildcards: if wildcard in starts_with: return [] # Get a list of every directory in the PATH environment variable and ignore symbolic links - env_path = os.getenv('PATH') + env_path = os.getenv("PATH") paths = [] if env_path is None else [p for p in env_path.split(os.path.pathsep) if not os.path.islink(p)] # Use a set to store exe names since there can be duplicates @@ -382,7 +382,7 @@ def get_exes_in_path(starts_with: str) -> list[str]: # Find every executable file in the user's path that matches the pattern for path in paths: full_path = os.path.join(path, starts_with) - matches = files_from_glob_pattern(full_path + '*', access=os.X_OK) + matches = files_from_glob_pattern(full_path + "*", access=os.X_OK) for match in matches: exes_set.add(os.path.basename(match)) @@ -398,11 +398,11 @@ class StdSim: def __init__( self, - inner_stream: Union[TextIO, 'StdSim'], + inner_stream: Union[TextIO, "StdSim"], *, echo: bool = False, - encoding: str = 'utf-8', - errors: str = 'replace', + encoding: str = "utf-8", + errors: str = "replace", ) -> None: """StdSim Initializer. @@ -424,7 +424,7 @@ def write(self, s: str) -> None: :param s: String to write to the stream """ if not isinstance(s, str): - raise TypeError(f'write() argument must be str, not {type(s)}') + raise TypeError(f"write() argument must be str, not {type(s)}") if not self.pause_storage: self.buffer.byte_buf += s.encode(encoding=self.encoding, errors=self.errors) @@ -491,7 +491,7 @@ class ByteBuf: """Used by StdSim to write binary data and stores the actual bytes written.""" # Used to know when to flush the StdSim - NEWLINES = (b'\n', b'\r') + NEWLINES = (b"\n", b"\r") def __init__(self, std_sim_instance: StdSim) -> None: """Initialize the ByteBuf instance.""" @@ -501,7 +501,7 @@ def __init__(self, std_sim_instance: StdSim) -> None: def write(self, b: bytes) -> None: """Add bytes to internal bytes buffer and if echo is True, echo contents to inner stream.""" if not isinstance(b, bytes): - raise TypeError(f'a bytes-like object is required, not {type(b)}') + raise TypeError(f"a bytes-like object is required, not {type(b)}") if not self.std_sim_instance.pause_storage: self.byte_buf += b if self.std_sim_instance.echo: @@ -532,9 +532,9 @@ def __init__(self, proc: PopenTextIO, stdout: StdSim | TextIO, stderr: StdSim | self._stdout = stdout self._stderr = stderr - self._out_thread = threading.Thread(name='out_thread', target=self._reader_thread_func, kwargs={'read_stdout': True}) + self._out_thread = threading.Thread(name="out_thread", target=self._reader_thread_func, kwargs={"read_stdout": True}) - self._err_thread = threading.Thread(name='err_thread', target=self._reader_thread_func, kwargs={'read_stdout': False}) + self._err_thread = threading.Thread(name="err_thread", target=self._reader_thread_func, kwargs={"read_stdout": False}) # Start the reader threads for pipes only if self._proc.stdout is not None: @@ -546,7 +546,7 @@ def send_sigint(self) -> None: """Send a SIGINT to the process similar to if +C were pressed.""" import signal - if sys.platform.startswith('win'): + if sys.platform.startswith("win"): # cmd2 started the Windows process in a new process group. Therefore we must send # a CTRL_BREAK_EVENT since CTRL_C_EVENT signals cannot be generated for process groups. self._proc.send_signal(signal.CTRL_BREAK_EVENT) @@ -719,23 +719,23 @@ def get_defining_class(meth: Callable[..., Any]) -> type[Any] | None: if isinstance(meth, functools.partial): return get_defining_class(meth.func) if inspect.ismethod(meth) or ( - inspect.isbuiltin(meth) and hasattr(meth, '__self__') and hasattr(meth.__self__, '__class__') + inspect.isbuiltin(meth) and hasattr(meth, "__self__") and hasattr(meth.__self__, "__class__") ): for cls in inspect.getmro(meth.__self__.__class__): if meth.__name__ in cls.__dict__: return cls - meth = getattr(meth, '__func__', meth) # fallback to __qualname__ parsing + meth = getattr(meth, "__func__", meth) # fallback to __qualname__ parsing if inspect.isfunction(meth): - cls = getattr(inspect.getmodule(meth), meth.__qualname__.split('.', 1)[0].rsplit('.', 1)[0]) + cls = getattr(inspect.getmodule(meth), meth.__qualname__.split(".", 1)[0].rsplit(".", 1)[0]) if isinstance(cls, type): return cls - return cast(type, getattr(meth, '__objclass__', None)) # handle special descriptor objects + return cast(type, getattr(meth, "__objclass__", None)) # handle special descriptor objects class CustomCompletionSettings: """Used by cmd2.Cmd.complete() to complete strings other than command arguments.""" - def __init__(self, parser: 'Cmd2ArgumentParser', *, preserve_quotes: bool = False) -> None: + def __init__(self, parser: "Cmd2ArgumentParser", *, preserve_quotes: bool = False) -> None: """CustomCompletionSettings initializer. :param parser: arg parser defining format of string being completed @@ -755,13 +755,13 @@ def strip_doc_annotations(doc: str) -> str: :param doc: documentation string """ # Attempt to locate the first documentation block - cmd_desc = '' + cmd_desc = "" found_first = False for doc_line in doc.splitlines(): stripped_line = doc_line.strip() # Don't include :param type lines - if stripped_line.startswith(':'): + if stripped_line.startswith(":"): if found_first: break elif stripped_line: @@ -822,9 +822,9 @@ def get_types(func_or_method: Callable[..., Any]) -> tuple[dict[str, Any], Any]: type_hints = inspect.get_annotations(func_or_method, eval_str=True) # Get dictionary of type hints except TypeError as exc: raise ValueError("Argument passed to get_types should be a function or method") from exc - ret_ann = type_hints.pop('return', None) # Pop off the return annotation if it exists + ret_ann = type_hints.pop("return", None) # Pop off the return annotation if it exists if inspect.ismethod(func_or_method): - type_hints.pop('self', None) # Pop off `self` hint for methods + type_hints.pop("self", None) # Pop off `self` hint for methods return type_hints, ret_ann diff --git a/examples/argparse_completion.py b/examples/argparse_completion.py index b6d3e40b7..aa4b24677 100755 --- a/examples/argparse_completion.py +++ b/examples/argparse_completion.py @@ -20,13 +20,13 @@ ) # Data source for argparse.choices -food_item_strs = ['Pizza', 'Ham', 'Ham Sandwich', 'Potato'] +food_item_strs = ["Pizza", "Ham", "Ham Sandwich", "Potato"] class ArgparseCompletion(Cmd): def __init__(self) -> None: super().__init__(include_ipy=True) - self.sport_item_strs = ['Bat', 'Basket', 'Basketball', 'Football', 'Space Ball'] + self.sport_item_strs = ["Bat", "Basket", "Basketball", "Football", "Space Ball"] def choices_provider(self) -> Choices: """A choices provider is useful when the choice list is based on instance data of your application.""" @@ -77,11 +77,11 @@ def choices_arg_tokens(self, arg_tokens: dict[str, list[str]]) -> Choices: a particular argument expects only 1 token. """ # Check if choices_provider flag has appeared - values = ['choices_provider', 'flag'] - if 'choices_provider' in arg_tokens: - values.append('is {}'.format(arg_tokens['choices_provider'][0])) + values = ["choices_provider", "flag"] + if "choices_provider" in arg_tokens: + values.append("is {}".format(arg_tokens["choices_provider"][0])) else: - values.append('not supplied') + values.append("not supplied") return Choices.from_values(values) # Parser for example command @@ -91,26 +91,26 @@ def choices_arg_tokens(self, arg_tokens: dict[str, list[str]]) -> Choices: # Tab complete from a list using argparse choices. Set metavar if you don't # want the entire choices list showing in the usage text for this command. - example_parser.add_argument('--choices', choices=food_item_strs, metavar="CHOICE", 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" + "--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', + "--completion_error", choices_provider=choices_completion_error, help="raise a CompletionError while tab completing if debug is False", ) # Demonstrate use of completion table example_parser.add_argument( - '--completion_table', + "--completion_table", choices_provider=choices_completion_tables, metavar="ITEM_ID", table_columns=["Description"], @@ -119,7 +119,7 @@ def choices_arg_tokens(self, arg_tokens: dict[str, list[str]]) -> Choices: # Demonstrate use of arg_tokens dictionary example_parser.add_argument( - '--arg_tokens', choices_provider=choices_arg_tokens, help="demonstrate use of arg_tokens dictionary" + "--arg_tokens", choices_provider=choices_arg_tokens, help="demonstrate use of arg_tokens dictionary" ) @with_argparser(example_parser) @@ -128,7 +128,7 @@ def do_example(self, _: argparse.Namespace) -> None: self.poutput("I do nothing") -if __name__ == '__main__': +if __name__ == "__main__": import sys app = ArgparseCompletion() diff --git a/examples/argparse_example.py b/examples/argparse_example.py index 5db31d035..40c9c66d9 100755 --- a/examples/argparse_example.py +++ b/examples/argparse_example.py @@ -21,9 +21,9 @@ from cmd2.string_utils import stylize # Command categories -ARGPARSE_USAGE = 'Argparse Basic Usage' -ARGPARSE_PRINTING = 'Argparse Printing' -ARGPARSE_SUBCOMMANDS = 'Argparse Subcommands' +ARGPARSE_USAGE = "Argparse Basic Usage" +ARGPARSE_PRINTING = "Argparse Printing" +ARGPARSE_SUBCOMMANDS = "Argparse Subcommands" class ArgparsingApp(cmd2.Cmd): @@ -31,16 +31,16 @@ def __init__(self, color: str) -> None: """Cmd2 application for demonstrating the use of argparse for command argument parsing.""" super().__init__(include_ipy=True) self.intro = stylize( - 'cmd2 has awesome decorators to make it easy to use Argparse to parse command arguments', style=color + "cmd2 has awesome decorators to make it easy to use Argparse to parse command arguments", style=color ) ## ------ Basic examples of using argparse for command argument parsing ----- # do_fsize parser - 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 = 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) @cmd2.with_argparser(fsize_parser) @cmd2.with_category(ARGPARSE_USAGE) @@ -54,21 +54,21 @@ def do_fsize(self, args: argparse.Namespace) -> None: self.perror(f"Error retrieving size: {ex}") return - if args.unit == 'KB': + if args.unit == "KB": size //= 1024 - elif args.unit == 'MB': + elif args.unit == "MB": size //= 1024 * 1024 else: - args.unit = 'bytes' + args.unit = "bytes" size = round(size, 2) - size_str = f'{size:,}' if args.comma else f'{size}' - self.poutput(f'{size_str} {args.unit}') + size_str = f"{size:,}" if args.comma else f"{size}" + self.poutput(f"{size_str} {args.unit}") # do_pow parser pow_parser = cmd2.Cmd2ArgumentParser() - pow_parser.add_argument('base', type=int) - pow_parser.add_argument('exponent', type=int, choices=range(-5, 6)) + pow_parser.add_argument("base", type=int) + pow_parser.add_argument("exponent", type=int, choices=range(-5, 6)) @cmd2.with_argparser(pow_parser) @cmd2.with_category(ARGPARSE_USAGE) @@ -77,59 +77,59 @@ def do_pow(self, args: argparse.Namespace) -> None: :param args: argparse arguments """ - self.poutput(f'{args.base} ** {args.exponent} == {args.base**args.exponent}') + self.poutput(f"{args.base} ** {args.exponent} == {args.base**args.exponent}") ## ------ Examples displaying how argparse arguments are passed to commands by printing them out ----- argprint_parser = cmd2.Cmd2ArgumentParser() - argprint_parser.add_argument('-p', '--piglatin', action='store_true', help='atinLay') - argprint_parser.add_argument('-s', '--shout', action='store_true', help='N00B EMULATION MODE') - argprint_parser.add_argument('-r', '--repeat', type=int, help='output [n] times') - argprint_parser.add_argument('words', nargs='+', help='words to print') + argprint_parser.add_argument("-p", "--piglatin", action="store_true", help="atinLay") + argprint_parser.add_argument("-s", "--shout", action="store_true", help="N00B EMULATION MODE") + argprint_parser.add_argument("-r", "--repeat", type=int, help="output [n] times") + argprint_parser.add_argument("words", nargs="+", help="words to print") @cmd2.with_argparser(argprint_parser) @cmd2.with_category(ARGPARSE_PRINTING) def do_print_args(self, args: argparse.Namespace) -> None: """Print the arpgarse argument list this command was called with.""" - self.poutput(f'print_args was called with the following\n\targuments: {args!r}') + self.poutput(f"print_args was called with the following\n\targuments: {args!r}") unknownprint_parser = cmd2.Cmd2ArgumentParser() - unknownprint_parser.add_argument('-p', '--piglatin', action='store_true', help='atinLay') - unknownprint_parser.add_argument('-s', '--shout', action='store_true', help='N00B EMULATION MODE') - unknownprint_parser.add_argument('-r', '--repeat', type=int, help='output [n] times') + unknownprint_parser.add_argument("-p", "--piglatin", action="store_true", help="atinLay") + unknownprint_parser.add_argument("-s", "--shout", action="store_true", help="N00B EMULATION MODE") + unknownprint_parser.add_argument("-r", "--repeat", type=int, help="output [n] times") @cmd2.with_argparser(unknownprint_parser, with_unknown_args=True) @cmd2.with_category(ARGPARSE_PRINTING) def do_print_unknown(self, args: argparse.Namespace, unknown: list[str]) -> None: """Print the arpgarse argument list this command was called with, including unknown arguments.""" - self.poutput(f'print_unknown was called with the following arguments\n\tknown: {args!r}\n\tunknown: {unknown}') + self.poutput(f"print_unknown was called with the following arguments\n\tknown: {args!r}\n\tunknown: {unknown}") ## ------ Examples demonstrating how to use argparse subcommands ----- # create the top-level parser for the base command calculate_parser = cmd2.Cmd2ArgumentParser(description="Perform simple mathematical calculations.") - calculate_subparsers = calculate_parser.add_subparsers(title='operation', help='Available operations', required=True) + calculate_subparsers = calculate_parser.add_subparsers(title="operation", help="Available operations", required=True) # create the parser for the "add" subcommand add_description = "Add two numbers" add_parser = cmd2.Cmd2ArgumentParser("add", description=add_description) - add_parser.add_argument('num1', type=int, help='The first number') - add_parser.add_argument('num2', type=int, help='The second number') + add_parser.add_argument("num1", type=int, help="The first number") + add_parser.add_argument("num2", type=int, help="The second number") # create the parser for the "add" subcommand subtract_description = "Subtract two numbers" subtract_parser = cmd2.Cmd2ArgumentParser("subtract", description=subtract_description) - subtract_parser.add_argument('num1', type=int, help='The first number') - subtract_parser.add_argument('num2', type=int, help='The second number') + subtract_parser.add_argument("num1", type=int, help="The first number") + subtract_parser.add_argument("num2", type=int, help="The second number") # subcommand functions for the calculate command - @cmd2.as_subcommand_to('calculate', 'add', add_parser, help=add_description.lower()) + @cmd2.as_subcommand_to("calculate", "add", add_parser, help=add_description.lower()) def add(self, args: argparse.Namespace) -> None: """add subcommand of calculate command.""" result = args.num1 + args.num2 self.poutput(f"{args.num1} + {args.num2} = {result}") - @cmd2.as_subcommand_to('calculate', 'subtract', subtract_parser, help=subtract_description.lower()) + @cmd2.as_subcommand_to("calculate", "subtract", subtract_parser, help=subtract_description.lower()) def subtract(self, args: argparse.Namespace) -> None: """subtract subcommand of calculate command.""" result = args.num1 - args.num2 @@ -142,20 +142,20 @@ def do_calculate(self, args: argparse.Namespace) -> None: args.cmd2_subcmd_handler(args) -if __name__ == '__main__': +if __name__ == "__main__": import sys from cmd2.colors import Color # You can do your custom Argparse parsing here to meet your application's needs - parser = cmd2.Cmd2ArgumentParser(description='Process the arguments however you like.') + parser = cmd2.Cmd2ArgumentParser(description="Process the arguments however you like.") # Add an argument which we will pass to the app to change some behavior parser.add_argument( - '-c', - '--color', + "-c", + "--color", choices=[Color.RED, Color.ORANGE1, Color.YELLOW, Color.GREEN, Color.BLUE, Color.PURPLE, Color.VIOLET, Color.WHITE], - help='Color of intro text', + help="Color of intro text", ) # Parse the arguments diff --git a/examples/async_call.py b/examples/async_call.py index f014fe97d..a3dc8bae8 100755 --- a/examples/async_call.py +++ b/examples/async_call.py @@ -22,7 +22,7 @@ def run_async(coro) -> concurrent.futures.Future: _event_loop = asyncio.new_event_loop() thread = threading.Thread( target=_event_loop.run_forever, - name='Async Runner', + name="Async Runner", daemon=True, ) thread.start() @@ -49,10 +49,10 @@ def do_async_wait(self, _: str) -> None: """ waitable = run_async(async_wait(0.1)) - self.poutput('Begin waiting...') + self.poutput("Begin waiting...") # Wait for coroutine to complete and get its return value: res = waitable.result() - self.poutput(f'Done waiting: {res}') + self.poutput(f"Done waiting: {res}") return def do_hello_world(self, _: str) -> None: @@ -60,7 +60,7 @@ def do_hello_world(self, _: str) -> None: Just a typical (synchronous) cmd2 command """ - self.poutput('Hello World') + self.poutput("Hello World") def main() -> int: @@ -70,7 +70,7 @@ def main() -> int: return app.cmdloop() -if __name__ == '__main__': +if __name__ == "__main__": import sys sys.exit(main()) diff --git a/examples/async_commands.py b/examples/async_commands.py index aa1b2bab6..5e18214de 100755 --- a/examples/async_commands.py +++ b/examples/async_commands.py @@ -36,7 +36,7 @@ def _get_event_loop() -> asyncio.AbstractEventLoop: _event_loop = asyncio.new_event_loop() thread = threading.Thread( target=_event_loop.run_forever, - name='Async Runner', + name="Async Runner", daemon=True, ) thread.start() @@ -83,7 +83,7 @@ def __init__(self) -> None: self.main_session.key_bindings = KeyBindings() # Add a custom key binding for +T that calls a method so it has access to self - @self.main_session.key_bindings.add('c-t') + @self.main_session.key_bindings.add("c-t") def _(_event: Any) -> None: self.handle_control_t(_event) @@ -115,7 +115,7 @@ def handle_control_t(self, _event) -> None: Prints 'fnord' above the prompt in a random color and random position. """ - word = 'fnord' + word = "fnord" # Generate a random RGB color tuple r = random.randint(0, 255) @@ -126,17 +126,17 @@ def handle_control_t(self, _event) -> None: cols, _ = shutil.get_terminal_size() extra_width = cols - len(word) - 1 padding_size = random.randint(0, extra_width) - padding = ' ' * padding_size + padding = " " * padding_size # Use rich to generate the the overall text to print out text = Text() text.append(padding) - text.append(word, style=f'rgb({r},{g},{b})') + text.append(word, style=f"rgb({r},{g},{b})") print_formatted_text(ANSI(cmd2.rich_utils.rich_text_to_string(text))) -if __name__ == '__main__': +if __name__ == "__main__": import sys app = AsyncCommandsApp() diff --git a/examples/async_printing.py b/examples/async_printing.py index da99fab14..13c58b126 100755 --- a/examples/async_printing.py +++ b/examples/async_printing.py @@ -49,7 +49,7 @@ def __init__(self) -> None: def _preloop_hook(self) -> None: """Start the alerter thread.""" self._stop_event.clear() - self._add_alert_thread = threading.Thread(name='alerter', target=self._add_alerts_func) + self._add_alert_thread = threading.Thread(name="alerter", target=self._add_alerts_func) self._add_alert_thread.start() def _postloop_hook(self) -> None: @@ -64,7 +64,7 @@ def do_start_alerts(self, _: cmd2.Statement) -> None: print("The alert thread is already started") else: self._stop_event.clear() - self._add_alert_thread = threading.Thread(name='alerter', target=self._add_alerts_func) + self._add_alert_thread = threading.Thread(name="alerter", target=self._add_alerts_func) self._add_alert_thread.start() def do_stop_alerts(self, _: cmd2.Statement) -> None: @@ -107,7 +107,7 @@ def _build_alert_str(self) -> str: """Combines alerts into one string that can be printed to the terminal :return: the alert string. """ - alert_str = '' + alert_str = "" alerts = self._get_alerts() longest_alert = max(ALERTS, key=len) @@ -115,13 +115,13 @@ def _build_alert_str(self) -> str: for i, cur_alert in enumerate(alerts): # Use padding to center the alert - padding = ' ' * int((num_asterisks - len(cur_alert)) / 2) + padding = " " * int((num_asterisks - len(cur_alert)) / 2) if i > 0: - alert_str += '\n' - alert_str += '*' * num_asterisks + '\n' - alert_str += padding + cur_alert + padding + '\n' - alert_str += '*' * num_asterisks + '\n' + alert_str += "\n" + alert_str += "*" * num_asterisks + "\n" + alert_str += padding + cur_alert + padding + "\n" + alert_str += "*" * num_asterisks + "\n" return alert_str @@ -171,7 +171,7 @@ def _add_alerts_func(self) -> None: self._stop_event.wait(0.5) -if __name__ == '__main__': +if __name__ == "__main__": import sys app = AlerterApp() diff --git a/examples/basic_completion.py b/examples/basic_completion.py index b41e2732d..96cb663cf 100755 --- a/examples/basic_completion.py +++ b/examples/basic_completion.py @@ -16,11 +16,11 @@ # This data is used to demonstrate delimiter_complete file_strs = [ - '/home/user/file.db', - '/home/user/file space.db', - '/home/user/another.db', - '/home/other user/maps.db', - '/home/other user/tests.db', + "/home/user/file.db", + "/home/user/file space.db", + "/home/user/another.db", + "/home/other user/maps.db", + "/home/other user/tests.db", ] @@ -33,7 +33,7 @@ def do_delimiter_complete(self, statement: cmd2.Statement) -> None: self.poutput(f"Args: {statement.args}") # Use a partialmethod to set arguments to delimiter_complete - complete_delimiter_complete = functools.partialmethod(cmd2.Cmd.delimiter_complete, match_against=file_strs, delimiter='/') + complete_delimiter_complete = functools.partialmethod(cmd2.Cmd.delimiter_complete, match_against=file_strs, delimiter="/") def do_raise_error(self, statement: cmd2.Statement) -> None: """Demonstrates effect of raising CompletionError.""" @@ -49,7 +49,7 @@ def complete_raise_error(self, _text: str, _line: str, _begidx: int, _endidx: in raise cmd2.CompletionError("This is how a CompletionError behaves") -if __name__ == '__main__': +if __name__ == "__main__": import sys app = BasicCompletion() diff --git a/examples/cmd_as_argument.py b/examples/cmd_as_argument.py index a9e24f25f..e3552b877 100755 --- a/examples/cmd_as_argument.py +++ b/examples/cmd_as_argument.py @@ -16,26 +16,26 @@ class CmdLineApp(cmd2.Cmd): """Example cmd2 application.""" - MUMBLES = ('like', '...', 'um', 'er', 'hmmm', 'ahh') - MUMBLE_FIRST = ('so', 'like', 'well') - MUMBLE_LAST = ('right?',) + MUMBLES = ("like", "...", "um", "er", "hmmm", "ahh") + MUMBLE_FIRST = ("so", "like", "well") + MUMBLE_LAST = ("right?",) def __init__(self) -> None: shortcuts = dict(cmd2.DEFAULT_SHORTCUTS) - shortcuts.update({'&': 'speak'}) + shortcuts.update({"&": "speak"}) # Set include_ipy to True to enable the "ipy" command which runs an interactive IPython shell - super().__init__(allow_cli_args=False, include_ipy=True, multiline_commands=['orate'], shortcuts=shortcuts) + super().__init__(allow_cli_args=False, include_ipy=True, multiline_commands=["orate"], shortcuts=shortcuts) self.self_in_py = True self.maxrepeats = 3 # Make maxrepeats settable at runtime - self.add_settable(cmd2.Settable('maxrepeats', int, 'max repetitions for speak command', self)) + self.add_settable(cmd2.Settable("maxrepeats", int, "max repetitions for speak command", self)) speak_parser = cmd2.Cmd2ArgumentParser() - speak_parser.add_argument('-p', '--piglatin', action='store_true', help='atinLay') - speak_parser.add_argument('-s', '--shout', action='store_true', help='N00B EMULATION MODE') - speak_parser.add_argument('-r', '--repeat', type=int, help='output [n] times') - speak_parser.add_argument('words', nargs='+', help='words to say') + speak_parser.add_argument("-p", "--piglatin", action="store_true", help="atinLay") + speak_parser.add_argument("-s", "--shout", action="store_true", help="N00B EMULATION MODE") + speak_parser.add_argument("-r", "--repeat", type=int, help="output [n] times") + speak_parser.add_argument("words", nargs="+", help="words to say") @cmd2.with_argparser(speak_parser) def do_speak(self, args) -> None: @@ -43,21 +43,21 @@ def do_speak(self, args) -> None: words = [] for word in args.words: if args.piglatin: - word = f'{word[1:]}{word[0]}ay' + word = f"{word[1:]}{word[0]}ay" if args.shout: word = word.upper() words.append(word) repetitions = args.repeat or 1 for _ in range(min(repetitions, self.maxrepeats)): # .poutput handles newlines, and accommodates output redirection too - self.poutput(' '.join(words)) + self.poutput(" ".join(words)) do_say = do_speak # now "say" is a synonym for "speak" do_orate = do_speak # another synonym, but this one takes multi-line input mumble_parser = cmd2.Cmd2ArgumentParser() - mumble_parser.add_argument('-r', '--repeat', type=int, help='how many times to repeat') - mumble_parser.add_argument('words', nargs='+', help='words to say') + mumble_parser.add_argument("-r", "--repeat", type=int, help="how many times to repeat") + mumble_parser.add_argument("words", nargs="+", help="words to say") @cmd2.with_argparser(mumble_parser) def do_mumble(self, args) -> None: @@ -73,16 +73,16 @@ def do_mumble(self, args) -> None: output.append(word) if random.random() < 0.25: output.append(random.choice(self.MUMBLE_LAST)) - self.poutput(' '.join(output)) + self.poutput(" ".join(output)) def main(argv=None): """Run when invoked from the operating system shell.""" - parser = cmd2.Cmd2ArgumentParser(description='Commands as arguments') - command_help = 'optional command to run, if no command given, enter an interactive shell' - parser.add_argument('command', nargs='?', help=command_help) - arg_help = 'optional arguments for command' - parser.add_argument('command_args', nargs=argparse.REMAINDER, help=arg_help) + parser = cmd2.Cmd2ArgumentParser(description="Commands as arguments") + command_help = "optional command to run, if no command given, enter an interactive shell" + parser.add_argument("command", nargs="?", help=command_help) + arg_help = "optional arguments for command" + parser.add_argument("command_args", nargs=argparse.REMAINDER, help=arg_help) args = parser.parse_args(argv) @@ -91,7 +91,7 @@ def main(argv=None): sys_exit_code = 0 if args.command: # we have a command, run it and then exit - c.onecmd_plus_hooks('{} {}'.format(args.command, ' '.join(args.command_args))) + c.onecmd_plus_hooks("{} {}".format(args.command, " ".join(args.command_args))) else: # we have no command, drop into interactive mode sys_exit_code = c.cmdloop() @@ -99,7 +99,7 @@ def main(argv=None): return sys_exit_code -if __name__ == '__main__': +if __name__ == "__main__": import sys sys.exit(main()) diff --git a/examples/color.py b/examples/color.py index e6e2cf26b..3440f3fce 100755 --- a/examples/color.py +++ b/examples/color.py @@ -19,11 +19,11 @@ class CmdLineApp(cmd2.Cmd): def __init__(self) -> None: # Set include_ipy to True to enable the "ipy" command which runs an interactive IPython shell super().__init__(include_ipy=True) - self.intro = 'Run the taste_the_rainbow command to see all of the colors available to you in cmd2.' + self.intro = "Run the taste_the_rainbow command to see all of the colors available to you in cmd2." rainbow_parser = cmd2.Cmd2ArgumentParser() - rainbow_parser.add_argument('-b', '--background', action='store_true', help='show background colors as well') - rainbow_parser.add_argument('-p', '--paged', action='store_true', help='display output using a pager') + rainbow_parser.add_argument("-b", "--background", action="store_true", help="show background colors as well") + rainbow_parser.add_argument("-p", "--paged", action="store_true", help="display output using a pager") @cmd2.with_argparser(rainbow_parser) def do_taste_the_rainbow(self, args: argparse.Namespace) -> None: @@ -44,7 +44,7 @@ def create_style(color: Color) -> Style: self.poutput(output) -if __name__ == '__main__': +if __name__ == "__main__": import sys c = CmdLineApp() diff --git a/examples/command_sets.py b/examples/command_sets.py index c27204fd8..f8dacf270 100755 --- a/examples/command_sets.py +++ b/examples/command_sets.py @@ -37,11 +37,11 @@ def __init__(self) -> None: def do_hello(self, _: cmd2.Statement) -> None: """Print hello.""" - self._cmd.poutput('Hello') + self._cmd.poutput("Hello") def do_world(self, _: cmd2.Statement) -> None: """Print World.""" - self._cmd.poutput('World') + self._cmd.poutput("World") class LoadableFruits(CommandSet[cmd2.Cmd]): @@ -53,20 +53,20 @@ def __init__(self) -> None: def do_apple(self, _: cmd2.Statement) -> None: """Print Apple.""" - self._cmd.poutput('Apple') + self._cmd.poutput("Apple") def do_banana(self, _: cmd2.Statement) -> None: """Print Banana.""" - self._cmd.poutput('Banana') + self._cmd.poutput("Banana") banana_description = "Cut a banana" banana_parser = cmd2.Cmd2ArgumentParser(description=banana_description) - banana_parser.add_argument('direction', choices=['discs', 'lengthwise']) + banana_parser.add_argument("direction", choices=["discs", "lengthwise"]) - @cmd2.as_subcommand_to('cut', 'banana', banana_parser, help=banana_description.lower()) + @cmd2.as_subcommand_to("cut", "banana", banana_parser, help=banana_description.lower()) def cut_banana(self, ns: argparse.Namespace) -> None: """Cut banana.""" - self._cmd.poutput('cutting banana: ' + ns.direction) + self._cmd.poutput("cutting banana: " + ns.direction) class LoadableVegetables(CommandSet[cmd2.Cmd]): @@ -78,20 +78,20 @@ def __init__(self) -> None: def do_arugula(self, _: cmd2.Statement) -> None: "Print Arguula." - self._cmd.poutput('Arugula') + self._cmd.poutput("Arugula") def do_bokchoy(self, _: cmd2.Statement) -> None: """Print Bok Choy.""" - self._cmd.poutput('Bok Choy') + self._cmd.poutput("Bok Choy") bokchoy_description = "Cut some bokchoy" bokchoy_parser = cmd2.Cmd2ArgumentParser(description=bokchoy_description) - bokchoy_parser.add_argument('style', choices=['quartered', 'diced']) + bokchoy_parser.add_argument("style", choices=["quartered", "diced"]) - @cmd2.as_subcommand_to('cut', 'bokchoy', bokchoy_parser, help=bokchoy_description.lower()) + @cmd2.as_subcommand_to("cut", "bokchoy", bokchoy_parser, help=bokchoy_description.lower()) def cut_bokchoy(self, ns: argparse.Namespace) -> None: """Cut bokchoy.""" - self._cmd.poutput('Bok Choy: ' + ns.style) + self._cmd.poutput("Bok Choy: " + ns.style) class CommandSetApp(cmd2.Cmd): @@ -108,43 +108,43 @@ def __init__(self) -> None: self._fruits = LoadableFruits() self._vegetables = LoadableVegetables() - self.intro = 'The CommandSet feature allows defining commands in multiple files and the dynamic load/unload at runtime' + self.intro = "The CommandSet feature allows defining commands in multiple files and the dynamic load/unload at runtime" load_parser = cmd2.Cmd2ArgumentParser() - load_parser.add_argument('cmds', choices=['fruits', 'vegetables']) + load_parser.add_argument("cmds", choices=["fruits", "vegetables"]) @with_argparser(load_parser) @with_category(COMMANDSET_LOAD_UNLOAD) def do_load(self, ns: argparse.Namespace) -> None: """Load a CommandSet at runtime.""" - if ns.cmds == 'fruits': + if ns.cmds == "fruits": try: self.register_command_set(self._fruits) - self.poutput('Fruits loaded') + self.poutput("Fruits loaded") except ValueError: - self.poutput('Fruits already loaded') + self.poutput("Fruits already loaded") - if ns.cmds == 'vegetables': + if ns.cmds == "vegetables": try: self.register_command_set(self._vegetables) - self.poutput('Vegetables loaded') + self.poutput("Vegetables loaded") except ValueError: - self.poutput('Vegetables already loaded') + self.poutput("Vegetables already loaded") @with_argparser(load_parser) @with_category(COMMANDSET_LOAD_UNLOAD) def do_unload(self, ns: argparse.Namespace) -> None: """Unload a CommandSet at runtime.""" - if ns.cmds == 'fruits': + if ns.cmds == "fruits": self.unregister_command_set(self._fruits) - self.poutput('Fruits unloaded') + self.poutput("Fruits unloaded") - if ns.cmds == 'vegetables': + if ns.cmds == "vegetables": self.unregister_command_set(self._vegetables) - self.poutput('Vegetables unloaded') + self.poutput("Vegetables unloaded") cut_parser = cmd2.Cmd2ArgumentParser() - cut_subparsers = cut_parser.add_subparsers(title='item', help='item to cut') + cut_subparsers = cut_parser.add_subparsers(title="item", help="item to cut") @with_argparser(cut_parser) @with_category(COMMANDSET_SUBCOMMAND) @@ -155,10 +155,10 @@ def do_cut(self, ns: argparse.Namespace) -> None: handler(ns) else: # No subcommand was provided, so call help - self.poutput('This command does nothing without sub-parsers registered') - self.do_help('cut') + self.poutput("This command does nothing without sub-parsers registered") + self.do_help("cut") -if __name__ == '__main__': +if __name__ == "__main__": app = CommandSetApp() app.cmdloop() diff --git a/examples/custom_parser.py b/examples/custom_parser.py index 70a279e8a..516ce5201 100644 --- a/examples/custom_parser.py +++ b/examples/custom_parser.py @@ -25,13 +25,13 @@ def __init__(self, *args, **kwargs) -> None: # type: ignore[no-untyped-def] def error(self, message: str) -> NoReturn: """Custom override that applies custom formatting to the error message.""" - lines = message.split('\n') - formatted_message = '' + lines = message.split("\n") + formatted_message = "" for linum, line in enumerate(lines): if linum == 0: - formatted_message = 'Error: ' + line + formatted_message = "Error: " + line else: - formatted_message += '\n ' + line + formatted_message += "\n " + line self.print_usage(sys.stderr) @@ -40,16 +40,16 @@ def error(self, message: str) -> NoReturn: formatted_message, style=styles.WARNING, ) - self.exit(2, f'{formatted_message}\n\n') + self.exit(2, f"{formatted_message}\n\n") -if __name__ == '__main__': +if __name__ == "__main__": import sys # Set the default parser type before instantiating app. set_default_argument_parser_type(CustomParser) - app = cmd2.Cmd(include_ipy=True, persistent_history_file='cmd2_history.dat') + app = cmd2.Cmd(include_ipy=True, persistent_history_file="cmd2_history.dat") app.self_in_py = True # Enable access to "self" within the py command app.debug = True # Show traceback if/when an exception occurs sys.exit(app.cmdloop()) diff --git a/examples/custom_types.py b/examples/custom_types.py index 39bfeecfa..b000e91c0 100644 --- a/examples/custom_types.py +++ b/examples/custom_types.py @@ -112,13 +112,13 @@ def __repr__(self) -> str: def __call__(self, arg: str) -> Iterable[int]: """Parse a string into an iterable returning ints.""" - if arg == 'all': + if arg == "all": return range(self.bottom, self.top) out = [] - for piece in arg.split(','): - if '-' in piece: - a, b = [int(x) for x in piece.split('-', 2)] + for piece in arg.split(","): + if "-" in piece: + a, b = [int(x) for x in piece.split("-", 2)] if a < self.bottom: raise ValueError(f"Value '{a}' not within {self.range_str}") if b >= self.top: @@ -132,19 +132,19 @@ def __call__(self, arg: str) -> Iterable[int]: return out -if __name__ == '__main__': +if __name__ == "__main__": import argparse import sys class CustomTypesExample(cmd2.Cmd): example_parser = cmd2.Cmd2ArgumentParser() example_parser.add_argument( - '--value', '-v', type=integer, help='Integer value, with optional K/M/G/Ki/Mi/Gi/... suffix' + "--value", "-v", type=integer, help="Integer value, with optional K/M/G/Ki/Mi/Gi/... suffix" ) - example_parser.add_argument('--memory-address', '-m', type=hexadecimal, help='Memory address in hex') - example_parser.add_argument('--year', type=Range(1900, 2000), help='Year between 1900-1999') + example_parser.add_argument("--memory-address", "-m", type=hexadecimal, help="Memory address in hex") + example_parser.add_argument("--year", type=Range(1900, 2000), help="Year between 1900-1999") example_parser.add_argument( - '--index', dest='index_list', type=IntSet(100), help='One or more indexes 0-99. e.g. "1,3,5", "10,30-50", "all"' + "--index", dest="index_list", type=IntSet(100), help='One or more indexes 0-99. e.g. "1,3,5", "10,30-50", "all"' ) @cmd2.with_argparser(example_parser) diff --git a/examples/default_categories.py b/examples/default_categories.py index df7ff724c..05f92e28a 100755 --- a/examples/default_categories.py +++ b/examples/default_categories.py @@ -60,7 +60,7 @@ def do_shortcuts(self, _: argparse.Namespace) -> None: super().do_shortcuts("") -if __name__ == '__main__': +if __name__ == "__main__": import sys app = CategoryApp() diff --git a/examples/dynamic_commands.py b/examples/dynamic_commands.py index 137f30c7f..14d005c03 100755 --- a/examples/dynamic_commands.py +++ b/examples/dynamic_commands.py @@ -9,8 +9,8 @@ HELP_FUNC_PREFIX, ) -COMMAND_LIST = ['foo', 'bar'] -CATEGORY = 'Dynamic Commands' +COMMAND_LIST = ["foo", "bar"] +CATEGORY = "Dynamic Commands" class CommandsInLoop(cmd2.Cmd): @@ -43,6 +43,6 @@ def text_help(self, *, text: str) -> None: self.poutput(f"Simulate sending {text!r} to a server and printing the response") -if __name__ == '__main__': +if __name__ == "__main__": app = CommandsInLoop() app.cmdloop() diff --git a/examples/environment.py b/examples/environment.py index 706f150f4..241425019 100755 --- a/examples/environment.py +++ b/examples/environment.py @@ -12,18 +12,18 @@ def __init__(self) -> None: self.degrees_c = 22 self.sunny = False self.add_settable( - cmd2.Settable('degrees_c', int, 'Temperature in Celsius', self, onchange_cb=self._onchange_degrees_c) + cmd2.Settable("degrees_c", int, "Temperature in Celsius", self, onchange_cb=self._onchange_degrees_c) ) - self.add_settable(cmd2.Settable('sunny', bool, 'Is it sunny outside?', self)) + self.add_settable(cmd2.Settable("sunny", bool, "Is it sunny outside?", self)) def do_sunbathe(self, _arg) -> None: """Attempt to sunbathe.""" if self.degrees_c < 20: result = f"It's {self.degrees_c} C - are you a penguin?" elif not self.sunny: - result = 'Too dim.' + result = "Too dim." else: - result = 'UV is bad for your skin.' + result = "UV is bad for your skin." self.poutput(result) def _onchange_degrees_c(self, _param_name, _old, new) -> None: @@ -32,7 +32,7 @@ def _onchange_degrees_c(self, _param_name, _old, new) -> None: self.sunny = True -if __name__ == '__main__': +if __name__ == "__main__": import sys c = EnvironmentApp() diff --git a/examples/event_loops.py b/examples/event_loops.py index aca434207..c2a4cc74a 100755 --- a/examples/event_loops.py +++ b/examples/event_loops.py @@ -18,7 +18,7 @@ def __init__(self) -> None: # ... your class code here ... -if __name__ == '__main__': +if __name__ == "__main__": app = Cmd2EventBased() app.preloop() diff --git a/examples/exit_code.py b/examples/exit_code.py index bfce8c909..389ab56d1 100755 --- a/examples/exit_code.py +++ b/examples/exit_code.py @@ -29,10 +29,10 @@ def do_exit(self, arg_list: list[str]) -> bool: return True -if __name__ == '__main__': +if __name__ == "__main__": import sys app = ReplWithExitCode() sys_exit_code = app.cmdloop() - app.poutput(f'{sys.argv[0]!r} exiting with code: {sys_exit_code}') + app.poutput(f"{sys.argv[0]!r} exiting with code: {sys_exit_code}") sys.exit(sys_exit_code) diff --git a/examples/getting_started.py b/examples/getting_started.py index a5668f0fc..5354f1c32 100755 --- a/examples/getting_started.py +++ b/examples/getting_started.py @@ -33,22 +33,22 @@ class BasicApp(cmd2.Cmd): """Cmd2 application to demonstrate many common features.""" - DEFAULT_CATEGORY = 'My Custom Commands' + DEFAULT_CATEGORY = "My Custom Commands" def __init__(self) -> None: """Initialize the cmd2 application.""" # Startup script that defines a couple aliases for running shell commands - alias_script = pathlib.Path(__file__).absolute().parent / '.cmd2rc' + alias_script = pathlib.Path(__file__).absolute().parent / ".cmd2rc" # Create a shortcut for one of our commands shortcuts = cmd2.DEFAULT_SHORTCUTS - shortcuts.update({'&': 'intro'}) + shortcuts.update({"&": "intro"}) super().__init__( auto_suggest=True, bottom_toolbar=True, include_ipy=True, - multiline_commands=['echo'], - persistent_history_file='cmd2_history.dat', + multiline_commands=["echo"], + persistent_history_file="cmd2_history.dat", shortcuts=shortcuts, startup_script=str(alias_script), ) @@ -62,18 +62,18 @@ def __init__(self) -> None: # Prints an intro banner once upon application startup self.intro = ( stylize( - 'Welcome to cmd2!', + "Welcome to cmd2!", style=Style(color=Color.GREEN1, bgcolor=Color.GRAY0, bold=True), ) - + ' Note the full Unicode support: 😇 💩' - + ' and the persistent bottom bar with realtime status updates!' + + " Note the full Unicode support: 😇 💩" + + " and the persistent bottom bar with realtime status updates!" ) # Show this as the prompt when asking for input - self.prompt = 'myapp> ' + self.prompt = "myapp> " # Used as prompt for multiline commands after the first line - self.continuation_prompt = '... ' + self.continuation_prompt = "... " # Allow access to your application in py and ipy via self self.self_in_py = True @@ -85,9 +85,9 @@ def __init__(self) -> None: fg_colors = [c.value for c in Color] self.add_settable( cmd2.Settable( - 'foreground_color', + "foreground_color", str, - 'Foreground color to use with echo command', + "Foreground color to use with echo command", self, choices=fg_colors, ) @@ -95,7 +95,7 @@ def __init__(self) -> None: def get_rprompt(self) -> str | FormattedText | None: current_working_directory = pathlib.Path.cwd() - style = 'bg:ansired fg:ansiwhite' + style = "bg:ansired fg:ansiwhite" text = f"cwd={current_working_directory}" return FormattedText([(style, text)]) @@ -131,7 +131,7 @@ def do_echo(self, arg: cmd2.Statement) -> None: ) -if __name__ == '__main__': +if __name__ == "__main__": import sys app = BasicApp() diff --git a/examples/hello_cmd2.py b/examples/hello_cmd2.py index 5a760cee9..72705e51e 100755 --- a/examples/hello_cmd2.py +++ b/examples/hello_cmd2.py @@ -5,14 +5,14 @@ cmd2, ) -if __name__ == '__main__': +if __name__ == "__main__": import sys # If run as the main application, simply start a bare-bones cmd2 application with only built-in functionality. app = cmd2.Cmd( include_ipy=True, # Enable support for interactive Python shell via py command include_py=True, # Enable support for interactive IPython shell via ipy command - persistent_history_file='cmd2_history.dat', # Persist history between runs + persistent_history_file="cmd2_history.dat", # Persist history between runs ) app.self_in_py = True # Enable access to "self" within the py command app.debug = True # Show traceback if/when an exception occurs diff --git a/examples/help_categories.py b/examples/help_categories.py index c49843fa6..6cc6b8ed9 100755 --- a/examples/help_categories.py +++ b/examples/help_categories.py @@ -13,7 +13,7 @@ def my_decorator(f): @functools.wraps(f) def wrapper(*args, **kwds): - print('Calling decorated function') + print("Calling decorated function") return f(*args, **kwds) return wrapper @@ -22,22 +22,22 @@ def wrapper(*args, **kwds): class HelpCategories(cmd2.Cmd): """Example cmd2 application.""" - START_TIMES = ('now', 'later', 'sometime', 'whenever') + START_TIMES = ("now", "later", "sometime", "whenever") # Command categories - CMD_CAT_CONNECTING = 'Connecting' - CMD_CAT_APP_MGMT = 'Application Management' - CMD_CAT_SERVER_INFO = 'Server Information' + CMD_CAT_CONNECTING = "Connecting" + CMD_CAT_APP_MGMT = "Application Management" + CMD_CAT_SERVER_INFO = "Server Information" # Show all other commands in "Other" category - cmd2.Cmd.DEFAULT_CATEGORY = 'Other' + cmd2.Cmd.DEFAULT_CATEGORY = "Other" def __init__(self) -> None: super().__init__() def do_connect(self, _) -> None: """Connect command.""" - self.poutput('Connect') + self.poutput("Connect") # Tag the above command functions under the category Connecting cmd2.categorize(do_connect, CMD_CAT_CONNECTING) @@ -45,64 +45,64 @@ def do_connect(self, _) -> None: @cmd2.with_category(CMD_CAT_CONNECTING) def do_which(self, _) -> None: """Which command.""" - self.poutput('Which') + self.poutput("Which") def do_list(self, _) -> None: """List command.""" - self.poutput('List') + self.poutput("List") def do_deploy(self, _) -> None: """Deploy command.""" - self.poutput('Deploy') + self.poutput("Deploy") start_parser = cmd2.Cmd2ArgumentParser( - description='Start', - epilog='my_decorator runs even with argparse errors', + description="Start", + epilog="my_decorator runs even with argparse errors", ) - start_parser.add_argument('when', choices=START_TIMES, help='Specify when to start') + start_parser.add_argument("when", choices=START_TIMES, help="Specify when to start") @my_decorator @cmd2.with_argparser(start_parser) def do_start(self, _) -> None: """Start command.""" - self.poutput('Start') + self.poutput("Start") def do_sessions(self, _) -> None: """Sessions command.""" - self.poutput('Sessions') + self.poutput("Sessions") def do_redeploy(self, _) -> None: """Redeploy command.""" - self.poutput('Redeploy') + self.poutput("Redeploy") restart_parser = cmd2.Cmd2ArgumentParser( - description='Restart', - epilog='my_decorator does not run when argparse errors', + description="Restart", + epilog="my_decorator does not run when argparse errors", ) - restart_parser.add_argument('when', choices=START_TIMES, help='Specify when to restart') + restart_parser.add_argument("when", choices=START_TIMES, help="Specify when to restart") @cmd2.with_argparser(restart_parser) @cmd2.with_category(CMD_CAT_APP_MGMT) @my_decorator def do_restart(self, _) -> None: """Restart command.""" - self.poutput('Restart') + self.poutput("Restart") def do_expire(self, _) -> None: """Expire command.""" - self.poutput('Expire') + self.poutput("Expire") def do_undeploy(self, _) -> None: """Undeploy command.""" - self.poutput('Undeploy') + self.poutput("Undeploy") def do_stop(self, _) -> None: """Stop command.""" - self.poutput('Stop') + self.poutput("Stop") def do_findleakers(self, _) -> None: """Find Leakers command.""" - self.poutput('Find Leakers') + self.poutput("Find Leakers") # Tag the above command functions under the category Application Management cmd2.categorize( @@ -112,19 +112,19 @@ def do_findleakers(self, _) -> None: def do_resources(self, _) -> None: """Resources command.""" - self.poutput('Resources') + self.poutput("Resources") def do_status(self, _) -> None: """Status command.""" - self.poutput('Status') + self.poutput("Status") def do_serverinfo(self, _) -> None: """Server Info command.""" - self.poutput('Server Info') + self.poutput("Server Info") def do_thread_dump(self, _) -> None: """Thread Dump command.""" - self.poutput('Thread Dump') + self.poutput("Thread Dump") def do_sslconnectorciphers(self, _) -> None: """SSL Connector Ciphers command is an example of a command that contains @@ -134,11 +134,11 @@ def do_sslconnectorciphers(self, _) -> None: This is after a blank line and won't de displayed in the verbose help """ - self.poutput('SSL Connector Ciphers') + self.poutput("SSL Connector Ciphers") def do_vminfo(self, _) -> None: """VM Info command.""" - self.poutput('VM Info') + self.poutput("VM Info") # Tag the above command functions under the category Server Information cmd2.categorize(do_resources, CMD_CAT_SERVER_INFO) @@ -152,7 +152,7 @@ def do_vminfo(self, _) -> None: # and show up in the 'Other' group def do_config(self, _) -> None: """Config command.""" - self.poutput('Config') + self.poutput("Config") def do_version(self, _) -> None: """Version command.""" @@ -172,7 +172,7 @@ def do_enable_commands(self, _) -> None: self.poutput("The Application Management commands have been enabled") -if __name__ == '__main__': +if __name__ == "__main__": import sys c = HelpCategories() diff --git a/examples/hooks.py b/examples/hooks.py index 73487bcd7..f8c3a6b39 100755 --- a/examples/hooks.py +++ b/examples/hooks.py @@ -59,14 +59,14 @@ def add_whitespace_hook(self, data: cmd2.plugin.PostparsingData) -> cmd2.plugin. # ^ - the beginning of the string # ([^\s\d]+) - one or more non-whitespace non-digit characters, set as capture group 1 # (\d+) - one or more digit characters, set as capture group 2 - command_pattern = re.compile(r'^([^\s\d]+)(\d+)') + command_pattern = re.compile(r"^([^\s\d]+)(\d+)") match = command_pattern.search(command) if match: command = match.group(1) first_arg = match.group(2) rest_args = data.statement.args post_command = data.statement.post_command - data.statement = self.statement_parser.parse(f'{command} {first_arg} {rest_args} {post_command}') + data.statement = self.statement_parser.parse(f"{command} {first_arg} {rest_args} {post_command}") return data def downcase_hook(self, data: cmd2.plugin.PostparsingData) -> cmd2.plugin.PostparsingData: @@ -74,7 +74,7 @@ def downcase_hook(self, data: cmd2.plugin.PostparsingData) -> cmd2.plugin.Postpa command = data.statement.command.lower() args = data.statement.args post_command = data.statement.post_command - data.statement = self.statement_parser.parse(f'{command} {args} {post_command}') + data.statement = self.statement_parser.parse(f"{command} {args} {post_command}") return data def abbrev_hook(self, data: cmd2.plugin.PostparsingData) -> cmd2.plugin.PostparsingData: @@ -91,7 +91,7 @@ def abbrev_hook(self, data: cmd2.plugin.PostparsingData) -> cmd2.plugin.Postpars def proof_hook(self, data: cmd2.plugin.PostcommandData) -> cmd2.plugin.PostcommandData: """Update the shell prompt with the new raw statement after postparsing hooks are finished.""" if self.debug: - self.prompt = f'({data.statement.raw})' + self.prompt = f"({data.statement.raw})" return data @cmd2.with_argument_list @@ -111,7 +111,7 @@ def do_list(self, arglist: list[str]) -> None: self.poutput(str(x)) -if __name__ == '__main__': +if __name__ == "__main__": import sys c = CmdLineApp() diff --git a/examples/migrating.py b/examples/migrating.py index 9c79d488e..2023ef0d1 100755 --- a/examples/migrating.py +++ b/examples/migrating.py @@ -9,9 +9,9 @@ class CmdLineApp(cmd.Cmd): """Example cmd application.""" - MUMBLES = ('like', '...', 'um', 'er', 'hmmm', 'ahh') - MUMBLE_FIRST = ('so', 'like', 'well') - MUMBLE_LAST = ('right?',) + MUMBLES = ("like", "...", "um", "er", "hmmm", "ahh") + MUMBLE_FIRST = ("so", "like", "well") + MUMBLE_LAST = ("right?",) def do_exit(self, _line) -> bool: """Exit the application.""" @@ -28,7 +28,7 @@ def do_speak(self, line) -> None: def do_mumble(self, line) -> None: """Mumbles what you tell me to.""" - words = line.split(' ') + words = line.split(" ") output = [] if random.random() < 0.33: output.append(random.choice(self.MUMBLE_FIRST)) @@ -38,10 +38,10 @@ def do_mumble(self, line) -> None: output.append(word) if random.random() < 0.25: output.append(random.choice(self.MUMBLE_LAST)) - print(' '.join(output), file=self.stdout) + print(" ".join(output), file=self.stdout) -if __name__ == '__main__': +if __name__ == "__main__": import sys c = CmdLineApp() diff --git a/examples/mixin.py b/examples/mixin.py index 90b2ce56d..e80813c4f 100755 --- a/examples/mixin.py +++ b/examples/mixin.py @@ -62,7 +62,7 @@ def cmd2_mymixin_postloop_hook(self) -> None: def cmd2_mymixin_postparsing_hook(self, data: cmd2.plugin.PostparsingData) -> cmd2.plugin.PostparsingData: """Method to be called after parsing user input, but before running the command.""" - self.poutput('in postparsing hook') + self.poutput("in postparsing hook") return data @@ -75,9 +75,9 @@ def __init__(self, *args, **kwargs) -> None: @empty_decorator def do_something(self, _arg) -> None: - self.poutput('this is the something command') + self.poutput("this is the something command") -if __name__ == '__main__': +if __name__ == "__main__": app = Example() app.cmdloop() diff --git a/examples/modular_commands/commandset_basic.py b/examples/modular_commands/commandset_basic.py index 01d121caa..8462e9ddb 100644 --- a/examples/modular_commands/commandset_basic.py +++ b/examples/modular_commands/commandset_basic.py @@ -11,15 +11,15 @@ class BasicCompletionCommandSet(CommandSet[Cmd]): - DEFAULT_CATEGORY = 'Basic Completion' + DEFAULT_CATEGORY = "Basic Completion" # This data is used to demonstrate delimiter_complete file_strs = ( - '/home/user/file.db', - '/home/user/file space.db', - '/home/user/another.db', - '/home/other user/maps.db', - '/home/other user/tests.db', + "/home/user/file.db", + "/home/user/file space.db", + "/home/user/another.db", + "/home/other user/maps.db", + "/home/other user/tests.db", ) def do_delimiter_complete(self, statement: Statement) -> None: @@ -27,7 +27,7 @@ def do_delimiter_complete(self, statement: Statement) -> None: self._cmd.poutput(f"Args: {statement.args}") def complete_delimiter_complete(self, text: str, line: str, begidx: int, endidx: int) -> Completions: - return self._cmd.delimiter_complete(text, line, begidx, endidx, match_against=self.file_strs, delimiter='/') + return self._cmd.delimiter_complete(text, line, begidx, endidx, match_against=self.file_strs, delimiter="/") def do_raise_error(self, statement: Statement) -> None: """Demonstrates effect of raising CompletionError.""" @@ -42,6 +42,6 @@ def complete_raise_error(self, _text: str, _line: str, _begidx: int, _endidx: in """ raise CompletionError("This is how a CompletionError behaves") - @with_category('Not Basic Completion') + @with_category("Not Basic Completion") def do_custom_category(self, _statement: Statement) -> None: - self._cmd.poutput('Demonstrates a command that bypasses the default category') + self._cmd.poutput("Demonstrates a command that bypasses the default category") diff --git a/examples/modular_commands/commandset_complex.py b/examples/modular_commands/commandset_complex.py index 8d78b97b1..c18d60e9d 100644 --- a/examples/modular_commands/commandset_complex.py +++ b/examples/modular_commands/commandset_complex.py @@ -6,43 +6,43 @@ class CommandSetA(cmd2.CommandSet): - DEFAULT_CATEGORY = 'Fruits' + DEFAULT_CATEGORY = "Fruits" def do_apple(self, _statement: cmd2.Statement) -> None: """Apple Command.""" - self._cmd.poutput('Apple!') + self._cmd.poutput("Apple!") def do_banana(self, _statement: cmd2.Statement) -> None: """Banana Command.""" - self._cmd.poutput('Banana!!') + self._cmd.poutput("Banana!!") cranberry_parser = cmd2.Cmd2ArgumentParser() - cranberry_parser.add_argument('arg1', choices=['lemonade', 'juice', 'sauce']) + cranberry_parser.add_argument("arg1", choices=["lemonade", "juice", "sauce"]) @cmd2.with_argparser(cranberry_parser, with_unknown_args=True) def do_cranberry(self, ns: argparse.Namespace, unknown: list[str]) -> None: - self._cmd.poutput(f'Cranberry {ns.arg1}!!') + self._cmd.poutput(f"Cranberry {ns.arg1}!!") if unknown and len(unknown): - self._cmd.poutput('Unknown: ' + ', '.join(['{}'] * len(unknown)).format(*unknown)) - self._cmd.last_result = {'arg1': ns.arg1, 'unknown': unknown} + self._cmd.poutput("Unknown: " + ", ".join(["{}"] * len(unknown)).format(*unknown)) + self._cmd.last_result = {"arg1": ns.arg1, "unknown": unknown} def help_cranberry(self) -> None: - self._cmd.stdout.write('This command does diddly squat...\n') + self._cmd.stdout.write("This command does diddly squat...\n") @cmd2.with_argument_list - @cmd2.with_category('Also Alone') + @cmd2.with_category("Also Alone") def do_durian(self, args: list[str]) -> None: """Durian Command.""" - self._cmd.poutput(f'{len(args)} Arguments: ') - self._cmd.poutput(', '.join(['{}'] * len(args)).format(*args)) + self._cmd.poutput(f"{len(args)} Arguments: ") + self._cmd.poutput(", ".join(["{}"] * len(args)).format(*args)) def complete_durian(self, text: str, line: str, begidx: int, endidx: int) -> list[str]: - return self._cmd.basic_complete(text, line, begidx, endidx, ['stinks', 'smells', 'disgusting']) + return self._cmd.basic_complete(text, line, begidx, endidx, ["stinks", "smells", "disgusting"]) elderberry_parser = cmd2.Cmd2ArgumentParser() - elderberry_parser.add_argument('arg1') + elderberry_parser.add_argument("arg1") - @cmd2.with_category('Alone') + @cmd2.with_category("Alone") @cmd2.with_argparser(elderberry_parser) def do_elderberry(self, ns: argparse.Namespace) -> None: - self._cmd.poutput(f'Elderberry {ns.arg1}!!') + self._cmd.poutput(f"Elderberry {ns.arg1}!!") diff --git a/examples/modular_commands/commandset_custominit.py b/examples/modular_commands/commandset_custominit.py index 989f19f70..8d1918f5b 100644 --- a/examples/modular_commands/commandset_custominit.py +++ b/examples/modular_commands/commandset_custominit.py @@ -8,7 +8,7 @@ class CustomInitCommandSet(CommandSet[Cmd]): - DEFAULT_CATEGORY = 'Custom Init' + DEFAULT_CATEGORY = "Custom Init" def __init__(self, arg1, arg2) -> None: super().__init__() @@ -18,8 +18,8 @@ def __init__(self, arg1, arg2) -> None: def do_show_arg1(self, _: Statement) -> None: """Show Arg 1.""" - self._cmd.poutput('Arg1: ' + self._arg1) + self._cmd.poutput("Arg1: " + self._arg1) def do_show_arg2(self, _: Statement) -> None: """Show Arg 2.""" - self._cmd.poutput('Arg2: ' + self._arg2) + self._cmd.poutput("Arg2: " + self._arg2) diff --git a/examples/modular_commandsets.py b/examples/modular_commandsets.py index 79cc366b1..3d9900d61 100755 --- a/examples/modular_commandsets.py +++ b/examples/modular_commandsets.py @@ -29,7 +29,7 @@ class WithCommandSets(Cmd): def __init__(self, command_sets: Iterable[CommandSet] | None = None) -> None: """Cmd2 application to demonstrate a variety of methods for loading CommandSets.""" super().__init__(command_sets=command_sets) - self.sport_item_strs = ['Bat', 'Basket', 'Basketball', 'Football', 'Space Ball'] + self.sport_item_strs = ["Bat", "Basket", "Basketball", "Football", "Space Ball"] def choices_provider(self) -> list[str]: """A choices provider is useful when the choice list is based on instance data of your application.""" @@ -43,16 +43,16 @@ def choices_provider(self) -> list[str]: # 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" + "--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" + "--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: @@ -60,10 +60,10 @@ def do_example(self, _: argparse.Namespace) -> None: self.poutput("I do nothing") -if __name__ == '__main__': +if __name__ == "__main__": import sys print("Starting") - my_sets = [BasicCompletionCommandSet(), CommandSetA(), CustomInitCommandSet('First argument', 'Second argument')] + my_sets = [BasicCompletionCommandSet(), CommandSetA(), CustomInitCommandSet("First argument", "Second argument")] app = WithCommandSets(command_sets=my_sets) sys.exit(app.cmdloop()) diff --git a/examples/paged_output.py b/examples/paged_output.py index 935bdd2e9..d10740fe6 100755 --- a/examples/paged_output.py +++ b/examples/paged_output.py @@ -20,7 +20,7 @@ def page_file(self, file_path: str, chop: bool = False) -> None: text = f.read() self.ppaged(text, chop=chop) except OSError as ex: - self.pexcept(f'Error reading {filename!r}: {ex}') + self.pexcept(f"Error reading {filename!r}: {ex}") @cmd2.with_argument_list def do_page_wrap(self, args: list[str]) -> None: @@ -29,7 +29,7 @@ def do_page_wrap(self, args: list[str]) -> None: Usage: page_wrap """ if not args: - self.perror('page_wrap requires a path to a file as an argument') + self.perror("page_wrap requires a path to a file as an argument") return self.page_file(args[0], chop=False) @@ -44,14 +44,14 @@ def do_page_truncate(self, args: list[str]) -> None: Usage: page_chop """ if not args: - self.perror('page_truncate requires a path to a file as an argument') + self.perror("page_truncate requires a path to a file as an argument") return self.page_file(args[0], chop=True) complete_page_truncate = cmd2.Cmd.path_complete -if __name__ == '__main__': +if __name__ == "__main__": import sys app = PagedOutput() diff --git a/examples/persistent_history.py b/examples/persistent_history.py index d2ae8ceff..e1bc607cf 100755 --- a/examples/persistent_history.py +++ b/examples/persistent_history.py @@ -17,15 +17,15 @@ def __init__(self, hist_file) -> None: :param hist_file: file to load history from at start and write it to at end """ super().__init__(persistent_history_file=hist_file, persistent_history_length=500, allow_cli_args=False) - self.prompt = 'ph> ' + self.prompt = "ph> " # ... your class code here ... -if __name__ == '__main__': +if __name__ == "__main__": import sys - history_file = '~/.persistent_history.cmd2' + history_file = "~/.persistent_history.cmd2" if len(sys.argv) > 1: history_file = sys.argv[1] diff --git a/examples/pretty_print.py b/examples/pretty_print.py index 110f9aa86..f10dc185c 100755 --- a/examples/pretty_print.py +++ b/examples/pretty_print.py @@ -23,6 +23,6 @@ def do_pretty(self, _: cmd2.Statement) -> None: self.ppretty(EXAMPLE_DATA) -if __name__ == '__main__': +if __name__ == "__main__": app = Cmd2App() app.cmdloop() diff --git a/examples/python_scripting.py b/examples/python_scripting.py index 0e5c6fc61..044736ad4 100755 --- a/examples/python_scripting.py +++ b/examples/python_scripting.py @@ -37,12 +37,12 @@ def __init__(self) -> None: # Set include_ipy to True to enable the "ipy" command which runs an interactive IPython shell super().__init__(include_ipy=True) self._set_prompt() - self.intro = 'Happy 𝛑 Day. Note the full Unicode support: 😇 💩' + self.intro = "Happy 𝛑 Day. Note the full Unicode support: 😇 💩" def _set_prompt(self) -> None: """Set prompt so it displays the current working directory.""" self.cwd = os.getcwd() - self.prompt = stylize(f'{self.cwd} $ ', style=Color.CYAN) + self.prompt = stylize(f"{self.cwd} $ ", style=Color.CYAN) def postcmd(self, stop: bool, _line: str) -> bool: """Hook method executed just after a command dispatch is finished. @@ -64,8 +64,8 @@ def do_cd(self, arglist: list[str]) -> None: # Expect 1 argument, the directory to change to if not arglist or len(arglist) != 1: self.perror("cd requires exactly 1 argument:") - self.do_help('cd') - self.last_result = 'Bad arguments' + self.do_help("cd") + self.last_result = "Bad arguments" return # Convert relative paths to absolute paths @@ -75,16 +75,16 @@ def do_cd(self, arglist: list[str]) -> None: err = None data = None if not os.path.isdir(path): - err = f'{path} is not a directory' + err = f"{path} is not a directory" elif not os.access(path, os.R_OK): - err = f'You do not have read access to {path}' + err = f"You do not have read access to {path}" else: try: os.chdir(path) except Exception as ex: # noqa: BLE001 - err = f'{ex}' + err = f"{ex}" else: - self.poutput(f'Successfully changed directory to {path}') + self.poutput(f"Successfully changed directory to {path}") data = path if err: @@ -97,7 +97,7 @@ def complete_cd(self, text: str, line: str, begidx: int, endidx: int) -> list[st return self.path_complete(text, line, begidx, endidx, path_filter=os.path.isdir) dir_parser = cmd2.Cmd2ArgumentParser() - dir_parser.add_argument('-l', '--long', action='store_true', help="display in long format with one item per line") + dir_parser.add_argument("-l", "--long", action="store_true", help="display in long format with one item per line") @cmd2.with_argparser(dir_parser, with_unknown_args=True) def do_dir(self, _args: argparse.Namespace, unknown: list[str]) -> None: @@ -105,21 +105,21 @@ def do_dir(self, _args: argparse.Namespace, unknown: list[str]) -> None: # No arguments for this command if unknown: self.perror("dir does not take any positional arguments:") - self.do_help('dir') - self.last_result = 'Bad arguments' + self.do_help("dir") + self.last_result = "Bad arguments" return # Get the contents as a list contents = os.listdir(self.cwd) for f in contents: - self.poutput(f'{f}') - self.poutput('') + self.poutput(f"{f}") + self.poutput("") self.last_result = contents -if __name__ == '__main__': +if __name__ == "__main__": import sys c = CmdLineApp() diff --git a/examples/read_input.py b/examples/read_input.py index 054264842..24dcad205 100755 --- a/examples/read_input.py +++ b/examples/read_input.py @@ -16,7 +16,7 @@ class ReadInputApp(cmd2.Cmd): def __init__(self, *args, **kwargs) -> None: super().__init__(*args, **kwargs) self.prompt = "\n" + self.prompt - self.custom_history = ['history 1', 'history 2'] + self.custom_history = ["history 1", "history 2"] @cmd2.with_category(EXAMPLE_COMMANDS) def do_basic(self, _) -> None: @@ -44,7 +44,7 @@ def do_custom_choices(self, _) -> None: input_str = self.read_input( "> ", history=self.custom_history, - choices=['choice_1', 'choice_2', 'choice_3'], + choices=["choice_1", "choice_2", "choice_3"], ) except EOFError: pass @@ -83,10 +83,10 @@ def do_custom_completer(self, _) -> None: @cmd2.with_category(EXAMPLE_COMMANDS) def do_custom_parser(self, _) -> None: """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_2', help="path of something", completer=cmd2.Cmd.path_complete) + 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_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()) @@ -116,13 +116,13 @@ def do_eat(self, arg): Usage: eat wheatties """ - sauce = self.select('sweet salty', 'Sauce? ') - result = '{food} with {sauce} sauce, yum!' + sauce = self.select("sweet salty", "Sauce? ") + result = "{food} with {sauce} sauce, yum!" result = result.format(food=arg, sauce=sauce) - self.stdout.write(result + '\n') + self.stdout.write(result + "\n") -if __name__ == '__main__': +if __name__ == "__main__": import sys app = ReadInputApp() diff --git a/examples/remove_builtin_commands.py b/examples/remove_builtin_commands.py index eb226c7a8..c4fe0a1c3 100755 --- a/examples/remove_builtin_commands.py +++ b/examples/remove_builtin_commands.py @@ -18,13 +18,13 @@ def __init__(self) -> None: super().__init__() # To hide commands from displaying in the help menu, add them to the hidden_commands list - self.hidden_commands.append('history') + self.hidden_commands.append("history") # To remove built-in commands entirely, delete their "do_*" function from the cmd2.Cmd class del cmd2.Cmd.do_edit -if __name__ == '__main__': +if __name__ == "__main__": import sys app = RemoveBuiltinCommands() diff --git a/examples/remove_settable.py b/examples/remove_settable.py index c2c338890..c11eff7d8 100755 --- a/examples/remove_settable.py +++ b/examples/remove_settable.py @@ -7,10 +7,10 @@ class MyApp(cmd2.Cmd): def __init__(self) -> None: super().__init__() - self.remove_settable('debug') + self.remove_settable("debug") -if __name__ == '__main__': +if __name__ == "__main__": import sys c = MyApp() diff --git a/examples/rich_tables.py b/examples/rich_tables.py index cc336d79b..a729b2a34 100755 --- a/examples/rich_tables.py +++ b/examples/rich_tables.py @@ -20,7 +20,7 @@ import cmd2 from cmd2.colors import Color -CITY_HEADERS = ['Flag', 'City', 'Country', '2025 Population'] +CITY_HEADERS = ["Flag", "City", "Country", "2025 Population"] CITY_DATA = [ ["🇯🇵", "Tokyo (東京)", "Japan", 37_036_200], ["🇮🇳", "Delhi", "India", 34_665_600], @@ -37,13 +37,13 @@ CITY_CAPTION = "Data from https://worldpopulationreview.com/" COUNTRY_HEADERS = [ - 'Flag', - 'Country', - '2025 Population', - 'Area (M km^2)', - 'Population Density (/km^2)', - 'GDP (million US$)', - 'GDP per capita (US$)', + "Flag", + "Country", + "2025 Population", + "Area (M km^2)", + "Population Density (/km^2)", + "GDP (million US$)", + "GDP per capita (US$)", ] COUNTRY_DATA = [ ["🇮🇳", "India", 1_463_870_000, 3.3, 492, 4_187_017, 2_878], @@ -64,14 +64,14 @@ class TableApp(cmd2.Cmd): """Cmd2 application to demonstrate displaying tabular data using rich.""" - DEFAULT_CATEGORY = 'Table Commands' + DEFAULT_CATEGORY = "Table Commands" def __init__(self) -> None: """Initialize the cmd2 application.""" super().__init__() # Prints an intro banner once upon application startup - self.intro = 'Are you curious which countries and cities on Earth have the largest populations?' + self.intro = "Are you curious which countries and cities on Earth have the largest populations?" def do_cities(self, _: cmd2.Statement) -> None: """Display the cities with the largest population.""" @@ -105,9 +105,9 @@ def do_countries(self, _: cmd2.Statement) -> None: case percap if "per capita" in percap: header_style = Color.BRIGHT_GREEN style = Color.GREEN - case flag if 'Flag' in flag: + case flag if "Flag" in flag: justify = "center" - case country if 'Country' in country: + case country if "Country" in country: justify = "left" table.add_column(header, justify=justify, header_style=header_style, style=style) @@ -120,6 +120,6 @@ def do_countries(self, _: cmd2.Statement) -> None: self.poutput(table) -if __name__ == '__main__': +if __name__ == "__main__": app = TableApp() app.cmdloop() diff --git a/examples/scripts/conditional.py b/examples/scripts/conditional.py index 99c442de7..ca47465f6 100644 --- a/examples/scripts/conditional.py +++ b/examples/scripts/conditional.py @@ -13,36 +13,36 @@ if len(sys.argv) > 1: directory = sys.argv[1] - print(f'Using specified directory: {directory!r}') + print(f"Using specified directory: {directory!r}") else: - directory = 'foobar' - print(f'Using default directory: {directory!r}') + directory = "foobar" + print(f"Using default directory: {directory!r}") # Keep track of where we stared original_dir = os.getcwd() # Try to change to the specified directory -result = app(f'cd {directory}') +result = app(f"cd {directory}") # Conditionally do something based on the results of the last command if result: print(f"STDOUT: {result.stdout}\n") print(f"STDERR: {result.stderr}\n") - print(f'\nContents of directory {directory!r}:') - result = app('dir -l') + print(f"\nContents of directory {directory!r}:") + result = app("dir -l") print(f"STDOUT: {result.stdout}\n") print(f"STDERR: {result.stderr}\n") - print(f'{result.data}\n') + print(f"{result.data}\n") # Change back to where we were - print(f'Changing back to original directory: {original_dir!r}') - app(f'cd {original_dir}') + print(f"Changing back to original directory: {original_dir!r}") + app(f"cd {original_dir}") else: # cd command failed, print a warning - print(f'Failed to change directory to {directory!r}') + print(f"Failed to change directory to {directory!r}") print(f"STDOUT: {result.stdout}\n") print(f"STDERR: {result.stderr}\n") diff --git a/examples/scripts/save_help_text.py b/examples/scripts/save_help_text.py index cbc425592..a9e196cc7 100644 --- a/examples/scripts/save_help_text.py +++ b/examples/scripts/save_help_text.py @@ -31,7 +31,7 @@ def get_sub_commands(parser: Cmd2ArgumentParser) -> list[str]: sub_cmds.append(subcmd) # Look for nested subcommands - sub_cmds.extend(f'{subcmd} {nested_subcmd}' for nested_subcmd in get_sub_commands(subcmd_parser)) + sub_cmds.extend(f"{subcmd} {nested_subcmd}" for nested_subcmd in get_sub_commands(subcmd_parser)) sub_cmds.sort() return sub_cmds @@ -46,17 +46,17 @@ def add_help_to_file(item: str, outfile: TextIO, is_command: bool) -> None: """ label = "COMMAND" if is_command else "TOPIC" - header = f'{ASTERISKS}\n{label}: {item}\n{ASTERISKS}\n' + header = f"{ASTERISKS}\n{label}: {item}\n{ASTERISKS}\n" outfile.write(header) - result = app(f'help {item}') + result = app(f"help {item}") outfile.write(result.stdout) def main() -> None: """Main function of this script.""" # Make sure we have access to self - if 'self' not in globals(): + if "self" not in globals(): print("Re-run this script from a cmd2 application where self_in_py is True") return @@ -67,12 +67,12 @@ def main() -> None: outfile_path = os.path.expanduser(sys.argv[1]) try: - with open(outfile_path, 'w') as outfile: + with open(outfile_path, "w") as outfile: # Write the help summary - header = f'{ASTERISKS}\nSUMMARY\n{ASTERISKS}\n' + header = f"{ASTERISKS}\nSUMMARY\n{ASTERISKS}\n" outfile.write(header) - result = app('help -v') + result = app("help -v") outfile.write(result.stdout) # Get a list of all commands and help topics and then filter out duplicates @@ -94,7 +94,7 @@ def main() -> None: # Add any subcommands for subcmd in get_sub_commands(parser): - full_cmd = f'{item} {subcmd}' + full_cmd = f"{item} {subcmd}" add_help_to_file(full_cmd, outfile, is_command) print(f"Output written to {outfile_path}") diff --git a/examples/unicode_commands.py b/examples/unicode_commands.py index 3321e636f..ee8cc0dcb 100755 --- a/examples/unicode_commands.py +++ b/examples/unicode_commands.py @@ -11,7 +11,7 @@ class UnicodeApp(cmd2.Cmd): def __init__(self) -> None: super().__init__() - self.intro = 'Welcome the Unicode example app. Note the full Unicode support: 😇 💩' + self.intro = "Welcome the Unicode example app. Note the full Unicode support: 😇 💩" def do_𝛑print(self, _) -> None: # noqa: PLC2401 """This command prints 𝛑 to 5 decimal places.""" @@ -22,6 +22,6 @@ def do_你好(self, arg) -> None: # noqa: N802, PLC2401 self.poutput("你好 " + arg) -if __name__ == '__main__': +if __name__ == "__main__": app = UnicodeApp() app.cmdloop() diff --git a/ruff.toml b/ruff.toml index 64ccea3db..e63651609 100644 --- a/ruff.toml +++ b/ruff.toml @@ -165,7 +165,7 @@ mccabe.max-complexity = 49 [format] # Like Black, use double quotes for strings. -quote-style = "preserve" +quote-style = "double" # Like Black, indent with spaces, rather than tabs. indent-style = "space" diff --git a/scripts/validate_tag.py b/scripts/validate_tag.py index 4c8325645..ff5d0ecf1 100755 --- a/scripts/validate_tag.py +++ b/scripts/validate_tag.py @@ -4,7 +4,7 @@ import re import subprocess -SEMVER_SIMPLE = re.compile(r'(\d+)\.(\d+)\.(\d+)((a|b|rc)(\d+))?') +SEMVER_SIMPLE = re.compile(r"(\d+)\.(\d+)\.(\d+)((a|b|rc)(\d+))?") SEMVER_PATTERN = re.compile( r""" ^ # Start of the string @@ -31,12 +31,12 @@ def get_current_tag() -> str: try: # Gets the name of the latest tag reachable from the current commit result = subprocess.run( - ['git', 'describe', '--exact-match', '--tags', '--abbrev=0'], capture_output=True, text=True, check=True + ["git", "describe", "--exact-match", "--tags", "--abbrev=0"], capture_output=True, text=True, check=True ) return result.stdout.strip() except subprocess.CalledProcessError: print("Could not find a reachable tag.") - return '' + return "" def is_semantic_version(tag_name: str) -> bool: @@ -73,12 +73,12 @@ def is_semantic_version(tag_name: str) -> bool: return bool(semver_pattern.match(tag_name)) -if __name__ == '__main__': +if __name__ == "__main__": import sys git_tag = get_current_tag() if not git_tag: - print('Git tag does not exist for current commit.') + print("Git tag does not exist for current commit.") sys.exit(-1) if not is_semantic_version(git_tag): diff --git a/tests/conftest.py b/tests/conftest.py index 3b68e36c6..8cbce3036 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -18,8 +18,8 @@ from cmd2.utils import StdSim # For type hinting decorators -P = ParamSpec('P') -T = TypeVar('T') +P = ParamSpec("P") +T = TypeVar("T") def verify_help_text(cmd2_app: cmd2.Cmd, help_output: str | list[str], verbose_strings: list[str] | None = None) -> None: @@ -29,7 +29,7 @@ def verify_help_text(cmd2_app: cmd2.Cmd, help_output: str | list[str], verbose_s :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) + help_text = help_output if isinstance(help_output, str) else "".join(help_output) commands = cmd2_app.get_visible_commands() for command in commands: assert command in help_text @@ -55,7 +55,7 @@ def normalize(block: str) -> list[str]: from each line. """ assert isinstance(block, str) - block = block.strip('\n') + block = block.strip("\n") return [line.rstrip() for line in block.splitlines()] @@ -114,7 +114,7 @@ def cmd_wrapper(*args: P.args, **kwargs: P.kwargs) -> T: # These are odd file names for testing quoting of them -odd_file_names = ['nothingweird', 'has spaces', '"is_double_quoted"', "'is_single_quoted'"] +odd_file_names = ["nothingweird", "has spaces", '"is_double_quoted"', "'is_single_quoted'"] if TYPE_CHECKING: @@ -139,7 +139,7 @@ def __init__(self, *args, **kwargs): # code placed here runs before cmd2 initializes super().__init__(*args, **kwargs) if not isinstance(self, cmd2.Cmd): - raise TypeError('The ExternalTestMixin class is intended to be used in multiple inheritance with cmd2.Cmd') + raise TypeError("The ExternalTestMixin class is intended to be used in multiple inheritance with cmd2.Cmd") # code placed here runs after cmd2 initializes self._pybridge = cmd2.py_bridge.PyBridge(self) diff --git a/tests/pyscript/echo.py b/tests/pyscript/echo.py index c5999355a..4eb19d86e 100644 --- a/tests/pyscript/echo.py +++ b/tests/pyscript/echo.py @@ -2,7 +2,7 @@ app.cmd_echo = False # echo defaults to current setting which is False, so this help text should not be echoed to pytest's stdout -app('help alias') +app("help alias") # pytest's stdout should have this help text written to it -app('help edit', echo=True) +app("help edit", echo=True) diff --git a/tests/pyscript/environment.py b/tests/pyscript/environment.py index 758c85002..adced4ae0 100644 --- a/tests/pyscript/environment.py +++ b/tests/pyscript/environment.py @@ -4,7 +4,7 @@ app.cmd_echo = True -if __name__ != '__main__': +if __name__ != "__main__": print(f"Error: __name__ is: {__name__}") quit() diff --git a/tests/pyscript/help.py b/tests/pyscript/help.py index 480c6cd70..7cce3da3e 100644 --- a/tests/pyscript/help.py +++ b/tests/pyscript/help.py @@ -1,5 +1,5 @@ app.cmd_echo = True -app('help') +app("help") # Exercise py_quit() in unit test quit() diff --git a/tests/pyscript/raises_exception.py b/tests/pyscript/raises_exception.py index 9883a2b87..cfd466a77 100644 --- a/tests/pyscript/raises_exception.py +++ b/tests/pyscript/raises_exception.py @@ -1,3 +1,3 @@ """Example demonstrating what happens when a Python script raises an exception""" -x = 1 + 'blue' +x = 1 + "blue" diff --git a/tests/pyscript/recursive.py b/tests/pyscript/recursive.py index f71234b8e..05cf49c81 100644 --- a/tests/pyscript/recursive.py +++ b/tests/pyscript/recursive.py @@ -5,4 +5,4 @@ app.cmd_echo = True my_dir = os.path.dirname(os.path.realpath(sys.argv[0])) -app('run_pyscript {}'.format(os.path.join(my_dir, 'stop.py'))) +app("run_pyscript {}".format(os.path.join(my_dir, "stop.py"))) diff --git a/tests/pyscript/self_in_py.py b/tests/pyscript/self_in_py.py index ee26293f6..67a1b7dc3 100644 --- a/tests/pyscript/self_in_py.py +++ b/tests/pyscript/self_in_py.py @@ -1,5 +1,5 @@ # Tests self_in_py in pyscripts -if 'self' in globals(): +if "self" in globals(): print("I see self") else: print("I do not see self") diff --git a/tests/pyscript/stop.py b/tests/pyscript/stop.py index 31b587bd2..a7d7eb69b 100644 --- a/tests/pyscript/stop.py +++ b/tests/pyscript/stop.py @@ -1,8 +1,8 @@ app.cmd_echo = True -app('help') +app("help") # This will set stop to True in the PyBridge -app('quit') +app("quit") # Exercise py_quit() in unit test quit() diff --git a/tests/test_argparse.py b/tests/test_argparse.py index 4b4fb3772..8e0c44459 100644 --- a/tests/test_argparse.py +++ b/tests/test_argparse.py @@ -24,10 +24,10 @@ def namespace_provider(self) -> argparse.Namespace: @staticmethod def _say_parser_builder() -> cmd2.Cmd2ArgumentParser: say_parser = cmd2.Cmd2ArgumentParser() - say_parser.add_argument('-p', '--piglatin', action='store_true', help='atinLay') - say_parser.add_argument('-s', '--shout', action='store_true', help='N00B EMULATION MODE') - say_parser.add_argument('-r', '--repeat', type=int, help='output [n] times') - say_parser.add_argument('words', nargs='+', help='words to say') + say_parser.add_argument("-p", "--piglatin", action="store_true", help="atinLay") + say_parser.add_argument("-s", "--shout", action="store_true", help="N00B EMULATION MODE") + say_parser.add_argument("-r", "--repeat", type=int, help="output [n] times") + say_parser.add_argument("words", nargs="+", help="words to say") return say_parser @cmd2.with_argparser(_say_parser_builder) @@ -41,53 +41,53 @@ def do_say(self, args, *, keyword_arg: str | None = None) -> None: for word in args.words: modified_word = word if word is None: - modified_word = '' + modified_word = "" if args.piglatin: - modified_word = f'{word[1:]}{word[0]}ay' + modified_word = f"{word[1:]}{word[0]}ay" if args.shout: modified_word = word.upper() words.append(modified_word) repetitions = args.repeat or 1 for _ in range(min(repetitions, self.maxrepeats)): - self.stdout.write(' '.join(words)) - self.stdout.write('\n') + self.stdout.write(" ".join(words)) + self.stdout.write("\n") if keyword_arg is not None: print(keyword_arg) - tag_parser = cmd2.Cmd2ArgumentParser(description='create a html tag') - tag_parser.add_argument('tag', help='tag') - tag_parser.add_argument('content', nargs='+', help='content to surround with tag') + tag_parser = cmd2.Cmd2ArgumentParser(description="create a html tag") + tag_parser.add_argument("tag", help="tag") + tag_parser.add_argument("content", nargs="+", help="content to surround with tag") @cmd2.with_argparser(tag_parser, preserve_quotes=True) def do_tag(self, args) -> None: - self.stdout.write('<{0}>{1}'.format(args.tag, ' '.join(args.content))) - self.stdout.write('\n') + self.stdout.write("<{0}>{1}".format(args.tag, " ".join(args.content))) + self.stdout.write("\n") @cmd2.with_argparser(cmd2.Cmd2ArgumentParser(), ns_provider=namespace_provider) def do_test_argparse_ns(self, args) -> None: - self.stdout.write(f'{args.custom_stuff}') + self.stdout.write(f"{args.custom_stuff}") @cmd2.with_argument_list def do_arglist(self, arglist, *, keyword_arg: str | None = None) -> None: if isinstance(arglist, list): - self.stdout.write('True') + self.stdout.write("True") else: - self.stdout.write('False') + self.stdout.write("False") if keyword_arg is not None: print(keyword_arg) @cmd2.with_argument_list(preserve_quotes=True) def do_preservelist(self, arglist) -> None: - self.stdout.write(f'{arglist}') + self.stdout.write(f"{arglist}") @classmethod def _speak_parser_builder(cls) -> cmd2.Cmd2ArgumentParser: known_parser = cmd2.Cmd2ArgumentParser() - known_parser.add_argument('-p', '--piglatin', action='store_true', help='atinLay') - known_parser.add_argument('-s', '--shout', action='store_true', help='N00B EMULATION MODE') - known_parser.add_argument('-r', '--repeat', type=int, help='output [n] times') + known_parser.add_argument("-p", "--piglatin", action="store_true", help="atinLay") + known_parser.add_argument("-s", "--shout", action="store_true", help="N00B EMULATION MODE") + known_parser.add_argument("-r", "--repeat", type=int, help="output [n] times") return known_parser @cmd2.with_argparser(_speak_parser_builder, with_unknown_args=True) @@ -97,27 +97,27 @@ def do_speak(self, args, extra, *, keyword_arg: str | None = None) -> None: for word in extra: modified_word = word if word is None: - modified_word = '' + modified_word = "" if args.piglatin: - modified_word = f'{word[1:]}{word[0]}ay' + modified_word = f"{word[1:]}{word[0]}ay" if args.shout: modified_word = word.upper() words.append(modified_word) repetitions = args.repeat or 1 for _ in range(min(repetitions, self.maxrepeats)): - self.stdout.write(' '.join(words)) - self.stdout.write('\n') + self.stdout.write(" ".join(words)) + self.stdout.write("\n") if keyword_arg is not None: print(keyword_arg) @cmd2.with_argparser(cmd2.Cmd2ArgumentParser(), preserve_quotes=True, with_unknown_args=True) def do_test_argparse_with_list_quotes(self, args, extra) -> None: - self.stdout.write('{}'.format(' '.join(extra))) + self.stdout.write("{}".format(" ".join(extra))) @cmd2.with_argparser(cmd2.Cmd2ArgumentParser(), ns_provider=namespace_provider, with_unknown_args=True) def do_test_argparse_with_list_ns(self, args, extra) -> None: - self.stdout.write(f'{args.custom_stuff}') + self.stdout.write(f"{args.custom_stuff}") @pytest.fixture @@ -131,25 +131,25 @@ def test_invalid_syntax(argparse_app) -> None: def test_argparse_basic_command(argparse_app) -> None: - out, _err = run_cmd(argparse_app, 'say hello') - assert out == ['hello'] + out, _err = run_cmd(argparse_app, "say hello") + assert out == ["hello"] def test_argparse_remove_quotes(argparse_app) -> None: out, _err = run_cmd(argparse_app, 'say "hello there"') - assert out == ['hello there'] + assert out == ["hello there"] def test_argparse_with_no_args(argparse_app) -> None: """Make sure we receive TypeError when calling argparse-based function with no args""" with pytest.raises(TypeError) as excinfo: argparse_app.do_say() - assert 'Expected arguments' in str(excinfo.value) + assert "Expected arguments" in str(excinfo.value) def test_argparser_kwargs(argparse_app, capsys) -> None: """Test with_argparser wrapper passes through kwargs to command function""" - argparse_app.do_say('word', keyword_arg="foo") + argparse_app.do_say("word", keyword_arg="foo") out, _err = capsys.readouterr() assert out == "foo\n" @@ -160,18 +160,18 @@ def test_argparse_preserve_quotes(argparse_app) -> None: def test_argparse_custom_namespace(argparse_app) -> None: - out, _err = run_cmd(argparse_app, 'test_argparse_ns') - assert out[0] == 'custom' + out, _err = run_cmd(argparse_app, "test_argparse_ns") + assert out[0] == "custom" def test_argparse_with_list(argparse_app) -> None: - out, _err = run_cmd(argparse_app, 'speak -s hello world!') - assert out == ['HELLO WORLD!'] + out, _err = run_cmd(argparse_app, "speak -s hello world!") + assert out == ["HELLO WORLD!"] def test_argparse_with_list_remove_quotes(argparse_app) -> None: out, _err = run_cmd(argparse_app, 'speak -s hello "world!"') - assert out == ['HELLO WORLD!'] + assert out == ["HELLO WORLD!"] def test_argparse_with_list_preserve_quotes(argparse_app) -> None: @@ -180,62 +180,62 @@ def test_argparse_with_list_preserve_quotes(argparse_app) -> None: def test_argparse_with_list_custom_namespace(argparse_app) -> None: - out, _err = run_cmd(argparse_app, 'test_argparse_with_list_ns') - assert out[0] == 'custom' + out, _err = run_cmd(argparse_app, "test_argparse_with_list_ns") + assert out[0] == "custom" def test_argparse_with_list_and_empty_doc(argparse_app) -> None: - out, _err = run_cmd(argparse_app, 'speak -s hello world!') - assert out == ['HELLO WORLD!'] + out, _err = run_cmd(argparse_app, "speak -s hello world!") + assert out == ["HELLO WORLD!"] def test_argparser_correct_args_with_quotes_and_midline_options(argparse_app) -> None: out, _err = run_cmd(argparse_app, "speak 'This is a' -s test of the emergency broadcast system!") - assert out == ['THIS IS A TEST OF THE EMERGENCY BROADCAST SYSTEM!'] + assert out == ["THIS IS A TEST OF THE EMERGENCY BROADCAST SYSTEM!"] def test_argparser_and_unknown_args_kwargs(argparse_app, capsys) -> None: """Test with_argparser wrapper passing through kwargs to command function""" - argparse_app.do_speak('', keyword_arg="foo") + argparse_app.do_speak("", keyword_arg="foo") out, _err = capsys.readouterr() assert out == "foo\n" def test_argparse_quoted_arguments_multiple(argparse_app) -> None: out, _err = run_cmd(argparse_app, 'say "hello there" "rick & morty"') - assert out == ['hello there rick & morty'] + assert out == ["hello there rick & morty"] def test_argparse_help_docstring(argparse_app) -> None: - out, _err = run_cmd(argparse_app, 'help say') - assert out[0].startswith('Usage: say') - assert out[1] == '' - assert out[2] == 'Repeat what you tell me to.' + out, _err = run_cmd(argparse_app, "help say") + assert out[0].startswith("Usage: say") + assert out[1] == "" + assert out[2] == "Repeat what you tell me to." for line in out: - assert not line.startswith(':') + assert not line.startswith(":") def test_argparse_help_description(argparse_app) -> None: - out, _err = run_cmd(argparse_app, 'help tag') - assert out[0].startswith('Usage: tag') - assert out[1] == '' - assert out[2] == 'create a html tag' + out, _err = run_cmd(argparse_app, "help tag") + assert out[0].startswith("Usage: tag") + assert out[1] == "" + assert out[2] == "create a html tag" def test_argparse_prog(argparse_app) -> None: - out, _err = run_cmd(argparse_app, 'help tag') - progname = out[0].split(' ')[1] - assert progname == 'tag' + out, _err = run_cmd(argparse_app, "help tag") + progname = out[0].split(" ")[1] + assert progname == "tag" def test_arglist(argparse_app) -> None: out, _err = run_cmd(argparse_app, 'arglist "we should" get these') - assert out[0] == 'True' + assert out[0] == "True" def test_arglist_kwargs(argparse_app, capsys) -> None: """Test with_argument_list wrapper passes through kwargs to command function""" - argparse_app.do_arglist('arg', keyword_arg="foo") + argparse_app.do_arglist("arg", keyword_arg="foo") out, _err = capsys.readouterr() assert out == "foo\n" @@ -288,7 +288,7 @@ def __call__(self): builder = NamelessBuilder() # Verify __name__ is actually missing - assert not hasattr(builder, '__name__') + assert not hasattr(builder, "__name__") # The error message should now contain the string representation of the object expected_msg = f"The parser returned by '{builder}' must be a Cmd2ArgumentParser" @@ -299,7 +299,7 @@ def __call__(self): def _build_has_subcmd_parser() -> cmd2.Cmd2ArgumentParser: has_subcmds_parser = cmd2.Cmd2ArgumentParser(description="Tests as_subcmd_to decorator") - has_subcmds_parser.add_subparsers(dest='subcommand', metavar='SUBCOMMAND', required=True) + has_subcmds_parser.add_subparsers(dest="subcommand", metavar="SUBCOMMAND", required=True) return has_subcmds_parser @@ -313,31 +313,31 @@ def base_foo(self, args) -> None: def base_bar(self, args) -> None: """Bar subcommand of base command""" - self.poutput(f'(({args.z}))') + self.poutput(f"(({args.z}))") def base_helpless(self, args) -> None: """Helpless subcommand of base command""" - self.poutput(f'(({args.z}))') + self.poutput(f"(({args.z}))") # create the top-level parser for the base command base_parser = cmd2.Cmd2ArgumentParser() - base_subparsers = base_parser.add_subparsers(dest='subcommand', metavar='SUBCOMMAND', required=True) + base_subparsers = base_parser.add_subparsers(dest="subcommand", metavar="SUBCOMMAND", required=True) # create the parser for the "foo" subcommand - parser_foo = base_subparsers.add_parser('foo', help='foo help') - parser_foo.add_argument('-x', type=int, default=1, help='integer') - parser_foo.add_argument('y', type=float, help='float') + parser_foo = base_subparsers.add_parser("foo", help="foo help") + parser_foo.add_argument("-x", type=int, default=1, help="integer") + parser_foo.add_argument("y", type=float, help="float") parser_foo.set_defaults(func=base_foo) # create the parser for the "bar" subcommand - parser_bar = base_subparsers.add_parser('bar', help='bar help', aliases=['bar_1', 'bar_2']) - parser_bar.add_argument('z', help='string') + parser_bar = base_subparsers.add_parser("bar", help="bar help", aliases=["bar_1", "bar_2"]) + parser_bar.add_argument("z", help="string") parser_bar.set_defaults(func=base_bar) # create the parser for the "helpless" subcommand # This subcommand has aliases and no help text. - parser_helpless = base_subparsers.add_parser('helpless', aliases=['helpless_1', 'helpless_2']) - parser_helpless.add_argument('z', help='string') + parser_helpless = base_subparsers.add_parser("helpless", aliases=["helpless_1", "helpless_2"]) + parser_helpless.add_argument("z", help="string") parser_helpless.set_defaults(func=base_helpless) @cmd2.with_argparser(base_parser) @@ -354,7 +354,7 @@ def do_test_subcmd_decorator(self, args: argparse.Namespace) -> None: subcmd_parser = cmd2.Cmd2ArgumentParser(description="A subcommand") - @cmd2.as_subcommand_to('test_subcmd_decorator', 'subcmd', subcmd_parser, help=subcmd_parser.description.lower()) + @cmd2.as_subcommand_to("test_subcmd_decorator", "subcmd", subcmd_parser, help=subcmd_parser.description.lower()) def subcmd_func(self, args: argparse.Namespace) -> None: # Make sure printing the Namespace works. The way we originally added cmd2_handler to it resulted in a RecursionError. self.poutput(args) @@ -362,7 +362,7 @@ def subcmd_func(self, args: argparse.Namespace) -> None: helpless_subcmd_parser = cmd2.Cmd2ArgumentParser(add_help=False, description="A subcommand with no help") @cmd2.as_subcommand_to( - 'test_subcmd_decorator', 'helpless_subcmd', helpless_subcmd_parser, help=helpless_subcmd_parser.description.lower() + "test_subcmd_decorator", "helpless_subcmd", helpless_subcmd_parser, help=helpless_subcmd_parser.description.lower() ) def helpless_subcmd_func(self, args: argparse.Namespace) -> None: # Make sure vars(Namespace) works. The way we originally added cmd2_handler to it resulted in a RecursionError. @@ -375,96 +375,96 @@ def subcommand_app(): def test_subcommand_foo(subcommand_app) -> None: - out, _err = run_cmd(subcommand_app, 'base foo -x2 5.0') - assert out == ['10.0'] + out, _err = run_cmd(subcommand_app, "base foo -x2 5.0") + assert out == ["10.0"] def test_subcommand_bar(subcommand_app) -> None: - out, _err = run_cmd(subcommand_app, 'base bar baz') - assert out == ['((baz))'] + out, _err = run_cmd(subcommand_app, "base bar baz") + assert out == ["((baz))"] def test_subcommand_invalid(subcommand_app) -> None: - _out, err = run_cmd(subcommand_app, 'base baz') - assert err[0].startswith('Usage: base') + _out, err = run_cmd(subcommand_app, "base baz") + assert err[0].startswith("Usage: base") assert err[1].startswith("Error: argument SUBCOMMAND: invalid choice: 'baz'") def test_subcommand_base_help(subcommand_app) -> None: - out, _err = run_cmd(subcommand_app, 'help base') - assert out[0].startswith('Usage: base') - assert out[1] == '' - assert out[2] == 'Base command help' + out, _err = run_cmd(subcommand_app, "help base") + assert out[0].startswith("Usage: base") + assert out[1] == "" + assert out[2] == "Base command help" def test_subcommand_help(subcommand_app) -> None: # foo has no aliases - out, _err = run_cmd(subcommand_app, 'help base foo') - assert out[0].startswith('Usage: base foo') - assert out[1] == '' - assert out[2] == 'Positional Arguments:' + out, _err = run_cmd(subcommand_app, "help base foo") + assert out[0].startswith("Usage: base foo") + assert out[1] == "" + assert out[2] == "Positional Arguments:" # bar has aliases (usage should never show alias name) - out, _err = run_cmd(subcommand_app, 'help base bar') - assert out[0].startswith('Usage: base bar') - assert out[1] == '' - assert out[2] == 'Positional Arguments:' + out, _err = run_cmd(subcommand_app, "help base bar") + assert out[0].startswith("Usage: base bar") + assert out[1] == "" + assert out[2] == "Positional Arguments:" - out, _err = run_cmd(subcommand_app, 'help base bar_1') - assert out[0].startswith('Usage: base bar') - assert out[1] == '' - assert out[2] == 'Positional Arguments:' + out, _err = run_cmd(subcommand_app, "help base bar_1") + assert out[0].startswith("Usage: base bar") + assert out[1] == "" + assert out[2] == "Positional Arguments:" - out, _err = run_cmd(subcommand_app, 'help base bar_2') - assert out[0].startswith('Usage: base bar') - assert out[1] == '' - assert out[2] == 'Positional Arguments:' + out, _err = run_cmd(subcommand_app, "help base bar_2") + assert out[0].startswith("Usage: base bar") + assert out[1] == "" + assert out[2] == "Positional Arguments:" # helpless has aliases and no help text (usage should never show alias name) - out, _err = run_cmd(subcommand_app, 'help base helpless') - assert out[0].startswith('Usage: base helpless') - assert out[1] == '' - assert out[2] == 'Positional Arguments:' + out, _err = run_cmd(subcommand_app, "help base helpless") + assert out[0].startswith("Usage: base helpless") + assert out[1] == "" + assert out[2] == "Positional Arguments:" - out, _err = run_cmd(subcommand_app, 'help base helpless_1') - assert out[0].startswith('Usage: base helpless') - assert out[1] == '' - assert out[2] == 'Positional Arguments:' + out, _err = run_cmd(subcommand_app, "help base helpless_1") + assert out[0].startswith("Usage: base helpless") + assert out[1] == "" + assert out[2] == "Positional Arguments:" - out, _err = run_cmd(subcommand_app, 'help base helpless_2') - assert out[0].startswith('Usage: base helpless') - assert out[1] == '' - assert out[2] == 'Positional Arguments:' + out, _err = run_cmd(subcommand_app, "help base helpless_2") + assert out[0].startswith("Usage: base helpless") + assert out[1] == "" + assert out[2] == "Positional Arguments:" def test_subcommand_invalid_help(subcommand_app) -> None: - out, _err = run_cmd(subcommand_app, 'help base baz') - assert out[0].startswith('Usage: base') + out, _err = run_cmd(subcommand_app, "help base baz") + assert out[0].startswith("Usage: base") def test_subcmd_decorator(subcommand_app) -> None: # Test subcommand that has help option - out, err = run_cmd(subcommand_app, 'test_subcmd_decorator subcmd') - assert out[0].startswith('Namespace(') + out, err = run_cmd(subcommand_app, "test_subcmd_decorator subcmd") + assert out[0].startswith("Namespace(") - out, err = run_cmd(subcommand_app, 'help test_subcmd_decorator subcmd') - assert out[0] == 'Usage: test_subcmd_decorator subcmd [-h]' + out, err = run_cmd(subcommand_app, "help test_subcmd_decorator subcmd") + assert out[0] == "Usage: test_subcmd_decorator subcmd [-h]" - out, err = run_cmd(subcommand_app, 'test_subcmd_decorator subcmd -h') - assert out[0] == 'Usage: test_subcmd_decorator subcmd [-h]' + out, err = run_cmd(subcommand_app, "test_subcmd_decorator subcmd -h") + assert out[0] == "Usage: test_subcmd_decorator subcmd [-h]" # Test subcommand that has no help option - out, err = run_cmd(subcommand_app, 'test_subcmd_decorator helpless_subcmd') + out, err = run_cmd(subcommand_app, "test_subcmd_decorator helpless_subcmd") assert "'subcommand': 'helpless_subcmd'" in out[1] - out, err = run_cmd(subcommand_app, 'help test_subcmd_decorator helpless_subcmd') - assert out[0] == 'Usage: test_subcmd_decorator helpless_subcmd' + out, err = run_cmd(subcommand_app, "help test_subcmd_decorator helpless_subcmd") + assert out[0] == "Usage: test_subcmd_decorator helpless_subcmd" assert not err - out, err = run_cmd(subcommand_app, 'test_subcmd_decorator helpless_subcmd -h') + out, err = run_cmd(subcommand_app, "test_subcmd_decorator helpless_subcmd -h") assert not out - assert err[0] == 'Usage: test_subcmd_decorator [-h] SUBCOMMAND ...' - assert err[1] == 'Error: unrecognized arguments: -h' + assert err[0] == "Usage: test_subcmd_decorator [-h] SUBCOMMAND ..." + assert err[1] == "Error: unrecognized arguments: -h" def test_unittest_mock() -> None: @@ -476,16 +476,16 @@ def test_unittest_mock() -> None: CommandSetRegistrationError, ) - with mock.patch.object(ArgparseApp, 'namespace_provider'), pytest.raises(CommandSetRegistrationError): + with mock.patch.object(ArgparseApp, "namespace_provider"), pytest.raises(CommandSetRegistrationError): ArgparseApp() - with mock.patch.object(ArgparseApp, 'namespace_provider', spec=True): + with mock.patch.object(ArgparseApp, "namespace_provider", spec=True): ArgparseApp() - with mock.patch.object(ArgparseApp, 'namespace_provider', spec_set=True): + with mock.patch.object(ArgparseApp, "namespace_provider", spec_set=True): ArgparseApp() - with mock.patch.object(ArgparseApp, 'namespace_provider', autospec=True): + with mock.patch.object(ArgparseApp, "namespace_provider", autospec=True): ArgparseApp() @@ -494,19 +494,19 @@ def test_pytest_mock_invalid(mocker) -> None: CommandSetRegistrationError, ) - mocker.patch.object(ArgparseApp, 'namespace_provider') + mocker.patch.object(ArgparseApp, "namespace_provider") with pytest.raises(CommandSetRegistrationError): ArgparseApp() @pytest.mark.parametrize( - 'spec_param', + "spec_param", [ - {'spec': True}, - {'spec_set': True}, - {'autospec': True}, + {"spec": True}, + {"spec_set": True}, + {"autospec": True}, ], ) def test_pytest_mock_valid(mocker, spec_param) -> None: - mocker.patch.object(ArgparseApp, 'namespace_provider', **spec_param) + mocker.patch.object(ArgparseApp, "namespace_provider", **spec_param) ArgparseApp() diff --git a/tests/test_argparse_completer.py b/tests/test_argparse_completer.py index eb33e0776..b15ada148 100644 --- a/tests/test_argparse_completer.py +++ b/tests/test_argparse_completer.py @@ -26,8 +26,8 @@ ) # Data and functions for testing standalone choice_provider and completer -standalone_choices = ['standalone', 'provider'] -standalone_completions = ['standalone', 'completer'] +standalone_choices = ["standalone", "provider"] +standalone_completions = ["standalone", "completer"] def standalone_choice_provider(cli: cmd2.Cmd) -> Choices: @@ -48,16 +48,16 @@ def __init__(self, *args, **kwargs) -> None: # Begin code related to help and command name completion ############################################################################################################ # Top level parser for music command - music_parser = Cmd2ArgumentParser(description='Manage music') + music_parser = Cmd2ArgumentParser(description="Manage music") # Add subcommands to music music_subparsers = music_parser.add_subparsers() - music_create_parser = music_subparsers.add_parser('create', help='create music') + music_create_parser = music_subparsers.add_parser("create", help="create music") # Add subcommands to music -> create music_create_subparsers = music_create_parser.add_subparsers() - music_create_jazz_parser = music_create_subparsers.add_parser('jazz', help='create jazz') - music_create_rock_parser = music_create_subparsers.add_parser('rock', help='create rock') + music_create_jazz_parser = music_create_subparsers.add_parser("jazz", help="create jazz") + music_create_rock_parser = music_create_subparsers.add_parser("rock", help="create rock") @with_argparser(music_parser) def do_music(self, args: argparse.Namespace) -> None: @@ -69,23 +69,23 @@ def do_music(self, args: argparse.Namespace) -> None: # Uses default flag prefix value (-) flag_parser = Cmd2ArgumentParser() - flag_parser.add_argument('-n', '--normal_flag', help='a normal flag', action='store_true') - flag_parser.add_argument('-a', '--append_flag', help='append flag', action='append') - flag_parser.add_argument('-o', '--append_const_flag', help='append const flag', action='append_const', const=True) - flag_parser.add_argument('-c', '--count_flag', help='count flag', action='count') - flag_parser.add_argument('-e', '--extend_flag', help='extend flag', action='extend') - flag_parser.add_argument('-s', '--suppressed_flag', help=argparse.SUPPRESS, action='store_true') - flag_parser.add_argument('-r', '--remainder_flag', nargs=argparse.REMAINDER, help='a remainder flag') - flag_parser.add_argument('-q', '--required_flag', required=True, help='a required flag', action='store_true') + flag_parser.add_argument("-n", "--normal_flag", help="a normal flag", action="store_true") + flag_parser.add_argument("-a", "--append_flag", help="append flag", action="append") + flag_parser.add_argument("-o", "--append_const_flag", help="append const flag", action="append_const", const=True) + flag_parser.add_argument("-c", "--count_flag", help="count flag", action="count") + flag_parser.add_argument("-e", "--extend_flag", help="extend flag", action="extend") + flag_parser.add_argument("-s", "--suppressed_flag", help=argparse.SUPPRESS, action="store_true") + flag_parser.add_argument("-r", "--remainder_flag", nargs=argparse.REMAINDER, help="a remainder flag") + flag_parser.add_argument("-q", "--required_flag", required=True, help="a required flag", action="store_true") @with_argparser(flag_parser) def do_flag(self, args: argparse.Namespace) -> None: pass # Uses non-default flag prefix value (+) - plus_flag_parser = Cmd2ArgumentParser(prefix_chars='+') - plus_flag_parser.add_argument('+n', '++normal_flag', help='a normal flag', action='store_true') - plus_flag_parser.add_argument('+q', '++required_flag', required=True, help='a required flag', action='store_true') + plus_flag_parser = Cmd2ArgumentParser(prefix_chars="+") + plus_flag_parser.add_argument("+n", "++normal_flag", help="a normal flag", action="store_true") + plus_flag_parser.add_argument("+q", "++required_flag", required=True, help="a required flag", action="store_true") @with_argparser(plus_flag_parser) def do_plus_flag(self, args: argparse.Namespace) -> None: @@ -94,7 +94,7 @@ def do_plus_flag(self, args: argparse.Namespace) -> None: # A parser with a positional and flags. Used to test that remaining flag names are completed when all positionals are done. pos_and_flag_parser = Cmd2ArgumentParser() pos_and_flag_parser.add_argument("positional", choices=["a", "choice"]) - pos_and_flag_parser.add_argument("-f", "--flag", action='store_true') + pos_and_flag_parser.add_argument("-f", "--flag", action="store_true") @with_argparser(pos_and_flag_parser) def do_pos_and_flag(self, args: argparse.Namespace) -> None: @@ -104,18 +104,18 @@ def do_pos_and_flag(self, args: argparse.Namespace) -> None: # Begin code related to testing choices and choices_provider parameters ############################################################################################################ STR_METAVAR = "HEADLESS" - TUPLE_METAVAR = ('arg1', 'others') + TUPLE_METAVAR = ("arg1", "others") DESCRIPTION_TABLE_COLUMNS = ("Description",) # tuples (for sake of immutability) used in our tests (there is a mix of sorted and unsorted on purpose) non_negative_num_choices = (1, 2, 3, 0.5, 22) num_choices = (-1, 1, -2, 2.5, 0, -12) - static_choices_list = ('static', 'choices', 'stop', 'here') - choices_from_provider = ('choices', 'provider', 'probably', 'improved') + static_choices_list = ("static", "choices", "stop", "here") + choices_from_provider = ("choices", "provider", "probably", "improved") completion_item_choices = ( - CompletionItem('choice_1', table_data=['Description 1']), - CompletionItem('choice_2', table_data=[su.stylize("String with style", style=cmd2.Color.BLUE)]), - CompletionItem('choice_3', table_data=[Text("Text with style", style=cmd2.Color.RED)]), + CompletionItem("choice_1", table_data=["Description 1"]), + CompletionItem("choice_2", table_data=[su.stylize("String with style", style=cmd2.Color.BLUE)]), + CompletionItem("choice_3", table_data=[Text("Text with style", style=cmd2.Color.RED)]), ) # This tests that CompletionItems created with numerical values are sorted as numbers. @@ -133,8 +133,8 @@ def completion_item_method(self) -> list[CompletionItem]: """Choices method that returns CompletionItems""" items = [] for i in range(10): - main_str = f'main_str{i}' - items.append(CompletionItem(main_str, table_data=['blah blah'])) + main_str = f"main_str{i}" + items.append(CompletionItem(main_str, table_data=["blah blah"])) return items choices_parser = Cmd2ArgumentParser() @@ -154,42 +154,42 @@ def completion_item_method(self) -> list[CompletionItem]: ) choices_parser.add_argument( "--no_metavar", - help='this arg has no metavar', + help="this arg has no metavar", choices_provider=completion_item_method, table_columns=DESCRIPTION_TABLE_COLUMNS, ) choices_parser.add_argument( "--str_metavar", - help='this arg has str for a metavar', + help="this arg has str for a metavar", choices_provider=completion_item_method, metavar=STR_METAVAR, table_columns=DESCRIPTION_TABLE_COLUMNS, ) choices_parser.add_argument( - '-t', + "-t", "--tuple_metavar", - help='this arg has tuple for a metavar', + help="this arg has tuple for a metavar", metavar=TUPLE_METAVAR, nargs=argparse.ONE_OR_MORE, choices_provider=completion_item_method, table_columns=DESCRIPTION_TABLE_COLUMNS, ) choices_parser.add_argument( - '-n', - '--num', + "-n", + "--num", type=int, - help='a flag with an int type', + help="a flag with an int type", choices=num_choices, ) choices_parser.add_argument( - '--completion_items', - help='choices are CompletionItems', + "--completion_items", + help="choices are CompletionItems", choices=completion_item_choices, table_columns=DESCRIPTION_TABLE_COLUMNS, ) choices_parser.add_argument( - '--num_completion_items', - help='choices are numerical CompletionItems', + "--num_completion_items", + help="choices are numerical CompletionItems", choices=num_completion_items, table_columns=DESCRIPTION_TABLE_COLUMNS, ) @@ -206,14 +206,14 @@ def completion_item_method(self) -> list[CompletionItem]: choices_provider=choices_provider, ) choices_parser.add_argument( - 'non_negative_num', + "non_negative_num", type=int, - help='a positional with non-negative numerical choices', + help="a positional with non-negative numerical choices", choices=non_negative_num_choices, ) choices_parser.add_argument( - 'empty_choices', - help='a positional with empty choices', + "empty_choices", + help="a positional with empty choices", choices=[], ) @@ -224,9 +224,9 @@ def do_choices(self, args: argparse.Namespace) -> None: ############################################################################################################ # Begin code related to testing completer parameter ############################################################################################################ - completions_for_flag = ('completions', 'flag', 'fairly', 'complete') - completions_for_pos_1 = ('completions', 'positional_1', 'probably', 'missed', 'spot') - completions_for_pos_2 = ('completions', 'positional_2', 'probably', 'missed', 'me') + completions_for_flag = ("completions", "flag", "fairly", "complete") + completions_for_pos_1 = ("completions", "positional_1", "probably", "missed", "spot") + completions_for_pos_2 = ("completions", "positional_2", "probably", "missed", "me") def flag_completer(self, text: str, line: str, begidx: int, endidx: int) -> Completions: return self.basic_complete(text, line, begidx, endidx, self.completions_for_flag) @@ -253,12 +253,12 @@ def do_completer(self, args: argparse.Namespace) -> None: ############################################################################################################ # Begin code related to nargs ############################################################################################################ - set_value_choices = ('set', 'value', 'choices') - one_or_more_choices = ('one', 'or', 'more', 'choices') - optional_choices = ('a', 'few', 'optional', 'choices') - range_choices = ('some', 'range', 'choices') - remainder_choices = ('remainder', 'choices') - positional_choices = ('the', 'positional', 'choices') + set_value_choices = ("set", "value", "choices") + one_or_more_choices = ("one", "or", "more", "choices") + optional_choices = ("a", "few", "optional", "choices") + range_choices = ("some", "range", "choices") + remainder_choices = ("remainder", "choices") + positional_choices = ("the", "positional", "choices") nargs_parser = Cmd2ArgumentParser() @@ -288,12 +288,12 @@ def do_nargs(self, args: argparse.Namespace) -> None: # Begin code related to testing tab hints ############################################################################################################ hint_parser = Cmd2ArgumentParser() - hint_parser.add_argument('-f', '--flag', help='a flag arg') - hint_parser.add_argument('-s', '--suppressed_help', help=argparse.SUPPRESS) - hint_parser.add_argument('-t', '--suppressed_hint', help='a flag arg', suppress_tab_hint=True) + hint_parser.add_argument("-f", "--flag", help="a flag arg") + hint_parser.add_argument("-s", "--suppressed_help", help=argparse.SUPPRESS) + hint_parser.add_argument("-t", "--suppressed_hint", help="a flag arg", suppress_tab_hint=True) - hint_parser.add_argument('hint_pos', help='here is a hint\nwith new lines') - hint_parser.add_argument('no_help_pos') + hint_parser.add_argument("hint_pos", help="here is a hint\nwith new lines") + hint_parser.add_argument("no_help_pos") @with_argparser(hint_parser) def do_hint(self, args: argparse.Namespace) -> None: @@ -304,15 +304,15 @@ def do_hint(self, args: argparse.Namespace) -> None: ############################################################################################################ def completer_raise_error(self, text: str, line: str, begidx: int, endidx: int) -> list[str]: """Raises CompletionError""" - raise CompletionError('completer broke something') + raise CompletionError("completer broke something") def choice_raise_completion_error(self) -> list[str]: """Raises CompletionError""" - raise CompletionError('choice broke something') + 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_completion_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_completion_error) @with_argparser(comp_error_parser) def do_raise_completion_error(self, args: argparse.Namespace) -> None: @@ -323,27 +323,27 @@ def do_raise_completion_error(self, args: argparse.Namespace) -> None: ############################################################################################################ def choices_takes_arg_tokens(self, arg_tokens: dict[str, list[str]]) -> Choices: """Choices function that receives arg_tokens from ArgparseCompleter""" - return Choices.from_values([arg_tokens['parent_arg'][0], arg_tokens['subcommand'][0]]) + return Choices.from_values([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: dict[str, list[str]] ) -> Completions: """Completer function that receives arg_tokens from ArgparseCompleter""" - match_against = [arg_tokens['parent_arg'][0], arg_tokens['subcommand'][0]] + match_against = [arg_tokens["parent_arg"][0], arg_tokens["subcommand"][0]] return self.basic_complete(text, line, begidx, endidx, match_against) arg_tokens_parser = Cmd2ArgumentParser() - arg_tokens_parser.add_argument('parent_arg', help='arg from a parent parser') + arg_tokens_parser.add_argument("parent_arg", help="arg from a parent parser") # Create a subcommand to exercise receiving parent_tokens and subcommand name in arg_tokens - arg_tokens_subparser = arg_tokens_parser.add_subparsers(dest='subcommand') - arg_tokens_subcmd_parser = arg_tokens_subparser.add_parser('subcmd') + arg_tokens_subparser = arg_tokens_parser.add_subparsers(dest="subcommand") + arg_tokens_subcmd_parser = arg_tokens_subparser.add_parser("subcmd") - arg_tokens_subcmd_parser.add_argument('choices_pos', choices_provider=choices_takes_arg_tokens) - arg_tokens_subcmd_parser.add_argument('completer_pos', completer=completer_takes_arg_tokens) + arg_tokens_subcmd_parser.add_argument("choices_pos", choices_provider=choices_takes_arg_tokens) + arg_tokens_subcmd_parser.add_argument("completer_pos", completer=completer_takes_arg_tokens) # Used to override parent_arg in arg_tokens_parser - arg_tokens_subcmd_parser.add_argument('--parent_arg') + arg_tokens_subcmd_parser.add_argument("--parent_arg") @with_argparser(arg_tokens_parser) def do_arg_tokens(self, args: argparse.Namespace) -> None: @@ -355,11 +355,11 @@ def do_arg_tokens(self, args: argparse.Namespace) -> None: mutex_parser = Cmd2ArgumentParser() mutex_group = mutex_parser.add_mutually_exclusive_group(required=True) - mutex_group.add_argument('optional_pos', help='the optional positional', nargs=argparse.OPTIONAL) - mutex_group.add_argument('-f', '--flag', help='the flag arg') - mutex_group.add_argument('-o', '--other_flag', help='the other flag arg') + mutex_group.add_argument("optional_pos", help="the optional positional", nargs=argparse.OPTIONAL) + mutex_group.add_argument("-f", "--flag", help="the flag arg") + mutex_group.add_argument("-o", "--other_flag", help="the other flag arg") - mutex_parser.add_argument('last_arg', help='the last arg') + mutex_parser.add_argument("last_arg", help="the last arg") @with_argparser(mutex_parser) def do_mutex(self, args: argparse.Namespace) -> None: @@ -369,8 +369,8 @@ def do_mutex(self, args: argparse.Namespace) -> None: # Begin code related to standalone functions ############################################################################################################ standalone_parser = Cmd2ArgumentParser() - standalone_parser.add_argument('--provider', help='standalone provider', choices_provider=standalone_choice_provider) - standalone_parser.add_argument('--completer', help='standalone completer', completer=standalone_completer) + standalone_parser.add_argument("--provider", help="standalone provider", choices_provider=standalone_choice_provider) + standalone_parser.add_argument("--completer", help="standalone completer", completer=standalone_completer) @with_argparser(standalone_parser) def do_standalone(self, args: argparse.Namespace) -> None: @@ -385,12 +385,12 @@ def do_standalone(self, args: argparse.Namespace) -> None: meta_subparsers = meta_parser.add_subparsers() # Create subcommands with and without help text - meta_helpful_parser = meta_subparsers.add_parser('helpful', help='my helpful text') - meta_helpless_parser = meta_subparsers.add_parser('helpless') + meta_helpful_parser = meta_subparsers.add_parser("helpful", help="my helpful text") + meta_helpless_parser = meta_subparsers.add_parser("helpless") # Create flags with and without help text - meta_helpful_parser.add_argument('--helpful_flag', help="a helpful flag") - meta_helpless_parser.add_argument('--helpless_flag') + meta_helpful_parser.add_argument("--helpful_flag", help="a helpful flag") + meta_helpless_parser.add_argument("--helpless_flag") @with_argparser(meta_parser) def do_meta(self, args: argparse.Namespace) -> None: @@ -402,36 +402,36 @@ def ac_app() -> ArgparseCompleterTester: return ArgparseCompleterTester() -@pytest.mark.parametrize('command', ['music', 'music create', 'music create rock', 'music create jazz']) +@pytest.mark.parametrize("command", ["music", "music create", "music create rock", "music create jazz"]) def test_help(ac_app, command) -> None: - out1, _err1 = run_cmd(ac_app, f'{command} -h') - out2, _err2 = run_cmd(ac_app, f'help {command}') + out1, _err1 = run_cmd(ac_app, f"{command} -h") + out2, _err2 = run_cmd(ac_app, f"help {command}") assert out1 == out2 def test_bad_subcommand_help(ac_app) -> None: # These should give the same output because the second one isn't using a # real subcommand, so help will be called on the music command instead. - out1, _err1 = run_cmd(ac_app, 'help music') - out2, _err2 = run_cmd(ac_app, 'help music fake') + out1, _err1 = run_cmd(ac_app, "help music") + out2, _err2 = run_cmd(ac_app, "help music fake") assert out1 == out2 @pytest.mark.parametrize( - ('command', 'text', 'expected'), + ("command", "text", "expected"), [ - ('', 'mus', ['music']), - ('music', 'cre', ['create']), - ('music', 'creab', []), - ('music create', '', ['jazz', 'rock']), - ('music crea', 'jazz', []), - ('music create', 'foo', []), - ('fake create', '', []), - ('music fake', '', []), + ("", "mus", ["music"]), + ("music", "cre", ["create"]), + ("music", "creab", []), + ("music create", "", ["jazz", "rock"]), + ("music crea", "jazz", []), + ("music create", "foo", []), + ("fake create", "", []), + ("music fake", "", []), ], ) def test_complete_help(ac_app, command, text, expected) -> None: - line = f'help {command} {text}' + line = f"help {command} {text}" endidx = len(line) begidx = endidx - len(text) @@ -440,16 +440,16 @@ def test_complete_help(ac_app, command, text, expected) -> None: @pytest.mark.parametrize( - ('subcommand', 'text', 'expected'), + ("subcommand", "text", "expected"), [ - ('create', '', ['jazz', 'rock']), - ('create', 'ja', ['jazz']), - ('create', 'foo', []), - ('creab', 'ja', []), + ("create", "", ["jazz", "rock"]), + ("create", "ja", ["jazz"]), + ("create", "foo", []), + ("creab", "ja", []), ], ) def test_subcommand_completions(ac_app, subcommand, text, expected) -> None: - line = f'music {subcommand} {text}' + line = f"music {subcommand} {text}" endidx = len(line) begidx = endidx - len(text) @@ -459,16 +459,16 @@ def test_subcommand_completions(ac_app, subcommand, text, expected) -> None: @pytest.mark.parametrize( # expected_data is a list of tuples with completion text and display values - ('command_and_args', 'text', 'expected_data'), + ("command_and_args", "text", "expected_data"), [ # Complete all flags (suppressed will not show) ( - 'flag', - '-', + "flag", + "-", [ ("-a", "[-a, --append_flag]"), ("-c", "[-c, --count_flag]"), - ('-e', '[-e, --extend_flag]'), + ("-e", "[-e, --extend_flag]"), ("-h", "[-h, --help]"), ("-n", "[-n, --normal_flag]"), ("-o", "[-o, --append_const_flag]"), @@ -477,95 +477,95 @@ def test_subcommand_completions(ac_app, subcommand, text, expected) -> None: ], ), ( - 'flag', - '--', + "flag", + "--", [ - ('--append_const_flag', '[--append_const_flag]'), - ('--append_flag', '[--append_flag]'), - ('--count_flag', '[--count_flag]'), - ('--extend_flag', '[--extend_flag]'), - ('--help', '[--help]'), - ('--normal_flag', '[--normal_flag]'), - ('--remainder_flag', '[--remainder_flag]'), - ('--required_flag', '--required_flag'), + ("--append_const_flag", "[--append_const_flag]"), + ("--append_flag", "[--append_flag]"), + ("--count_flag", "[--count_flag]"), + ("--extend_flag", "[--extend_flag]"), + ("--help", "[--help]"), + ("--normal_flag", "[--normal_flag]"), + ("--remainder_flag", "[--remainder_flag]"), + ("--required_flag", "--required_flag"), ], ), # Complete individual flag - ('flag', '-n', [('-n', '[-n]')]), - ('flag', '--n', [('--normal_flag', '[--normal_flag]')]), + ("flag", "-n", [("-n", "[-n]")]), + ("flag", "--n", [("--normal_flag", "[--normal_flag]")]), # No flags should complete until current flag has its args - ('flag --append_flag', '-', []), + ("flag --append_flag", "-", []), # Complete REMAINDER flag name - ('flag', '-r', [('-r', '[-r]')]), - ('flag', '--rem', [('--remainder_flag', '[--remainder_flag]')]), + ("flag", "-r", [("-r", "[-r]")]), + ("flag", "--rem", [("--remainder_flag", "[--remainder_flag]")]), # No flags after a REMAINDER should complete - ('flag -r value', '-', []), - ('flag --remainder_flag value', '--', []), + ("flag -r value", "-", []), + ("flag --remainder_flag value", "--", []), # Suppressed flag should not complete - ('flag', '-s', []), - ('flag', '--s', []), + ("flag", "-s", []), + ("flag", "--s", []), # A used flag should not show in completions ( - 'flag -n', - '--', + "flag -n", + "--", [ - ('--append_const_flag', '[--append_const_flag]'), - ('--append_flag', '[--append_flag]'), - ('--count_flag', '[--count_flag]'), - ('--extend_flag', '[--extend_flag]'), - ('--help', '[--help]'), - ('--remainder_flag', '[--remainder_flag]'), - ('--required_flag', '--required_flag'), + ("--append_const_flag", "[--append_const_flag]"), + ("--append_flag", "[--append_flag]"), + ("--count_flag", "[--count_flag]"), + ("--extend_flag", "[--extend_flag]"), + ("--help", "[--help]"), + ("--remainder_flag", "[--remainder_flag]"), + ("--required_flag", "--required_flag"), ], ), # Flags with actions set to append, append_const, extend, and count will always show even if they've been used ( - 'flag --append_flag value --append_const_flag --count_flag --extend_flag value', - '--', + "flag --append_flag value --append_const_flag --count_flag --extend_flag value", + "--", [ - ('--append_const_flag', '[--append_const_flag]'), - ('--append_flag', '[--append_flag]'), - ('--count_flag', '[--count_flag]'), - ('--extend_flag', '[--extend_flag]'), - ('--help', '[--help]'), - ('--normal_flag', '[--normal_flag]'), - ('--remainder_flag', '[--remainder_flag]'), - ('--required_flag', '--required_flag'), + ("--append_const_flag", "[--append_const_flag]"), + ("--append_flag", "[--append_flag]"), + ("--count_flag", "[--count_flag]"), + ("--extend_flag", "[--extend_flag]"), + ("--help", "[--help]"), + ("--normal_flag", "[--normal_flag]"), + ("--remainder_flag", "[--remainder_flag]"), + ("--required_flag", "--required_flag"), ], ), # Non-default flag prefix character (+) ( - 'plus_flag', - '+', + "plus_flag", + "+", [ - ('+h', '[+h, ++help]'), - ('+n', '[+n, ++normal_flag]'), - ('+q', '+q, ++required_flag'), + ("+h", "[+h, ++help]"), + ("+n", "[+n, ++normal_flag]"), + ("+q", "+q, ++required_flag"), ], ), ( - 'plus_flag', - '++', + "plus_flag", + "++", [ - ('++help', '[++help]'), - ('++normal_flag', '[++normal_flag]'), - ('++required_flag', '++required_flag'), + ("++help", "[++help]"), + ("++normal_flag", "[++normal_flag]"), + ("++required_flag", "++required_flag"), ], ), # Flag completion should not occur after '--' since that tells argparse all remaining arguments are non-flags - ('flag --', '--', []), - ('flag --help --', '--', []), - ('plus_flag --', '++', []), - ('plus_flag ++help --', '++', []), + ("flag --", "--", []), + ("flag --help --", "--", []), + ("plus_flag --", "++", []), + ("plus_flag ++help --", "++", []), # Test remaining flag names complete after all positionals are complete - ('pos_and_flag', '', [('a', 'a'), ('choice', 'choice')]), - ('pos_and_flag choice ', '', [('-f', '[-f, --flag]'), ('-h', '[-h, --help]')]), - ('pos_and_flag choice -f ', '', [('-h', '[-h, --help]')]), - ('pos_and_flag choice -f -h ', '', []), + ("pos_and_flag", "", [("a", "a"), ("choice", "choice")]), + ("pos_and_flag choice ", "", [("-f", "[-f, --flag]"), ("-h", "[-h, --help]")]), + ("pos_and_flag choice -f ", "", [("-h", "[-h, --help]")]), + ("pos_and_flag choice -f -h ", "", []), ], ) def test_autcomp_flag_completion(ac_app, command_and_args, text, expected_data) -> None: - line = f'{command_and_args} {text}' + line = f"{command_and_args} {text}" endidx = len(line) begidx = endidx - len(text) @@ -577,21 +577,21 @@ def test_autcomp_flag_completion(ac_app, command_and_args, text, expected_data) @pytest.mark.parametrize( - ('flag', 'text', 'expected'), + ("flag", "text", "expected"), [ - ('-l', '', ArgparseCompleterTester.static_choices_list), - ('--list', 's', ['static', 'stop']), - ('-p', '', ArgparseCompleterTester.choices_from_provider), - ('--provider', 'pr', ['provider', 'probably']), - ('-n', '', ArgparseCompleterTester.num_choices), - ('--num', '1', ['1']), - ('--num', '-', [-1, -2, -12]), - ('--num', '-1', [-1, -12]), - ('--num_completion_items', '', ArgparseCompleterTester.num_completion_items), + ("-l", "", ArgparseCompleterTester.static_choices_list), + ("--list", "s", ["static", "stop"]), + ("-p", "", ArgparseCompleterTester.choices_from_provider), + ("--provider", "pr", ["provider", "probably"]), + ("-n", "", ArgparseCompleterTester.num_choices), + ("--num", "1", ["1"]), + ("--num", "-", [-1, -2, -12]), + ("--num", "-1", [-1, -12]), + ("--num_completion_items", "", ArgparseCompleterTester.num_completion_items), ], ) def test_autocomp_flag_choices_completion(ac_app, flag, text, expected) -> None: - line = f'choices {flag} {text}' + line = f"choices {flag} {text}" endidx = len(line) begidx = endidx - len(text) @@ -600,20 +600,20 @@ def test_autocomp_flag_choices_completion(ac_app, flag, text, expected) -> None: @pytest.mark.parametrize( - ('pos', 'text', 'expected'), + ("pos", "text", "expected"), [ - (1, '', ArgparseCompleterTester.static_choices_list), - (1, 's', ['static', 'stop']), - (2, '', ArgparseCompleterTester.choices_from_provider), - (2, 'pr', ['provider', 'probably']), - (3, '', ArgparseCompleterTester.non_negative_num_choices), - (3, '2', [2, 22]), - (4, '', []), + (1, "", ArgparseCompleterTester.static_choices_list), + (1, "s", ["static", "stop"]), + (2, "", ArgparseCompleterTester.choices_from_provider), + (2, "pr", ["provider", "probably"]), + (3, "", ArgparseCompleterTester.non_negative_num_choices), + (3, "2", [2, 22]), + (4, "", []), ], ) def test_autocomp_positional_choices_completion(ac_app, pos, text, expected) -> None: # Generate line where preceding positionals are already filled - line = 'choices {} {}'.format('foo ' * (pos - 1), text) + line = "choices {} {}".format("foo " * (pos - 1), text) endidx = len(line) begidx = endidx - len(text) @@ -622,14 +622,14 @@ def test_autocomp_positional_choices_completion(ac_app, pos, text, expected) -> @pytest.mark.parametrize( - ('flag', 'text', 'expected'), + ("flag", "text", "expected"), [ - ('-c', '', ArgparseCompleterTester.completions_for_flag), - ('--completer', 'f', ['flag', 'fairly']), + ("-c", "", ArgparseCompleterTester.completions_for_flag), + ("--completer", "f", ["flag", "fairly"]), ], ) def test_autocomp_flag_completers(ac_app, flag, text, expected) -> None: - line = f'completer {flag} {text}' + line = f"completer {flag} {text}" endidx = len(line) begidx = endidx - len(text) @@ -638,17 +638,17 @@ def test_autocomp_flag_completers(ac_app, flag, text, expected) -> None: @pytest.mark.parametrize( - ('pos', 'text', 'expected'), + ("pos", "text", "expected"), [ - (1, '', ArgparseCompleterTester.completions_for_pos_1), - (1, 'p', ['positional_1', 'probably']), - (2, '', ArgparseCompleterTester.completions_for_pos_2), - (2, 'm', ['missed', 'me']), + (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, expected) -> None: # Generate line were preceding positionals are already filled - line = 'completer {} {}'.format('foo ' * (pos - 1), text) + line = "completer {} {}".format("foo " * (pos - 1), text) endidx = len(line) begidx = endidx - len(text) @@ -662,23 +662,23 @@ def test_autocomp_blank_token(ac_app) -> None: ArgparseCompleter, ) - blank = '' + blank = "" # Blank flag arg will be consumed. Therefore we expect to be completing the first positional. - text = '' - line = f'completer -c {blank} {text}' + text = "" + line = f"completer -c {blank} {text}" endidx = len(line) begidx = endidx - len(text) completer = ArgparseCompleter(ac_app.completer_parser, ac_app) - tokens = ['-c', blank, text] + tokens = ["-c", blank, text] completions = completer.complete(text, line, begidx, endidx, tokens) expected = ArgparseCompleterTester.completions_for_pos_1 assert completions.to_strings() == Completions.from_values(expected).to_strings() # Blank arg for first positional will be consumed. Therefore we expect to be completing the second positional. - text = '' - line = f'completer {blank} {text}' + text = "" + line = f"completer {blank} {text}" endidx = len(line) begidx = endidx - len(text) @@ -692,8 +692,8 @@ def test_autocomp_blank_token(ac_app) -> None: @with_ansi_style(ru.AllowStyle.ALWAYS) def test_completion_tables_strings(ac_app) -> None: # Test completion table created from strings - text = '' - line = f'choices --completion_items {text}' + text = "" + line = f"choices --completion_items {text}" endidx = len(line) begidx = endidx - len(text) @@ -731,8 +731,8 @@ def test_completion_tables_strings(ac_app) -> None: @with_ansi_style(ru.AllowStyle.ALWAYS) def test_completion_tables_numbers(ac_app) -> None: # Test completion table created from numbers - text = '' - line = f'choices --num_completion_items {text}' + text = "" + line = f"choices --num_completion_items {text}" endidx = len(line) begidx = endidx - len(text) @@ -761,7 +761,7 @@ def test_completion_tables_numbers(ac_app) -> None: @pytest.mark.parametrize( - ('num_aliases', 'show_table'), + ("num_aliases", "show_table"), [ # The number of completion results determines if a completion table is displayed. # The count must be greater than 1 and less than ac_app.max_completion_table_items, @@ -774,12 +774,12 @@ def test_completion_tables_numbers(ac_app) -> None: def test_max_completion_table_items(ac_app, num_aliases, show_table) -> None: # Create aliases for i in range(num_aliases): - run_cmd(ac_app, f'alias create fake_alias{i} help') + run_cmd(ac_app, f"alias create fake_alias{i} help") assert len(ac_app.aliases) == num_aliases - text = 'fake_alias' - line = f'alias list {text}' + text = "fake_alias" + line = f"alias list {text}" endidx = len(line) begidx = endidx - len(text) @@ -789,59 +789,59 @@ def test_max_completion_table_items(ac_app, num_aliases, show_table) -> None: @pytest.mark.parametrize( - ('args', 'expected'), + ("args", "expected"), [ # Flag with nargs = 2 - ('--set_value', ArgparseCompleterTester.set_value_choices), - ('--set_value set', ['value', 'choices']), + ("--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), + ("--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), + ("--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']), + ("--one_or_more", ArgparseCompleterTester.one_or_more_choices), + ("--one_or_more one", ["or", "more", "choices"]), # Flag with nargs = OPTIONAL - ('--optional', ArgparseCompleterTester.optional_choices), + ("--optional", ArgparseCompleterTester.optional_choices), # Only one arg allowed for an OPTIONAL. At positional now. - ('--optional optional', ArgparseCompleterTester.positional_choices), + ("--optional optional", ArgparseCompleterTester.positional_choices), # Flag with nargs range (1, 2) - ('--range', ArgparseCompleterTester.range_choices), - ('--range some', ['range', 'choices']), + ("--range", ArgparseCompleterTester.range_choices), + ("--range some", ["range", "choices"]), # Already used 2 args so at positional - ('--range some range', ArgparseCompleterTester.positional_choices), + ("--range some range", ArgparseCompleterTester.positional_choices), # Flag with nargs = REMAINDER - ('--remainder', ArgparseCompleterTester.remainder_choices), - ('--remainder remainder ', ['choices']), + ("--remainder", ArgparseCompleterTester.remainder_choices), + ("--remainder remainder ", ["choices"]), # No more flags can appear after a REMAINDER flag) - ('--remainder choices --set_value', ['remainder']), + ("--remainder choices --set_value", ["remainder"]), # Double dash ends the current flag - ('--range choice --', ArgparseCompleterTester.positional_choices), + ("--range choice --", ArgparseCompleterTester.positional_choices), # Double dash ends a REMAINDER flag - ('--remainder remainder --', ArgparseCompleterTester.positional_choices), + ("--remainder remainder --", ArgparseCompleterTester.positional_choices), # No more flags after a double dash - ('-- --one_or_more ', ArgparseCompleterTester.positional_choices), + ("-- --one_or_more ", ArgparseCompleterTester.positional_choices), # Consume positional - ('', ArgparseCompleterTester.positional_choices), - ('positional', ['the', 'choices']), + ("", ArgparseCompleterTester.positional_choices), + ("positional", ["the", "choices"]), # Intermixed flag and positional - ('positional --set_value', ArgparseCompleterTester.set_value_choices), - ('positional --set_value set', ['choices', 'value']), + ("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']), + ("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', []), + ("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']), + ("the positional --set_value", ArgparseCompleterTester.remainder_choices), + ("the positional remainder --set_value", ["choices"]), ], ) def test_autcomp_nargs(ac_app, args, expected) -> None: - text = '' - line = f'nargs {args} {text}' + text = "" + line = f"nargs {args} {text}" endidx = len(line) begidx = endidx - len(text) @@ -850,40 +850,40 @@ def test_autcomp_nargs(ac_app, args, expected) -> None: @pytest.mark.parametrize( - ('command_and_args', 'text', 'is_error'), + ("command_and_args", "text", "is_error"), [ # Flag is finished before moving on - ('hint --flag foo --', '', False), - ('hint --flag foo --help', '', False), - ('hint --flag foo', '--', False), - ('nargs --one_or_more one --', '', False), - ('nargs --one_or_more one or --set_value', '', False), - ('nargs --one_or_more one or more', '--', False), - ('nargs --set_value set value --', '', False), - ('nargs --set_value set value --one_or_more', '', False), - ('nargs --set_value set value', '--', False), - ('nargs --set_val set value', '--', False), # This exercises our abbreviated flag detection - ('nargs --range choices --', '', False), - ('nargs --range choices range --set_value', '', False), - ('nargs --range range', '--', False), + ("hint --flag foo --", "", False), + ("hint --flag foo --help", "", False), + ("hint --flag foo", "--", False), + ("nargs --one_or_more one --", "", False), + ("nargs --one_or_more one or --set_value", "", False), + ("nargs --one_or_more one or more", "--", False), + ("nargs --set_value set value --", "", False), + ("nargs --set_value set value --one_or_more", "", False), + ("nargs --set_value set value", "--", False), + ("nargs --set_val set value", "--", False), # This exercises our abbreviated flag detection + ("nargs --range choices --", "", False), + ("nargs --range choices range --set_value", "", False), + ("nargs --range range", "--", False), # Flag is not finished before moving on - ('hint --flag --', '', True), - ('hint --flag --help', '', True), - ('hint --flag', '--', True), - ('nargs --one_or_more --', '', True), - ('nargs --one_or_more --set_value', '', True), - ('nargs --one_or_more', '--', True), - ('nargs --set_value set --', '', True), - ('nargs --set_value set --one_or_more', '', True), - ('nargs --set_value set', '--', True), - ('nargs --set_val set', '--', True), # This exercises our abbreviated flag detection - ('nargs --range --', '', True), - ('nargs --range --set_value', '', True), - ('nargs --range', '--', True), + ("hint --flag --", "", True), + ("hint --flag --help", "", True), + ("hint --flag", "--", True), + ("nargs --one_or_more --", "", True), + ("nargs --one_or_more --set_value", "", True), + ("nargs --one_or_more", "--", True), + ("nargs --set_value set --", "", True), + ("nargs --set_value set --one_or_more", "", True), + ("nargs --set_value set", "--", True), + ("nargs --set_val set", "--", True), # This exercises our abbreviated flag detection + ("nargs --range --", "", True), + ("nargs --range --set_value", "", True), + ("nargs --range", "--", True), ], ) def test_unfinished_flag_error(ac_app, command_and_args, text, is_error) -> None: - line = f'{command_and_args} {text}' + line = f"{command_and_args} {text}" endidx = len(line) begidx = endidx - len(text) @@ -893,8 +893,8 @@ def test_unfinished_flag_error(ac_app, command_and_args, text, is_error) -> None def test_completion_table_metavar(ac_app) -> None: # Test when metavar is None - text = '' - line = f'choices --no_metavar {text}' + text = "" + line = f"choices --no_metavar {text}" endidx = len(line) begidx = endidx - len(text) @@ -903,8 +903,8 @@ def test_completion_table_metavar(ac_app) -> None: assert completions.table.columns[0].header == "NO_METAVAR" # Test when metavar is a string - text = '' - line = f'choices --str_metavar {text}' + text = "" + line = f"choices --str_metavar {text}" endidx = len(line) begidx = endidx - len(text) @@ -913,8 +913,8 @@ def test_completion_table_metavar(ac_app) -> None: assert completions.table.columns[0].header == ac_app.STR_METAVAR # Test when metavar is a tuple - text = '' - line = f'choices --tuple_metavar {text}' + text = "" + line = f"choices --tuple_metavar {text}" endidx = len(line) begidx = endidx - len(text) @@ -923,8 +923,8 @@ def test_completion_table_metavar(ac_app) -> None: assert completions.table is not None assert completions.table.columns[0].header == ac_app.TUPLE_METAVAR[0].upper() - text = '' - line = f'choices --tuple_metavar token_1 {text}' + text = "" + line = f"choices --tuple_metavar token_1 {text}" endidx = len(line) begidx = endidx - len(text) @@ -933,8 +933,8 @@ def test_completion_table_metavar(ac_app) -> None: assert completions.table is not None assert completions.table.columns[0].header == ac_app.TUPLE_METAVAR[1].upper() - text = '' - line = f'choices --tuple_metavar token_1 token_2 {text}' + text = "" + line = f"choices --tuple_metavar token_1 token_2 {text}" endidx = len(line) begidx = endidx - len(text) @@ -946,34 +946,34 @@ def test_completion_table_metavar(ac_app) -> None: @pytest.mark.parametrize( - ('command_and_args', 'text', 'has_hint'), + ("command_and_args", "text", "has_hint"), [ # Normal cases - ('hint', '', True), - ('hint --flag', '', True), - ('hint --suppressed_help', '', False), - ('hint --suppressed_hint', '', False), + ("hint", "", True), + ("hint --flag", "", True), + ("hint --suppressed_help", "", False), + ("hint --suppressed_hint", "", False), # Hint because flag does not have enough values to be considered finished - ('nargs --one_or_more', '-', True), + ("nargs --one_or_more", "-", True), # This flag has reached its minimum value count and therefore a new flag could start. # However the flag can still consume values and the text is not a single prefix character. # Therefore a hint will be shown. - ('nargs --one_or_more choices', 'bad_completion', True), + ("nargs --one_or_more choices", "bad_completion", True), # Like the previous case, but this time text is a single prefix character which will cause flag # name completion to occur instead of a hint for the current flag. - ('nargs --one_or_more choices', '-', False), + ("nargs --one_or_more choices", "-", False), # Hint because this is a REMAINDER flag and therefore no more flag name completions occur. - ('nargs --remainder', '-', True), + ("nargs --remainder", "-", True), # No hint for the positional because text is a single prefix character which results in flag name completion - ('hint', '-', False), + ("hint", "-", False), # Hint because this is a REMAINDER positional and therefore no more flag name completions occur. - ('nargs the choices', '-', True), - ('nargs the choices remainder', '-', True), + ("nargs the choices", "-", True), + ("nargs the choices remainder", "-", True), ], ) def test_autocomp_no_results_hint(ac_app, command_and_args, text, has_hint) -> None: """Test whether _NoResultsErrors include hint text.""" - line = f'{command_and_args} {text}' + line = f"{command_and_args} {text}" endidx = len(line) begidx = endidx - len(text) @@ -986,8 +986,8 @@ def test_autocomp_no_results_hint(ac_app, command_and_args, text, has_hint) -> N def test_autocomp_hint_no_help_text(ac_app) -> None: """Tests that a hint for an arg with no help text only includes the arg's name.""" - text = '' - line = f'hint foo {text}' + text = "" + line = f"hint foo {text}" endidx = len(line) begidx = endidx - len(text) @@ -996,16 +996,16 @@ def test_autocomp_hint_no_help_text(ac_app) -> None: @pytest.mark.parametrize( - ('args', 'text'), + ("args", "text"), [ # Exercise a flag arg and choices function that raises a CompletionError - ('--choice ', 'choice'), + ("--choice ", "choice"), # Exercise a positional arg and completer that raises a CompletionError - ('', 'completer'), + ("", "completer"), ], ) def test_completion_error(ac_app, args, text) -> None: - line = f'raise_completion_error {args} {text}' + line = f"raise_completion_error {args} {text}" endidx = len(line) begidx = endidx - len(text) @@ -1014,19 +1014,19 @@ def test_completion_error(ac_app, args, text) -> None: @pytest.mark.parametrize( - ('command_and_args', 'expected'), + ("command_and_args", "expected"), [ # Exercise a choices function that receives arg_tokens dictionary - ('arg_tokens choice subcmd', ['choice', 'subcmd']), + ("arg_tokens choice subcmd", ["choice", "subcmd"]), # Exercise a completer that receives arg_tokens dictionary - ('arg_tokens completer subcmd fake', ['completer', 'subcmd']), + ("arg_tokens completer subcmd fake", ["completer", "subcmd"]), # Exercise overriding parent_arg from the subcommand - ('arg_tokens completer subcmd --parent_arg override fake', ['override', 'subcmd']), + ("arg_tokens completer subcmd --parent_arg override fake", ["override", "subcmd"]), ], ) def test_arg_tokens(ac_app, command_and_args, expected) -> None: - text = '' - line = f'{command_and_args} {text}' + text = "" + line = f"{command_and_args} {text}" endidx = len(line) begidx = endidx - len(text) @@ -1035,30 +1035,30 @@ def test_arg_tokens(ac_app, command_and_args, expected) -> None: @pytest.mark.parametrize( - ('command_and_args', 'text', 'output_contains', 'first_match'), + ("command_and_args", "text", "output_contains", "first_match"), [ # Group isn't done. The optional positional's hint will show and flags will not complete. - ('mutex', '', 'the optional positional', None), + ("mutex", "", "the optional positional", None), # Group isn't done. Flag name will still complete. - ('mutex', '--fl', '', '--flag'), + ("mutex", "--fl", "", "--flag"), # Group isn't done. Flag hint will show. - ('mutex --flag', '', 'the flag arg', None), + ("mutex --flag", "", "the flag arg", None), # Group finished by optional positional. No flag name will complete. - ('mutex pos_val', '--fl', '', None), + ("mutex pos_val", "--fl", "", None), # Group finished by optional positional. Error will display trying to complete the flag's value. - ('mutex pos_val --flag', '', 'f/--flag: not allowed with argument optional_pos', None), + ("mutex pos_val --flag", "", "f/--flag: not allowed with argument optional_pos", None), # Group finished by --flag. Optional positional will be skipped and last_arg will show its hint. - ('mutex --flag flag_val', '', 'the last arg', None), + ("mutex --flag flag_val", "", "the last arg", None), # Group finished by --flag. Other flag name won't complete. - ('mutex --flag flag_val', '--oth', '', None), + ("mutex --flag flag_val", "--oth", "", None), # Group finished by --flag. Error will display trying to complete other flag's value. - ('mutex --flag flag_val --other', '', '-o/--other_flag: not allowed with argument -f/--flag', None), + ("mutex --flag flag_val --other", "", "-o/--other_flag: not allowed with argument -f/--flag", None), # Group finished by --flag. That same flag can be used again so it's hint will show. - ('mutex --flag flag_val --flag', '', 'the flag arg', None), + ("mutex --flag flag_val --flag", "", "the flag arg", None), ], ) def test_complete_mutex_group(ac_app, command_and_args, text, output_contains, first_match) -> None: - line = f'{command_and_args} {text}' + line = f"{command_and_args} {text}" endidx = len(line) begidx = endidx - len(text) @@ -1076,18 +1076,18 @@ def test_single_prefix_char() -> None: _single_prefix_char, ) - parser = Cmd2ArgumentParser(prefix_chars='-+') + parser = Cmd2ArgumentParser(prefix_chars="-+") # Invalid - assert not _single_prefix_char('', parser) - assert not _single_prefix_char('--', parser) - assert not _single_prefix_char('-+', parser) - assert not _single_prefix_char('++has space', parser) - assert not _single_prefix_char('foo', parser) + assert not _single_prefix_char("", parser) + assert not _single_prefix_char("--", parser) + assert not _single_prefix_char("-+", parser) + assert not _single_prefix_char("++has space", parser) + assert not _single_prefix_char("foo", parser) # Valid - assert _single_prefix_char('-', parser) - assert _single_prefix_char('+', parser) + assert _single_prefix_char("-", parser) + assert _single_prefix_char("+", parser) def test_looks_like_flag() -> None: @@ -1098,16 +1098,16 @@ def test_looks_like_flag() -> None: parser = Cmd2ArgumentParser() # Does not start like a flag - assert not _looks_like_flag('', parser) - assert not _looks_like_flag('non-flag', parser) - assert not _looks_like_flag('-', parser) - assert not _looks_like_flag('--has space', parser) - assert not _looks_like_flag('-2', parser) + assert not _looks_like_flag("", parser) + assert not _looks_like_flag("non-flag", parser) + assert not _looks_like_flag("-", parser) + assert not _looks_like_flag("--has space", parser) + assert not _looks_like_flag("-2", parser) # Does start like a flag - assert _looks_like_flag('--', parser) - assert _looks_like_flag('-flag', parser) - assert _looks_like_flag('--flag', parser) + assert _looks_like_flag("--", parser) + assert _looks_like_flag("-flag", parser) + assert _looks_like_flag("--flag", parser) def test_complete_command_no_tokens(ac_app) -> None: @@ -1118,7 +1118,7 @@ def test_complete_command_no_tokens(ac_app) -> None: parser = Cmd2ArgumentParser() ac = ArgparseCompleter(parser, ac_app) - completions = ac.complete(text='', line='', begidx=0, endidx=0, tokens=[]) + completions = ac.complete(text="", line="", begidx=0, endidx=0, tokens=[]) assert not completions @@ -1130,20 +1130,20 @@ def test_complete_command_help_no_tokens(ac_app) -> None: parser = Cmd2ArgumentParser() ac = ArgparseCompleter(parser, ac_app) - completions = ac.complete_subcommand_help(text='', line='', begidx=0, endidx=0, tokens=[]) + completions = ac.complete_subcommand_help(text="", line="", begidx=0, endidx=0, tokens=[]) assert not completions @pytest.mark.parametrize( - ('flag', 'expected'), + ("flag", "expected"), [ - ('--provider', standalone_choices), - ('--completer', standalone_completions), + ("--provider", standalone_choices), + ("--completer", standalone_completions), ], ) def test_complete_standalone(ac_app, flag, expected) -> None: - text = '' - line = f'standalone {flag} {text}' + text = "" + line = f"standalone {flag} {text}" endidx = len(line) begidx = endidx - len(text) @@ -1152,22 +1152,22 @@ def test_complete_standalone(ac_app, flag, expected) -> None: @pytest.mark.parametrize( - ('subcommand', 'flag', 'display_meta'), + ("subcommand", "flag", "display_meta"), [ - ('helpful', '', 'my helpful text'), - ('helpful', '--helpful_flag', "a helpful flag"), - ('helpless', '', ''), - ('helpless', '--helpless_flag', ''), + ("helpful", "", "my helpful text"), + ("helpful", "--helpful_flag", "a helpful flag"), + ("helpless", "", ""), + ("helpless", "--helpless_flag", ""), ], ) def test_display_meta(ac_app, subcommand, flag, display_meta) -> None: """Test that subcommands and flags can have display_meta data.""" if flag: text = flag - line = f'meta {subcommand} {text}' + line = f"meta {subcommand} {text}" else: text = subcommand - line = f'meta {text}' + line = f"meta {text}" endidx = len(line) begidx = endidx - len(text) @@ -1177,13 +1177,13 @@ def test_display_meta(ac_app, subcommand, flag, display_meta) -> None: def test_validate_table_data_no_table() -> None: - action = argparse.Action(option_strings=['-f'], dest='foo') + action = argparse.Action(option_strings=["-f"], dest="foo") action.set_table_columns(None) arg_state = argparse_completer._ArgumentState(action) completions = Completions( [ - CompletionItem('item1'), - CompletionItem('item2'), + CompletionItem("item1"), + CompletionItem("item2"), ] ) @@ -1192,14 +1192,14 @@ def test_validate_table_data_no_table() -> None: def test_validate_table_data_missing_columns() -> None: - action = argparse.Action(option_strings=['-f'], dest='foo') + action = argparse.Action(option_strings=["-f"], dest="foo") action.set_table_columns(None) arg_state = argparse_completer._ArgumentState(action) completions = Completions( [ - CompletionItem('item1', table_data=['data1']), - CompletionItem('item2', table_data=['data2']), + CompletionItem("item1", table_data=["data1"]), + CompletionItem("item2", table_data=["data2"]), ] ) @@ -1211,14 +1211,14 @@ def test_validate_table_data_missing_columns() -> None: def test_validate_table_data_missing_item_data() -> None: - action = argparse.Action(option_strings=['-f'], dest='foo') - action.set_table_columns(['Col1']) + action = argparse.Action(option_strings=["-f"], dest="foo") + action.set_table_columns(["Col1"]) arg_state = argparse_completer._ArgumentState(action) completions = Completions( [ - CompletionItem('item1', table_data=['data1']), - CompletionItem('item2'), # Missing table_data + CompletionItem("item1", table_data=["data1"]), + CompletionItem("item2"), # Missing table_data ] ) @@ -1230,14 +1230,14 @@ def test_validate_table_data_missing_item_data() -> None: def test_validate_table_data_length_mismatch() -> None: - action = argparse.Action(option_strings=['-f'], dest='foo') - action.set_table_columns(['Col1', 'Col2']) + action = argparse.Action(option_strings=["-f"], dest="foo") + action.set_table_columns(["Col1", "Col2"]) arg_state = argparse_completer._ArgumentState(action) completions = Completions( [ - CompletionItem('item1', table_data=['data1a', 'data1b']), - CompletionItem('item2', table_data=['only_one']), + CompletionItem("item1", table_data=["data1a", "data1b"]), + CompletionItem("item2", table_data=["only_one"]), ] ) @@ -1249,14 +1249,14 @@ def test_validate_table_data_length_mismatch() -> None: def test_validate_table_data_valid() -> None: - action = argparse.Action(option_strings=['-f'], dest='foo') - action.get_table_columns = lambda: ['Col1', 'Col2'] + action = argparse.Action(option_strings=["-f"], dest="foo") + action.get_table_columns = lambda: ["Col1", "Col2"] arg_state = argparse_completer._ArgumentState(action) completions = Completions( [ - CompletionItem('item1', table_data=['data1a', 'data1b']), - CompletionItem('item2', table_data=['data2a', 'data2b']), + CompletionItem("item1", table_data=["data1a", "data1b"]), + CompletionItem("item2", table_data=["data2a", "data2b"]), ] ) @@ -1279,7 +1279,7 @@ def _complete_flags(self, text: str, line: str, begidx: int, endidx: int, used_f # Add a custom argparse action attribute -argparse_utils.register_argparse_argument_parameter('complete_when_ready') +argparse_utils.register_argparse_argument_parameter("complete_when_ready") # App used to test custom ArgparseCompleter types and custom argparse attributes @@ -1290,7 +1290,7 @@ def __init__(self) -> None: # Parser that's used to test setting the app-wide default ArgparseCompleter type default_completer_parser = Cmd2ArgumentParser(description="Testing app-wide argparse completer") - default_completer_parser.add_argument('--myflag', complete_when_ready=True) + default_completer_parser.add_argument("--myflag", complete_when_ready=True) @with_argparser(default_completer_parser) def do_default_completer(self, args: argparse.Namespace) -> None: @@ -1300,7 +1300,7 @@ def do_default_completer(self, args: argparse.Namespace) -> None: custom_completer_parser = Cmd2ArgumentParser( description="Testing parser-specific argparse completer", ap_completer_type=CustomCompleter ) - custom_completer_parser.add_argument('--myflag', complete_when_ready=True) + custom_completer_parser.add_argument("--myflag", complete_when_ready=True) @with_argparser(custom_completer_parser) def do_custom_completer(self, args: argparse.Namespace) -> None: @@ -1308,7 +1308,7 @@ def do_custom_completer(self, args: argparse.Namespace) -> None: # Test as_subcommand_to decorator with custom completer top_parser = Cmd2ArgumentParser(description="Top Command") - top_parser.add_subparsers(dest='subcommand', metavar='SUBCOMMAND', required=True) + top_parser.add_subparsers(dest="subcommand", metavar="SUBCOMMAND", required=True) @with_argparser(top_parser) def do_top(self, args: argparse.Namespace) -> None: @@ -1318,17 +1318,17 @@ def do_top(self, args: argparse.Namespace) -> None: # Parser for a subcommand with no custom completer type no_custom_completer_parser = Cmd2ArgumentParser(description="No custom completer") - no_custom_completer_parser.add_argument('--myflag', complete_when_ready=True) + no_custom_completer_parser.add_argument("--myflag", complete_when_ready=True) - @cmd2.as_subcommand_to('top', 'no_custom', no_custom_completer_parser, help="no custom completer") + @cmd2.as_subcommand_to("top", "no_custom", no_custom_completer_parser, help="no custom completer") def _subcmd_no_custom(self, args: argparse.Namespace) -> None: pass # Parser for a subcommand with a custom completer type custom_completer_parser2 = Cmd2ArgumentParser(description="Custom completer", ap_completer_type=CustomCompleter) - custom_completer_parser2.add_argument('--myflag', complete_when_ready=True) + custom_completer_parser2.add_argument("--myflag", complete_when_ready=True) - @cmd2.as_subcommand_to('top', 'custom', custom_completer_parser2, help="custom completer") + @cmd2.as_subcommand_to("top", "custom", custom_completer_parser2, help="custom completer") def _subcmd_custom(self, args: argparse.Namespace) -> None: pass @@ -1343,8 +1343,8 @@ def test_default_custom_completer_type(custom_completer_app: CustomCompleterApp) try: argparse_completer.set_default_ap_completer_type(CustomCompleter) - text = '--m' - line = f'default_completer {text}' + text = "--m" + line = f"default_completer {text}" endidx = len(line) begidx = endidx - len(text) @@ -1365,8 +1365,8 @@ def test_default_custom_completer_type(custom_completer_app: CustomCompleterApp) def test_custom_completer_type(custom_completer_app: CustomCompleterApp) -> None: """Test parser with a specific custom ArgparseCompleter type""" - text = '--m' - line = f'custom_completer {text}' + text = "--m" + line = f"custom_completer {text}" endidx = len(line) begidx = endidx - len(text) @@ -1384,8 +1384,8 @@ def test_custom_completer_type(custom_completer_app: CustomCompleterApp) -> None def test_decorated_subcmd_custom_completer(custom_completer_app: CustomCompleterApp) -> None: """Tests custom completer type on a subcommand created with @cmd2.as_subcommand_to""" # First test the subcommand without the custom completer - text = '--m' - line = f'top no_custom {text}' + text = "--m" + line = f"top no_custom {text}" endidx = len(line) begidx = endidx - len(text) @@ -1399,8 +1399,8 @@ def test_decorated_subcmd_custom_completer(custom_completer_app: CustomCompleter assert completions.items[0].text == "--myflag" # Now test the subcommand with the custom completer - text = '--m' - line = f'top custom {text}' + text = "--m" + line = f"top custom {text}" endidx = len(line) begidx = endidx - len(text) diff --git a/tests/test_argparse_subcommands.py b/tests/test_argparse_subcommands.py index 653f3fdcd..968f42259 100644 --- a/tests/test_argparse_subcommands.py +++ b/tests/test_argparse_subcommands.py @@ -23,31 +23,31 @@ def base_foo(self, args) -> None: def base_bar(self, args) -> None: """Bar subcommand of base command""" - self._cmd.poutput(f'(({args.z}))') + self._cmd.poutput(f"(({args.z}))") def base_helpless(self, args) -> None: """Helpless subcommand of base command""" - self._cmd.poutput(f'(({args.z}))') + self._cmd.poutput(f"(({args.z}))") # create the top-level parser for the base command base_parser = cmd2.Cmd2ArgumentParser() - base_subparsers = base_parser.add_subparsers(dest='subcommand', metavar='SUBCOMMAND', required=True) + base_subparsers = base_parser.add_subparsers(dest="subcommand", metavar="SUBCOMMAND", required=True) # create the parser for the "foo" subcommand - parser_foo = base_subparsers.add_parser('foo', help='foo help') - parser_foo.add_argument('-x', type=int, default=1, help='integer') - parser_foo.add_argument('y', type=float, help='float') + parser_foo = base_subparsers.add_parser("foo", help="foo help") + parser_foo.add_argument("-x", type=int, default=1, help="integer") + parser_foo.add_argument("y", type=float, help="float") parser_foo.set_defaults(func=base_foo) # create the parser for the "bar" subcommand - parser_bar = base_subparsers.add_parser('bar', help='bar help', aliases=['bar_1', 'bar_2']) - parser_bar.add_argument('z', help='string') + parser_bar = base_subparsers.add_parser("bar", help="bar help", aliases=["bar_1", "bar_2"]) + parser_bar.add_argument("z", help="string") parser_bar.set_defaults(func=base_bar) # create the parser for the "helpless" subcommand # This subcommand has aliases and no help text. - parser_helpless = base_subparsers.add_parser('helpless', aliases=['helpless_1', 'helpless_2']) - parser_helpless.add_argument('z', help='string') + parser_helpless = base_subparsers.add_parser("helpless", aliases=["helpless_1", "helpless_2"]) + parser_helpless.add_argument("z", help="string") parser_helpless.set_defaults(func=base_helpless) @cmd2.with_argparser(base_parser) @@ -64,68 +64,68 @@ def subcommand_app(): def test_subcommand_foo(subcommand_app) -> None: - out, _err = run_cmd(subcommand_app, 'base foo -x2 5.0') - assert out == ['10.0'] + out, _err = run_cmd(subcommand_app, "base foo -x2 5.0") + assert out == ["10.0"] def test_subcommand_bar(subcommand_app) -> None: - out, _err = run_cmd(subcommand_app, 'base bar baz') - assert out == ['((baz))'] + out, _err = run_cmd(subcommand_app, "base bar baz") + assert out == ["((baz))"] def test_subcommand_invalid(subcommand_app) -> None: - _out, err = run_cmd(subcommand_app, 'base baz') - assert err[0].startswith('Usage: base') + _out, err = run_cmd(subcommand_app, "base baz") + assert err[0].startswith("Usage: base") assert err[1].startswith("Error: argument SUBCOMMAND: invalid choice: 'baz'") def test_subcommand_base_help(subcommand_app) -> None: - out, _err = run_cmd(subcommand_app, 'help base') - assert out[0].startswith('Usage: base') - assert out[1] == '' - assert out[2] == 'Base command help' + out, _err = run_cmd(subcommand_app, "help base") + assert out[0].startswith("Usage: base") + assert out[1] == "" + assert out[2] == "Base command help" def test_subcommand_help(subcommand_app) -> None: # foo has no aliases - out, _err = run_cmd(subcommand_app, 'help base foo') - assert out[0].startswith('Usage: base foo') - assert out[1] == '' - assert out[2] == 'Positional Arguments:' + out, _err = run_cmd(subcommand_app, "help base foo") + assert out[0].startswith("Usage: base foo") + assert out[1] == "" + assert out[2] == "Positional Arguments:" # bar has aliases (usage should never show alias name) - out, _err = run_cmd(subcommand_app, 'help base bar') - assert out[0].startswith('Usage: base bar') - assert out[1] == '' - assert out[2] == 'Positional Arguments:' + out, _err = run_cmd(subcommand_app, "help base bar") + assert out[0].startswith("Usage: base bar") + assert out[1] == "" + assert out[2] == "Positional Arguments:" - out, _err = run_cmd(subcommand_app, 'help base bar_1') - assert out[0].startswith('Usage: base bar') - assert out[1] == '' - assert out[2] == 'Positional Arguments:' + out, _err = run_cmd(subcommand_app, "help base bar_1") + assert out[0].startswith("Usage: base bar") + assert out[1] == "" + assert out[2] == "Positional Arguments:" - out, _err = run_cmd(subcommand_app, 'help base bar_2') - assert out[0].startswith('Usage: base bar') - assert out[1] == '' - assert out[2] == 'Positional Arguments:' + out, _err = run_cmd(subcommand_app, "help base bar_2") + assert out[0].startswith("Usage: base bar") + assert out[1] == "" + assert out[2] == "Positional Arguments:" # helpless has aliases and no help text (usage should never show alias name) - out, _err = run_cmd(subcommand_app, 'help base helpless') - assert out[0].startswith('Usage: base helpless') - assert out[1] == '' - assert out[2] == 'Positional Arguments:' + out, _err = run_cmd(subcommand_app, "help base helpless") + assert out[0].startswith("Usage: base helpless") + assert out[1] == "" + assert out[2] == "Positional Arguments:" - out, _err = run_cmd(subcommand_app, 'help base helpless_1') - assert out[0].startswith('Usage: base helpless') - assert out[1] == '' - assert out[2] == 'Positional Arguments:' + out, _err = run_cmd(subcommand_app, "help base helpless_1") + assert out[0].startswith("Usage: base helpless") + assert out[1] == "" + assert out[2] == "Positional Arguments:" - out, _err = run_cmd(subcommand_app, 'help base helpless_2') - assert out[0].startswith('Usage: base helpless') - assert out[1] == '' - assert out[2] == 'Positional Arguments:' + out, _err = run_cmd(subcommand_app, "help base helpless_2") + assert out[0].startswith("Usage: base helpless") + assert out[1] == "" + assert out[2] == "Positional Arguments:" def test_subcommand_invalid_help(subcommand_app) -> None: - out, _err = run_cmd(subcommand_app, 'help base baz') - assert out[0].startswith('Usage: base') + out, _err = run_cmd(subcommand_app, "help base baz") + assert out[0].startswith("Usage: base") diff --git a/tests/test_argparse_utils.py b/tests/test_argparse_utils.py index 8510432f2..43c294a8f 100644 --- a/tests/test_argparse_utils.py +++ b/tests/test_argparse_utils.py @@ -27,10 +27,10 @@ def __init__(self, *args, **kwargs) -> None: super().__init__(*args, **kwargs) range_parser = Cmd2ArgumentParser() - range_parser.add_argument('--arg0', nargs=1) - range_parser.add_argument('--arg1', nargs=2) - range_parser.add_argument('--arg2', nargs=(3,)) - range_parser.add_argument('--arg3', nargs=(2, 3)) + range_parser.add_argument("--arg0", nargs=1) + range_parser.add_argument("--arg1", nargs=2) + range_parser.add_argument("--arg2", nargs=(3,)) + range_parser.add_argument("--arg3", nargs=(2, 3)) @cmd2.with_argparser(range_parser) def do_range(self, _) -> None: @@ -47,39 +47,39 @@ def fake_func() -> None: @pytest.mark.parametrize( - ('kwargs', 'is_valid'), + ("kwargs", "is_valid"), [ - ({'choices_provider': fake_func}, True), - ({'completer': fake_func}, True), - ({'choices_provider': fake_func, 'completer': fake_func}, False), + ({"choices_provider": fake_func}, True), + ({"completer": fake_func}, True), + ({"choices_provider": fake_func, "completer": fake_func}, False), ], ) def test_apcustom_completion_callable_count(kwargs, is_valid) -> None: parser = Cmd2ArgumentParser() if is_valid: - parser.add_argument('name', **kwargs) + parser.add_argument("name", **kwargs) else: - expected_err = 'Only one of the following parameters' + expected_err = "Only one of the following parameters" with pytest.raises(ValueError, match=expected_err): - parser.add_argument('name', **kwargs) + parser.add_argument("name", **kwargs) -@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_completion_callable_alongside_choices(kwargs) -> None: parser = Cmd2ArgumentParser() expected_err = "None of the following parameters can be used alongside a choices parameter" with pytest.raises(ValueError, match=expected_err): - parser.add_argument('name', choices=['my', 'choices', 'list'], **kwargs) + parser.add_argument("name", choices=["my", "choices", "list"], **kwargs) -@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_completion_callable_when_nargs_is_0(kwargs) -> None: parser = Cmd2ArgumentParser() expected_err = "None of the following parameters can be used on an action that takes no arguments" with pytest.raises(ValueError, match=expected_err): - parser.add_argument('--name', action='store_true', **kwargs) + parser.add_argument("--name", action="store_true", **kwargs) def test_apcustom_usage() -> None: @@ -89,35 +89,35 @@ def test_apcustom_usage() -> None: def test_apcustom_nargs_help_format(cust_app) -> None: - out, _err = run_cmd(cust_app, 'help range') - assert 'Usage: range [-h] [--arg0 ARG0] [--arg1 ARG1{2}] [--arg2 ARG2{3+}]' in out[0] - assert ' [--arg3 ARG3{2..3}]' in out[1] + out, _err = run_cmd(cust_app, "help range") + assert "Usage: range [-h] [--arg0 ARG0] [--arg1 ARG1{2}] [--arg2 ARG2{3+}]" in out[0] + assert " [--arg3 ARG3{2..3}]" in out[1] def test_apcustom_nargs_range_validation(cust_app) -> None: # nargs = (3,) # noqa: ERA001 - _out, err = run_cmd(cust_app, 'range --arg2 one two') - assert 'Error: argument --arg2: expected at least 3 arguments' in err[2] + _out, err = run_cmd(cust_app, "range --arg2 one two") + assert "Error: argument --arg2: expected at least 3 arguments" in err[2] - _out, err = run_cmd(cust_app, 'range --arg2 one two three') + _out, err = run_cmd(cust_app, "range --arg2 one two three") assert not err - _out, err = run_cmd(cust_app, 'range --arg2 one two three four') + _out, err = run_cmd(cust_app, "range --arg2 one two three four") assert not err # nargs = (2,3) # noqa: ERA001 - _out, err = run_cmd(cust_app, 'range --arg3 one') - assert 'Error: argument --arg3: expected 2 to 3 arguments' in err[2] + _out, err = run_cmd(cust_app, "range --arg3 one") + assert "Error: argument --arg3: expected 2 to 3 arguments" in err[2] - _out, err = run_cmd(cust_app, 'range --arg3 one two') + _out, err = run_cmd(cust_app, "range --arg3 one two") assert not err - _out, err = run_cmd(cust_app, 'range --arg2 one two three') + _out, err = run_cmd(cust_app, "range --arg2 one two three") assert not err @pytest.mark.parametrize( - ('nargs', 'expected_parts'), + ("nargs", "expected_parts"), [ # arg{2} ( @@ -161,50 +161,50 @@ def test_rich_metavar_parts( @pytest.mark.parametrize( - 'nargs_tuple', + "nargs_tuple", [ (), - ('f', 5), - (5, 'f'), + ("f", 5), + (5, "f"), (1, 2, 3), ], ) def test_apcustom_narg_invalid_tuples(nargs_tuple) -> None: parser = Cmd2ArgumentParser() - expected_err = 'Ranged values for nargs must be a tuple of 1 or 2 integers' + expected_err = "Ranged values for nargs must be a tuple of 1 or 2 integers" with pytest.raises(ValueError, match=expected_err): - parser.add_argument('invalid_tuple', nargs=nargs_tuple) + parser.add_argument("invalid_tuple", nargs=nargs_tuple) def test_apcustom_narg_tuple_order() -> None: parser = Cmd2ArgumentParser() - expected_err = 'Invalid nargs range. The first value must be less than the second' + expected_err = "Invalid nargs range. The first value must be less than the second" with pytest.raises(ValueError, match=expected_err): - parser.add_argument('invalid_tuple', nargs=(2, 1)) + parser.add_argument("invalid_tuple", nargs=(2, 1)) def test_apcustom_narg_tuple_negative() -> None: parser = Cmd2ArgumentParser() - expected_err = 'Negative numbers are invalid for nargs range' + expected_err = "Negative numbers are invalid for nargs range" with pytest.raises(ValueError, match=expected_err): - parser.add_argument('invalid_tuple', nargs=(-1, 1)) + parser.add_argument("invalid_tuple", nargs=(-1, 1)) def test_apcustom_narg_tuple_zero_base() -> None: parser = Cmd2ArgumentParser() - arg = parser.add_argument('arg', nargs=(0,)) + arg = parser.add_argument("arg", nargs=(0,)) assert arg.nargs == argparse.ZERO_OR_MORE assert arg.get_nargs_range() is None assert "[arg ...]" in parser.format_help() parser = Cmd2ArgumentParser() - arg = parser.add_argument('arg', nargs=(0, 1)) + arg = parser.add_argument("arg", nargs=(0, 1)) assert arg.nargs == argparse.OPTIONAL assert arg.get_nargs_range() is None assert "[arg]" in parser.format_help() parser = Cmd2ArgumentParser() - arg = parser.add_argument('arg', nargs=(0, 3)) + arg = parser.add_argument("arg", nargs=(0, 3)) assert arg.nargs == argparse.ZERO_OR_MORE assert arg.get_nargs_range() == (0, 3) assert "arg{0..3}" in parser.format_help() @@ -212,13 +212,13 @@ def test_apcustom_narg_tuple_zero_base() -> None: def test_apcustom_narg_tuple_one_base() -> None: parser = Cmd2ArgumentParser() - arg = parser.add_argument('arg', nargs=(1,)) + arg = parser.add_argument("arg", nargs=(1,)) assert arg.nargs == argparse.ONE_OR_MORE assert arg.get_nargs_range() is None assert "arg [arg ...]" in parser.format_help() parser = Cmd2ArgumentParser() - arg = parser.add_argument('arg', nargs=(1, 5)) + arg = parser.add_argument("arg", nargs=(1, 5)) assert arg.nargs == argparse.ONE_OR_MORE assert arg.get_nargs_range() == (1, 5) assert "arg{1..5}" in parser.format_help() @@ -227,19 +227,19 @@ def test_apcustom_narg_tuple_one_base() -> None: def test_apcustom_narg_tuple_other_ranges() -> None: # Test range with no upper bound on max parser = Cmd2ArgumentParser() - arg = parser.add_argument('arg', nargs=(2,)) + arg = parser.add_argument("arg", nargs=(2,)) assert arg.nargs == argparse.ONE_OR_MORE assert arg.get_nargs_range() == (2, constants.INFINITY) # Test finite range parser = Cmd2ArgumentParser() - arg = parser.add_argument('arg', nargs=(2, 5)) + arg = parser.add_argument("arg", nargs=(2, 5)) assert arg.nargs == argparse.ONE_OR_MORE assert arg.get_nargs_range() == (2, 5) def test_apcustom_print_message(capsys) -> None: - test_message = 'The test message' + test_message = "The test message" # Specify the file parser = Cmd2ArgumentParser() @@ -280,8 +280,8 @@ def test_build_range_error() -> None: def test_apcustom_metavar_tuple() -> None: # Test the case when a tuple metavar is used with nargs an integer > 1 parser = Cmd2ArgumentParser() - parser.add_argument('--aflag', nargs=2, metavar=('foo', 'bar'), help='This is a test') - assert '[--aflag foo bar]' in parser.format_help() + parser.add_argument("--aflag", nargs=2, metavar=("foo", "bar"), help="This is a test") + assert "[--aflag foo bar]" in parser.format_help() def test_register_argparse_argument_parameter() -> None: @@ -290,8 +290,8 @@ def test_register_argparse_argument_parameter() -> None: register_argparse_argument_parameter(param_name) assert param_name in argparse_utils._CUSTOM_ACTION_ATTRIBS - assert hasattr(argparse.Action, f'get_{param_name}') - assert hasattr(argparse.Action, f'set_{param_name}') + assert hasattr(argparse.Action, f"get_{param_name}") + assert hasattr(argparse.Action, f"set_{param_name}") # Test duplicate registration expected_err = "already registered" @@ -315,7 +315,7 @@ def test_register_argparse_argument_parameter() -> None: with pytest.raises(KeyError, match=expected_err): register_argparse_argument_parameter("colliding_param") finally: - delattr(argparse.Action, 'get_colliding_param') + delattr(argparse.Action, "get_colliding_param") # Test collision with internal attribute try: @@ -466,15 +466,15 @@ def test_completion_items_as_choices(capsys) -> None: parser.add_argument("choices_arg", type=str, choices=choices) # First test valid choices. Confirm the parsed data matches the correct type of str. - args = parser.parse_args(['1']) - assert args.choices_arg == '1' + args = parser.parse_args(["1"]) + assert args.choices_arg == "1" - args = parser.parse_args(['2']) - assert args.choices_arg == '2' + args = parser.parse_args(["2"]) + assert args.choices_arg == "2" # Next test invalid choice with pytest.raises(SystemExit): - args = parser.parse_args(['3']) + args = parser.parse_args(["3"]) # Confirm error text contains correct value type of str _out, err = capsys.readouterr() @@ -488,49 +488,49 @@ def test_completion_items_as_choices(capsys) -> None: parser.add_argument("choices_arg", type=int, choices=choices) # First test valid choices. Confirm the parsed data matches the correct type of int. - args = parser.parse_args(['1']) + args = parser.parse_args(["1"]) assert args.choices_arg == 1 - args = parser.parse_args(['2']) + args = parser.parse_args(["2"]) assert args.choices_arg == 2 # Next test invalid choice with pytest.raises(SystemExit): - args = parser.parse_args(['3']) + args = parser.parse_args(["3"]) # Confirm error text contains correct value type of int _out, err = capsys.readouterr() - assert 'invalid choice: 3 (choose from 1, 2)' in err + assert "invalid choice: 3 (choose from 1, 2)" in err def test_update_prog() -> None: """Test Cmd2ArgumentParser.update_prog() across various scenarios.""" # Set up a complex parser hierarchy - old_app = 'old_app' + old_app = "old_app" root = Cmd2ArgumentParser(prog=old_app) # Positionals before subcommand - root.add_argument('pos1') + root.add_argument("pos1") # Mutually exclusive group with positionals group = root.add_mutually_exclusive_group(required=True) - group.add_argument('posA', nargs='?') - group.add_argument('posB', nargs='?') + group.add_argument("posA", nargs="?") + group.add_argument("posB", nargs="?") # Subparsers with aliases and no help text - root_subparsers = root.add_subparsers(dest='cmd') + root_subparsers = root.add_subparsers(dest="cmd") # Subcommand with aliases - sub1 = root_subparsers.add_parser('sub1', aliases=['s1', 'alias1'], help='help for sub1') + sub1 = root_subparsers.add_parser("sub1", aliases=["s1", "alias1"], help="help for sub1") # Subcommand with no help text - sub2 = root_subparsers.add_parser('sub2') + sub2 = root_subparsers.add_parser("sub2") # Nested subparser - sub2.add_argument('inner_pos') - sub2_subparsers = sub2.add_subparsers(dest='sub2_cmd') - leaf = sub2_subparsers.add_parser('leaf', help='leaf help') + sub2.add_argument("inner_pos") + sub2_subparsers = sub2.add_subparsers(dest="sub2_cmd") + leaf = sub2_subparsers.add_parser("leaf", help="leaf help") # Save initial prog values orig_root_prog = root.prog @@ -539,7 +539,7 @@ def test_update_prog() -> None: orig_leaf_prog = leaf.prog # Perform update - new_app = 'new_app' + new_app = "new_app" root.update_prog(new_app) # Verify updated prog values @@ -556,12 +556,12 @@ def test_update_prog() -> None: assert leaf.prog == orig_leaf_prog.replace(old_app, new_app, 1) # Verify that action._prog_prefix was updated by adding a new subparser - sub3 = root_subparsers.add_parser('sub3') + sub3 = root_subparsers.add_parser("sub3") assert sub3.prog.startswith(new_app) - assert sub3.prog == root_subparsers._prog_prefix + ' sub3' + assert sub3.prog == root_subparsers._prog_prefix + " sub3" # Verify aliases still point to the correct parser for action in root._actions: if isinstance(action, argparse._SubParsersAction): - assert action.choices['s1'].prog == sub1.prog - assert action.choices['alias1'].prog == sub1.prog + assert action.choices["s1"].prog == sub1.prog + assert action.choices["alias1"].prog == sub1.prog diff --git a/tests/test_cmd2.py b/tests/test_cmd2.py index 2c4225dd6..a35df53bf 100644 --- a/tests/test_cmd2.py +++ b/tests/test_cmd2.py @@ -66,7 +66,7 @@ def test_not_in_main_thread(base_app, capsys) -> None: # Mock threading.main_thread() to return our fake thread saved_main_thread = threading.main_thread fake_main = threading.Thread() - threading.main_thread = mock.MagicMock(name='main_thread', return_value=fake_main) + threading.main_thread = mock.MagicMock(name="main_thread", return_value=fake_main) with pytest.raises(RuntimeError) as excinfo: base_app.cmdloop() @@ -77,19 +77,19 @@ def test_not_in_main_thread(base_app, capsys) -> None: def test_empty_statement(base_app) -> None: - out, _err = run_cmd(base_app, '') - expected = normalize('') + out, _err = run_cmd(base_app, "") + expected = normalize("") assert out == expected def test_base_help(base_app) -> None: - out, _err = run_cmd(base_app, 'help') + out, _err = run_cmd(base_app, "help") assert base_app.last_result is True verify_help_text(base_app, out) def test_base_help_verbose(base_app) -> None: - out, _err = run_cmd(base_app, 'help -v') + out, _err = run_cmd(base_app, "help -v") assert base_app.last_result is True verify_help_text(base_app, out) @@ -98,31 +98,31 @@ def test_base_help_verbose(base_app) -> None: help_doc += "\n:param fake param" base_app.do_help.__func__.__doc__ = help_doc - out, _err = run_cmd(base_app, 'help --verbose') + out, _err = run_cmd(base_app, "help --verbose") assert base_app.last_result is True verify_help_text(base_app, out) - assert ':param' not in ''.join(out) + assert ":param" not in "".join(out) def test_base_argparse_help(base_app) -> None: # Verify that "set -h" gives the same output as "help set" and that it starts in a way that makes sense - out1, _err1 = run_cmd(base_app, 'set -h') - out2, _err2 = run_cmd(base_app, 'help set') + out1, _err1 = run_cmd(base_app, "set -h") + out2, _err2 = run_cmd(base_app, "help set") assert out1 == out2 - assert out1[0].startswith('Usage: set') - assert out1[1] == '' - assert out1[2].startswith('Set a settable parameter') + assert out1[0].startswith("Usage: set") + assert out1[1] == "" + assert out1[2].startswith("Set a settable parameter") def test_base_invalid_option(base_app) -> None: - _out, err = run_cmd(base_app, 'set -z') - assert err[0] == 'Usage: set [-h] [param] [value]' - assert 'Error: unrecognized arguments: -z' in err[1] + _out, err = run_cmd(base_app, "set -z") + assert err[0] == "Usage: set [-h] [param] [value]" + assert "Error: unrecognized arguments: -z" in err[1] def test_base_shortcuts(base_app) -> None: - out, _err = run_cmd(base_app, 'shortcuts') + out, _err = run_cmd(base_app, "shortcuts") expected = normalize(SHORTCUTS_TXT) assert out == expected assert base_app.last_result is True @@ -131,12 +131,12 @@ def test_base_shortcuts(base_app) -> None: def test_command_starts_with_shortcut() -> None: expected_err = "Invalid command name 'help'" with pytest.raises(ValueError, match=expected_err): - cmd2.Cmd(shortcuts={'help': 'fake'}) + cmd2.Cmd(shortcuts={"help": "fake"}) def test_base_set(base_app) -> None: # Make sure all settables appear in output. - out, _err = run_cmd(base_app, 'set') + out, _err = run_cmd(base_app, "set") settables = sorted(base_app.settables.keys()) # The settables will appear in order in the table. @@ -157,7 +157,7 @@ def test_base_set(base_app) -> None: def test_set(base_app) -> None: - out, _err = run_cmd(base_app, 'set quiet True') + out, _err = run_cmd(base_app, "set quiet True") expected = normalize( """ quiet - was: False @@ -168,7 +168,7 @@ def test_set(base_app) -> None: assert base_app.last_result is True line_found = False - out, _err = run_cmd(base_app, 'set quiet') + out, _err = run_cmd(base_app, "set quiet") for line in out: if "quiet" in line and "True" in line and "False" not in line: line_found = True @@ -176,25 +176,25 @@ def test_set(base_app) -> None: assert line_found assert len(base_app.last_result) == 1 - assert base_app.last_result['quiet'] is True + assert base_app.last_result["quiet"] is True def test_set_val_empty(base_app) -> None: base_app.editor = "fake" _out, _err = run_cmd(base_app, 'set editor ""') - assert base_app.editor == '' + assert base_app.editor == "" assert base_app.last_result is True def test_set_val_is_flag(base_app) -> None: base_app.editor = "fake" _out, _err = run_cmd(base_app, 'set editor "-h"') - assert base_app.editor == '-h' + assert base_app.editor == "-h" assert base_app.last_result is True def test_set_not_supported(base_app) -> None: - _out, err = run_cmd(base_app, 'set qqq True') + _out, err = run_cmd(base_app, "set qqq True") expected = normalize( """ Parameter 'qqq' not supported (type 'set' for list of parameters). @@ -206,28 +206,28 @@ def test_set_not_supported(base_app) -> None: def test_set_no_settables(base_app) -> None: base_app._settables.clear() - _out, err = run_cmd(base_app, 'set quiet True') + _out, err = run_cmd(base_app, "set quiet True") expected = normalize("There are no settable parameters") assert err == expected assert base_app.last_result is False @pytest.mark.parametrize( - ('new_val', 'is_valid', 'expected'), + ("new_val", "is_valid", "expected"), [ (ru.AllowStyle.NEVER, True, ru.AllowStyle.NEVER), - ('neVeR', True, ru.AllowStyle.NEVER), + ("neVeR", True, ru.AllowStyle.NEVER), (ru.AllowStyle.TERMINAL, True, ru.AllowStyle.TERMINAL), - ('TeRMInal', True, ru.AllowStyle.TERMINAL), + ("TeRMInal", True, ru.AllowStyle.TERMINAL), (ru.AllowStyle.ALWAYS, True, ru.AllowStyle.ALWAYS), - ('AlWaYs', True, ru.AllowStyle.ALWAYS), - ('invalid', False, ru.AllowStyle.TERMINAL), + ("AlWaYs", True, ru.AllowStyle.ALWAYS), + ("invalid", False, ru.AllowStyle.TERMINAL), ], ) @with_ansi_style(ru.AllowStyle.TERMINAL) def test_set_allow_style(base_app, new_val, is_valid, expected) -> None: # Use the set command to alter allow_style - out, err = run_cmd(base_app, f'set allow_style {new_val}') + out, err = run_cmd(base_app, f"set allow_style {new_val}") assert base_app.last_result is is_valid # Verify the results @@ -239,19 +239,19 @@ def test_set_allow_style(base_app, new_val, is_valid, expected) -> None: def test_set_with_choices(base_app) -> None: """Test choices validation of Settables""" - fake_choices = ['valid', 'choices'] + fake_choices = ["valid", "choices"] base_app.fake = fake_choices[0] - fake_settable = cmd2.Settable('fake', type(base_app.fake), "fake description", base_app, choices=fake_choices) + fake_settable = cmd2.Settable("fake", type(base_app.fake), "fake description", base_app, choices=fake_choices) base_app.add_settable(fake_settable) # Try a valid choice - _out, err = run_cmd(base_app, f'set fake {fake_choices[1]}') + _out, err = run_cmd(base_app, f"set fake {fake_choices[1]}") assert base_app.last_result is True assert not err # Try an invalid choice - _out, err = run_cmd(base_app, 'set fake bad_value') + _out, err = run_cmd(base_app, "set fake bad_value") assert base_app.last_result is False assert err[0].startswith("Error setting fake: invalid choice") @@ -259,7 +259,7 @@ def test_set_with_choices(base_app) -> None: class OnChangeHookApp(cmd2.Cmd): def __init__(self, *args, **kwargs) -> None: super().__init__(*args, **kwargs) - self.add_settable(utils.Settable('quiet', bool, "my description", self, onchange_cb=self._onchange_quiet)) + self.add_settable(utils.Settable("quiet", bool, "my description", self, onchange_cb=self._onchange_quiet)) def _onchange_quiet(self, name, old, new) -> None: """Runs when quiet is changed via set command""" @@ -272,7 +272,7 @@ def onchange_app(): def test_set_onchange_hook(onchange_app) -> None: - out, _err = run_cmd(onchange_app, 'set quiet True') + out, _err = run_cmd(onchange_app, "set quiet True") expected = normalize( """ You changed quiet @@ -286,32 +286,32 @@ def test_set_onchange_hook(onchange_app) -> None: def test_base_shell(base_app, monkeypatch) -> None: m = mock.Mock() - monkeypatch.setattr("{}.Popen".format('subprocess'), m) - out, _err = run_cmd(base_app, 'shell echo a') + monkeypatch.setattr("{}.Popen".format("subprocess"), m) + out, _err = run_cmd(base_app, "shell echo a") assert out == [] assert m.called def test_shell_last_result(base_app) -> None: base_app.last_result = None - run_cmd(base_app, 'shell fake') + run_cmd(base_app, "shell fake") assert base_app.last_result is not None def test_shell_manual_call(base_app) -> None: # Verifies crash from Issue #986 doesn't happen cmds = ['echo "hi"', 'echo "there"', 'echo "cmd2!"'] - cmd = ';'.join(cmds) + cmd = ";".join(cmds) base_app.do_shell(cmd) - cmd = '&&'.join(cmds) + cmd = "&&".join(cmds) base_app.do_shell(cmd) def test_base_error(base_app) -> None: - _out, err = run_cmd(base_app, 'meow') + _out, err = run_cmd(base_app, "meow") assert "is not a recognized command" in err[0] @@ -319,7 +319,7 @@ def test_base_error_suggest_command(base_app) -> None: try: old_suggest_similar_command = base_app.suggest_similar_command base_app.suggest_similar_command = True - _out, err = run_cmd(base_app, 'historic') + _out, err = run_cmd(base_app, "historic") assert "history" in err[1] finally: base_app.suggest_similar_command = old_suggest_similar_command @@ -327,20 +327,20 @@ def test_base_error_suggest_command(base_app) -> None: def test_run_script(base_app, request) -> None: test_dir = os.path.dirname(request.module.__file__) - filename = os.path.join(test_dir, 'script.txt') + filename = os.path.join(test_dir, "script.txt") assert base_app._script_dir == [] assert base_app._current_script_dir is None # Get output out the script - script_out, script_err = run_cmd(base_app, f'run_script {filename}') + script_out, script_err = run_cmd(base_app, f"run_script {filename}") assert base_app.last_result is True assert base_app._script_dir == [] assert base_app._current_script_dir is None # Now run the commands manually and compare their output to script's - with open(filename, encoding='utf-8') as file: + with open(filename, encoding="utf-8") as file: script_commands = file.read().splitlines() manual_out = [] @@ -355,28 +355,28 @@ def test_run_script(base_app, request) -> None: def test_run_script_with_empty_args(base_app) -> None: - _out, err = run_cmd(base_app, 'run_script') + _out, err = run_cmd(base_app, "run_script") assert "the following arguments are required" in err[1] assert base_app.last_result is None def test_run_script_with_invalid_file(base_app, request) -> None: # Path does not exist - _out, err = run_cmd(base_app, 'run_script does_not_exist.txt') + _out, err = run_cmd(base_app, "run_script does_not_exist.txt") assert "Problem accessing script from " in err[0] assert base_app.last_result is False # Path is a directory test_dir = os.path.dirname(request.module.__file__) - _out, err = run_cmd(base_app, f'run_script {test_dir}') + _out, err = run_cmd(base_app, f"run_script {test_dir}") assert "Problem accessing script from " in err[0] assert base_app.last_result is False def test_run_script_with_empty_file(base_app, request) -> None: test_dir = os.path.dirname(request.module.__file__) - filename = os.path.join(test_dir, 'scripts', 'empty.txt') - out, err = run_cmd(base_app, f'run_script {filename}') + filename = os.path.join(test_dir, "scripts", "empty.txt") + out, err = run_cmd(base_app, f"run_script {filename}") assert not out assert not err assert base_app.last_result is True @@ -384,39 +384,39 @@ def test_run_script_with_empty_file(base_app, request) -> None: def test_run_script_with_binary_file(base_app, request) -> None: test_dir = os.path.dirname(request.module.__file__) - filename = os.path.join(test_dir, 'scripts', 'binary.bin') - _out, err = run_cmd(base_app, f'run_script {filename}') + filename = os.path.join(test_dir, "scripts", "binary.bin") + _out, err = run_cmd(base_app, f"run_script {filename}") assert "is not an ASCII or UTF-8 encoded text file" in err[0] assert base_app.last_result is False def test_run_script_with_python_file(base_app, request, monkeypatch) -> None: - read_input_mock = mock.MagicMock(name='read_input', return_value='2') + read_input_mock = mock.MagicMock(name="read_input", return_value="2") monkeypatch.setattr("cmd2.Cmd.read_input", read_input_mock) test_dir = os.path.dirname(request.module.__file__) - filename = os.path.join(test_dir, 'pyscript', 'stop.py') - _out, err = run_cmd(base_app, f'run_script {filename}') + filename = os.path.join(test_dir, "pyscript", "stop.py") + _out, err = run_cmd(base_app, f"run_script {filename}") assert "appears to be a Python file" in err[0] assert base_app.last_result is False def test_run_script_with_utf8_file(base_app, request) -> None: test_dir = os.path.dirname(request.module.__file__) - filename = os.path.join(test_dir, 'scripts', 'utf8.txt') + filename = os.path.join(test_dir, "scripts", "utf8.txt") assert base_app._script_dir == [] assert base_app._current_script_dir is None # Get output out the script - script_out, script_err = run_cmd(base_app, f'run_script {filename}') + script_out, script_err = run_cmd(base_app, f"run_script {filename}") assert base_app.last_result is True assert base_app._script_dir == [] assert base_app._current_script_dir is None # Now run the commands manually and compare their output to script's - with open(filename, encoding='utf-8') as file: + with open(filename, encoding="utf-8") as file: script_commands = file.read().splitlines() manual_out = [] @@ -432,8 +432,8 @@ def test_run_script_with_utf8_file(base_app, request) -> None: def test_scripts_add_to_history(base_app, request) -> None: test_dir = os.path.dirname(request.module.__file__) - filename = os.path.join(test_dir, 'scripts', 'help.txt') - command = f'run_script {filename}' + filename = os.path.join(test_dir, "scripts", "help.txt") + command = f"run_script {filename}" # Add to history base_app.scripts_add_to_history = True @@ -441,7 +441,7 @@ def test_scripts_add_to_history(base_app, request) -> None: run_cmd(base_app, command) assert len(base_app.history) == 2 assert base_app.history.get(1).raw == command - assert base_app.history.get(2).raw == 'help -v' + assert base_app.history.get(2).raw == "help -v" # Do not add to history base_app.scripts_add_to_history = False @@ -455,10 +455,10 @@ def test_run_script_nested_run_scripts(base_app, request) -> None: # Verify that running a script with nested run_script commands works correctly, # and runs the nested script commands in the correct order. test_dir = os.path.dirname(request.module.__file__) - filename = os.path.join(test_dir, 'scripts', 'nested.txt') + filename = os.path.join(test_dir, "scripts", "nested.txt") # Run the top level script - initial_run = 'run_script ' + filename + initial_run = "run_script " + filename run_cmd(base_app, initial_run) assert base_app.last_result is True @@ -471,16 +471,16 @@ def test_run_script_nested_run_scripts(base_app, request) -> None: shortcuts _relative_run_script postcmds.txt set always_show_hint False""" - out, _err = run_cmd(base_app, 'history -s') + out, _err = run_cmd(base_app, "history -s") assert out == normalize(expected) def test_runcmds_plus_hooks(base_app, request) -> None: test_dir = os.path.dirname(request.module.__file__) - prefilepath = os.path.join(test_dir, 'scripts', 'precmds.txt') - postfilepath = os.path.join(test_dir, 'scripts', 'postcmds.txt') + prefilepath = os.path.join(test_dir, "scripts", "precmds.txt") + postfilepath = os.path.join(test_dir, "scripts", "postcmds.txt") - base_app.runcmds_plus_hooks(['run_script ' + prefilepath, 'help', 'shortcuts', 'run_script ' + postfilepath]) + base_app.runcmds_plus_hooks(["run_script " + prefilepath, "help", "shortcuts", "run_script " + postfilepath]) expected = f""" run_script {prefilepath} set always_show_hint True @@ -489,7 +489,7 @@ def test_runcmds_plus_hooks(base_app, request) -> None: run_script {postfilepath} set always_show_hint False""" - out, _err = run_cmd(base_app, 'history -s') + out, _err = run_cmd(base_app, "history -s") assert out == normalize(expected) @@ -498,20 +498,20 @@ def test_runcmds_plus_hooks_ctrl_c(base_app, capsys) -> None: import types def do_keyboard_interrupt(self, _) -> NoReturn: - raise KeyboardInterrupt('Interrupting this command') + raise KeyboardInterrupt("Interrupting this command") base_app.do_keyboard_interrupt = types.MethodType(do_keyboard_interrupt, base_app) # Default behavior is to not stop runcmds_plus_hooks() on Ctrl-C base_app.history.clear() - base_app.runcmds_plus_hooks(['help', 'keyboard_interrupt', 'shortcuts']) + base_app.runcmds_plus_hooks(["help", "keyboard_interrupt", "shortcuts"]) _out, err = capsys.readouterr() assert not err assert len(base_app.history) == 3 # Ctrl-C should stop runcmds_plus_hooks() in this case base_app.history.clear() - base_app.runcmds_plus_hooks(['help', 'keyboard_interrupt', 'shortcuts'], stop_on_keyboard_interrupt=True) + base_app.runcmds_plus_hooks(["help", "keyboard_interrupt", "shortcuts"], stop_on_keyboard_interrupt=True) _out, err = capsys.readouterr() assert err.startswith("Interrupting this command") assert len(base_app.history) == 2 @@ -519,20 +519,20 @@ def do_keyboard_interrupt(self, _) -> NoReturn: def test_relative_run_script(base_app, request) -> None: test_dir = os.path.dirname(request.module.__file__) - filename = os.path.join(test_dir, 'script.txt') + filename = os.path.join(test_dir, "script.txt") assert base_app._script_dir == [] assert base_app._current_script_dir is None # Get output out the script - script_out, script_err = run_cmd(base_app, f'_relative_run_script {filename}') + script_out, script_err = run_cmd(base_app, f"_relative_run_script {filename}") assert base_app.last_result is True assert base_app._script_dir == [] assert base_app._current_script_dir is None # Now run the commands manually and compare their output to script's - with open(filename, encoding='utf-8') as file: + with open(filename, encoding="utf-8") as file: script_commands = file.read().splitlines() manual_out = [] @@ -546,11 +546,11 @@ def test_relative_run_script(base_app, request) -> None: assert script_err == manual_err -@pytest.mark.parametrize('file_name', odd_file_names) +@pytest.mark.parametrize("file_name", odd_file_names) def test_relative_run_script_with_odd_file_names(base_app, file_name, monkeypatch) -> None: """Test file names with various patterns""" # Mock out the do_run_script call to see what args are passed to it - run_script_mock = mock.MagicMock(name='do_run_script') + run_script_mock = mock.MagicMock(name="do_run_script") monkeypatch.setattr("cmd2.Cmd.do_run_script", run_script_mock) run_cmd(base_app, f"_relative_run_script {su.quote(file_name)}") @@ -558,8 +558,8 @@ def test_relative_run_script_with_odd_file_names(base_app, file_name, monkeypatc def test_relative_run_script_requires_an_argument(base_app) -> None: - _out, err = run_cmd(base_app, '_relative_run_script') - assert 'Error: the following arguments' in err[1] + _out, err = run_cmd(base_app, "_relative_run_script") + assert "Error: the following arguments" in err[1] assert base_app.last_result is None @@ -576,8 +576,8 @@ def hook(self: cmd2.Cmd, data: plugin.CommandFinalizationData) -> plugin.Command hook_app = HookApp() test_dir = os.path.dirname(request.module.__file__) - filename = os.path.join(test_dir, 'script.txt') - out, _err = run_cmd(hook_app, f'run_script {filename}') + filename = os.path.join(test_dir, "script.txt") + out, _err = run_cmd(hook_app, f"run_script {filename}") assert "WE ARE IN SCRIPT" in out[-1] @@ -593,7 +593,7 @@ def do_system_exit(self, _) -> NoReturn: base_app.do_system_exit = types.MethodType(do_system_exit, base_app) - stop = base_app.onecmd_plus_hooks('system_exit') + stop = base_app.onecmd_plus_hooks("system_exit") assert stop assert base_app.exit_code == exit_code @@ -611,7 +611,7 @@ def do_passthrough(self, _) -> NoReturn: base_app.do_passthrough = types.MethodType(do_passthrough, base_app) with pytest.raises(OSError, match=expected_err): - base_app.onecmd_plus_hooks('passthrough') + base_app.onecmd_plus_hooks("passthrough") class RedirectionApp(cmd2.Cmd): @@ -634,19 +634,19 @@ def redirection_app(): def test_output_redirection(redirection_app) -> None: - fd, filename = tempfile.mkstemp(prefix='cmd2_test', suffix='.txt') + fd, filename = tempfile.mkstemp(prefix="cmd2_test", suffix=".txt") os.close(fd) try: # Verify that writing to a file works - run_cmd(redirection_app, f'print_output > {filename}') + run_cmd(redirection_app, f"print_output > {filename}") with open(filename) as f: lines = f.read().splitlines() assert lines[0] == "print" assert lines[1] == "poutput" # Verify that appending to a file also works - run_cmd(redirection_app, f'print_output >> {filename}') + run_cmd(redirection_app, f"print_output >> {filename}") with open(filename) as f: lines = f.read().splitlines() assert lines[0] == "print" @@ -659,20 +659,20 @@ def test_output_redirection(redirection_app) -> None: def test_output_redirection_custom_stdout(redirection_app) -> None: """sys.stdout should not redirect if it's different than self.stdout.""" - fd, filename = tempfile.mkstemp(prefix='cmd2_test', suffix='.txt') + fd, filename = tempfile.mkstemp(prefix="cmd2_test", suffix=".txt") os.close(fd) redirection_app.stdout = io.StringIO() try: # Verify that we only see output written to self.stdout - run_cmd(redirection_app, f'print_output > {filename}') + run_cmd(redirection_app, f"print_output > {filename}") with open(filename) as f: lines = f.read().splitlines() assert "print" not in lines assert lines[0] == "poutput" # Verify that appending to a file also works - run_cmd(redirection_app, f'print_output >> {filename}') + run_cmd(redirection_app, f"print_output >> {filename}") with open(filename) as f: lines = f.read().splitlines() assert "print" not in lines @@ -683,38 +683,38 @@ def test_output_redirection_custom_stdout(redirection_app) -> None: def test_output_redirection_to_nonexistent_directory(redirection_app) -> None: - filename = '~/fakedir/this_does_not_exist.txt' + filename = "~/fakedir/this_does_not_exist.txt" - _out, err = run_cmd(redirection_app, f'print_output > {filename}') - assert 'Failed to redirect' in err[0] + _out, err = run_cmd(redirection_app, f"print_output > {filename}") + assert "Failed to redirect" in err[0] - _out, err = run_cmd(redirection_app, f'print_output >> {filename}') - assert 'Failed to redirect' in err[0] + _out, err = run_cmd(redirection_app, f"print_output >> {filename}") + assert "Failed to redirect" in err[0] def test_output_redirection_to_too_long_filename(redirection_app) -> None: filename = ( - '~/sdkfhksdjfhkjdshfkjsdhfkjsdhfkjdshfkjdshfkjshdfkhdsfkjhewfuihewiufhweiufhiweufhiuewhiuewhfiuwehfia' - 'ewhfiuewhfiuewhfiuewhiuewhfiuewhfiuewfhiuwehewiufhewiuhfiweuhfiuwehfiuewfhiuwehiuewfhiuewhiewuhfiueh' - 'fiuwefhewiuhewiufhewiufhewiufhewiufhewiufhewiufhewiufhewiuhewiufhewiufhewiuheiufhiuewheiwufhewiufheu' - 'fheiufhieuwhfewiuhfeiufhiuewfhiuewheiwuhfiuewhfiuewhfeiuwfhewiufhiuewhiuewhfeiuwhfiuwehfuiwehfiuehie' - 'whfieuwfhieufhiuewhfeiuwfhiuefhueiwhfw' + "~/sdkfhksdjfhkjdshfkjsdhfkjsdhfkjdshfkjdshfkjshdfkhdsfkjhewfuihewiufhweiufhiweufhiuewhiuewhfiuwehfia" + "ewhfiuewhfiuewhfiuewhiuewhfiuewhfiuewfhiuwehewiufhewiuhfiweuhfiuwehfiuewfhiuwehiuewfhiuewhiewuhfiueh" + "fiuwefhewiuhewiufhewiufhewiufhewiufhewiufhewiufhewiufhewiuhewiufhewiufhewiuheiufhiuewheiwufhewiufheu" + "fheiufhieuwhfewiuhfeiufhiuewfhiuewheiwuhfiuewhfiuewhfeiuwfhewiufhiuewhiuewhfeiuwhfiuwehfuiwehfiuehie" + "whfieuwfhieufhiuewhfeiuwfhiuefhueiwhfw" ) - _out, err = run_cmd(redirection_app, f'print_output > {filename}') - assert 'Failed to redirect' in err[0] + _out, err = run_cmd(redirection_app, f"print_output > {filename}") + assert "Failed to redirect" in err[0] - _out, err = run_cmd(redirection_app, f'print_output >> {filename}') - assert 'Failed to redirect' in err[0] + _out, err = run_cmd(redirection_app, f"print_output >> {filename}") + assert "Failed to redirect" in err[0] def test_feedback_to_output_true(redirection_app) -> None: redirection_app.feedback_to_output = True - f, filename = tempfile.mkstemp(prefix='cmd2_test', suffix='.txt') + f, filename = tempfile.mkstemp(prefix="cmd2_test", suffix=".txt") os.close(f) try: - run_cmd(redirection_app, f'print_feedback > {filename}') + run_cmd(redirection_app, f"print_feedback > {filename}") with open(filename) as f: content = f.read().splitlines() assert "feedback" in content @@ -724,11 +724,11 @@ def test_feedback_to_output_true(redirection_app) -> None: def test_feedback_to_output_false(redirection_app) -> None: redirection_app.feedback_to_output = False - f, filename = tempfile.mkstemp(prefix='feedback_to_output', suffix='.txt') + f, filename = tempfile.mkstemp(prefix="feedback_to_output", suffix=".txt") os.close(f) try: - _out, err = run_cmd(redirection_app, f'print_feedback > {filename}') + _out, err = run_cmd(redirection_app, f"print_feedback > {filename}") with open(filename) as f: content = f.read().splitlines() @@ -742,10 +742,10 @@ def test_disallow_redirection(redirection_app) -> None: # Set allow_redirection to False redirection_app.allow_redirection = False - filename = 'test_allow_redirect.txt' + filename = "test_allow_redirect.txt" # Verify output wasn't redirected - out, _err = run_cmd(redirection_app, f'print_output > {filename}') + out, _err = run_cmd(redirection_app, f"print_output > {filename}") assert "print" in out assert "poutput" in out @@ -770,7 +770,7 @@ def test_pipe_to_shell_custom_stdout(redirection_app) -> None: def test_pipe_to_shell_and_redirect(redirection_app) -> None: - filename = 'out.txt' + filename = "out.txt" out, err = run_cmd(redirection_app, f"print_output | sort > {filename}") assert not out assert not err @@ -780,7 +780,7 @@ def test_pipe_to_shell_and_redirect(redirection_app) -> None: def test_pipe_to_shell_error(redirection_app) -> None: # Try to pipe command output to a shell command that doesn't exist in order to produce an error - out, err = run_cmd(redirection_app, 'print_output | foobarbaz.this_does_not_exist') + out, err = run_cmd(redirection_app, "print_output | foobarbaz.this_does_not_exist") assert not out assert "Pipe process exited with code" in err[0] @@ -802,13 +802,13 @@ def test_pipe_to_shell_error(redirection_app) -> None: @pytest.mark.skipif(not can_paste, reason="Pyperclip could not find a copy/paste mechanism for your system") def test_send_to_paste_buffer(redirection_app) -> None: # Test writing to the PasteBuffer/Clipboard - run_cmd(redirection_app, 'print_output >') + run_cmd(redirection_app, "print_output >") lines = cmd2.cmd2.get_paste_buffer().splitlines() assert lines[0] == "print" assert lines[1] == "poutput" # Test appending to the PasteBuffer/Clipboard - run_cmd(redirection_app, 'print_output >>') + run_cmd(redirection_app, "print_output >>") lines = cmd2.cmd2.get_paste_buffer().splitlines() assert lines[0] == "print" assert lines[1] == "poutput" @@ -822,13 +822,13 @@ def test_send_to_paste_buffer_custom_stdout(redirection_app) -> None: redirection_app.stdout = io.StringIO() # Verify that we only see output written to self.stdout - run_cmd(redirection_app, 'print_output >') + run_cmd(redirection_app, "print_output >") lines = cmd2.cmd2.get_paste_buffer().splitlines() assert "print" not in lines assert lines[0] == "poutput" # Test appending to the PasteBuffer/Clipboard - run_cmd(redirection_app, 'print_output >>') + run_cmd(redirection_app, "print_output >>") lines = cmd2.cmd2.get_paste_buffer().splitlines() assert "print" not in lines assert lines[0] == "poutput" @@ -837,18 +837,18 @@ def test_send_to_paste_buffer_custom_stdout(redirection_app) -> None: def test_get_paste_buffer_exception(redirection_app, mocker, capsys) -> None: # Force get_paste_buffer to throw an exception - pastemock = mocker.patch('pyperclip.paste') - pastemock.side_effect = ValueError('foo') + pastemock = mocker.patch("pyperclip.paste") + pastemock.side_effect = ValueError("foo") # Redirect command output to the clipboard - redirection_app.onecmd_plus_hooks('print_output > ') + redirection_app.onecmd_plus_hooks("print_output > ") # Make sure we got the exception output out, err = capsys.readouterr() - assert out == '' + assert out == "" # this just checks that cmd2 is surfacing whatever error gets raised by pyperclip.paste - assert 'ValueError' in err - assert 'foo' in err + assert "ValueError" in err + assert "foo" in err def test_allow_clipboard_initializer(redirection_app) -> None: @@ -863,14 +863,14 @@ def test_allow_clipboard_initializer(redirection_app) -> None: # work in the test environment, like we do for test_send_to_paste_buffer() def test_allow_clipboard(base_app) -> None: base_app.allow_clipboard = False - out, err = run_cmd(base_app, 'help >') + out, err = run_cmd(base_app, "help >") assert not out assert "Clipboard access not allowed" in err def test_base_timing(base_app) -> None: base_app.feedback_to_output = False - out, err = run_cmd(base_app, 'set timing True') + out, err = run_cmd(base_app, "set timing True") expected = normalize( """timing - was: False now: True @@ -878,10 +878,10 @@ def test_base_timing(base_app) -> None: ) assert out == expected - if sys.platform == 'win32': - assert err[0].startswith('Elapsed: 0:00:00') + if sys.platform == "win32": + assert err[0].startswith("Elapsed: 0:00:00") else: - assert err[0].startswith('Elapsed: 0:00:00.0') + assert err[0].startswith("Elapsed: 0:00:00.0") def test_base_debug(base_app) -> None: @@ -889,12 +889,12 @@ def test_base_debug(base_app) -> None: base_app.editor = None # Make sure we get an exception, but cmd2 handles it - out, err = run_cmd(base_app, 'edit') + out, err = run_cmd(base_app, "edit") assert "ValueError: Please use 'set editor'" in err[0] assert "To enable full traceback" in err[3] # Set debug true - out, err = run_cmd(base_app, 'set debug True') + out, err = run_cmd(base_app, "set debug True") expected = normalize( """ debug - was: False @@ -904,26 +904,26 @@ def test_base_debug(base_app) -> None: assert out == expected # Verify that we now see the exception traceback - out, err = run_cmd(base_app, 'edit') - assert 'Traceback (most recent call last)' in err[0] + out, err = run_cmd(base_app, "edit") + assert "Traceback (most recent call last)" in err[0] def test_debug_not_settable(base_app) -> None: # Set debug to False and make it unsettable base_app.debug = False - base_app.remove_settable('debug') + base_app.remove_settable("debug") # Cause an exception by setting editor to None and running edit base_app.editor = None - _out, err = run_cmd(base_app, 'edit') + _out, err = run_cmd(base_app, "edit") # Since debug is unsettable, the user will not be given the option to enable a full traceback - assert err == ["ValueError: Please use 'set editor' to specify your text editing program of", 'choice.'] + assert err == ["ValueError: Please use 'set editor' to specify your text editing program of", "choice."] def test_blank_exception(mocker, base_app): mocker.patch("cmd2.Cmd.do_help", side_effect=Exception) - _out, err = run_cmd(base_app, 'help') + _out, err = run_cmd(base_app, "help") # When an exception has no message, the first error line is just its type. assert err[0] == "Exception" @@ -931,49 +931,49 @@ def test_blank_exception(mocker, base_app): def test_remove_settable_keyerror(base_app) -> None: with pytest.raises(KeyError): - base_app.remove_settable('fake') + base_app.remove_settable("fake") def test_edit_file(base_app, request, monkeypatch) -> None: # Set a fake editor just to make sure we have one. We aren't really going to call it due to the mock - base_app.editor = 'fooedit' + base_app.editor = "fooedit" # Mock out the subprocess.Popen call so we don't actually open an editor - m = mock.MagicMock(name='Popen') + m = mock.MagicMock(name="Popen") monkeypatch.setattr("subprocess.Popen", m) test_dir = os.path.dirname(request.module.__file__) - filename = os.path.join(test_dir, 'script.txt') + filename = os.path.join(test_dir, "script.txt") - run_cmd(base_app, f'edit {filename}') + run_cmd(base_app, f"edit {filename}") # We think we have an editor, so should expect a Popen call m.assert_called_once() -@pytest.mark.parametrize('file_name', odd_file_names) +@pytest.mark.parametrize("file_name", odd_file_names) def test_edit_file_with_odd_file_names(base_app, file_name, monkeypatch) -> None: """Test editor and file names with various patterns""" # Mock out the do_shell call to see what args are passed to it - shell_mock = mock.MagicMock(name='do_shell') + shell_mock = mock.MagicMock(name="do_shell") monkeypatch.setattr("cmd2.Cmd.do_shell", shell_mock) - base_app.editor = 'fooedit' - file_name = su.quote('nothingweird.py') + base_app.editor = "fooedit" + file_name = su.quote("nothingweird.py") run_cmd(base_app, f"edit {su.quote(file_name)}") shell_mock.assert_called_once_with(f'"fooedit" {su.quote(file_name)}') def test_edit_file_with_spaces(base_app, request, monkeypatch) -> None: # Set a fake editor just to make sure we have one. We aren't really going to call it due to the mock - base_app.editor = 'fooedit' + base_app.editor = "fooedit" # Mock out the subprocess.Popen call so we don't actually open an editor - m = mock.MagicMock(name='Popen') + m = mock.MagicMock(name="Popen") monkeypatch.setattr("subprocess.Popen", m) test_dir = os.path.dirname(request.module.__file__) - filename = os.path.join(test_dir, 'my commands.txt') + filename = os.path.join(test_dir, "my commands.txt") run_cmd(base_app, f'edit "{filename}"') @@ -983,13 +983,13 @@ def test_edit_file_with_spaces(base_app, request, monkeypatch) -> None: def test_edit_blank(base_app, monkeypatch) -> None: # Set a fake editor just to make sure we have one. We aren't really going to call it due to the mock - base_app.editor = 'fooedit' + base_app.editor = "fooedit" # Mock out the subprocess.Popen call so we don't actually open an editor - m = mock.MagicMock(name='Popen') + m = mock.MagicMock(name="Popen") monkeypatch.setattr("subprocess.Popen", m) - run_cmd(base_app, 'edit') + run_cmd(base_app, "edit") # We have an editor, so should expect a Popen call m.assert_called_once() @@ -997,7 +997,7 @@ def test_edit_blank(base_app, monkeypatch) -> None: def test_base_py_interactive(base_app) -> None: # Mock out the InteractiveConsole.interact() call so we don't actually wait for a user's response on stdin - m = mock.MagicMock(name='interact') + m = mock.MagicMock(name="interact") InteractiveConsole.interact = m run_cmd(base_app, "py") @@ -1007,13 +1007,13 @@ def test_base_py_interactive(base_app) -> None: def test_base_cmdloop_with_startup_commands() -> None: - intro = 'Hello World, this is an intro ...' + intro = "Hello World, this is an intro ..." # Need to patch sys.argv so cmd2 doesn't think it was called with arguments equal to the py.test args - testargs = ["prog", 'quit'] - expected = intro + '\n' + testargs = ["prog", "quit"] + expected = intro + "\n" - with mock.patch.object(sys, 'argv', testargs): + with mock.patch.object(sys, "argv", testargs): app = create_outsim_app() # Run the command loop with custom intro @@ -1026,15 +1026,15 @@ def test_base_cmdloop_with_startup_commands() -> None: def test_base_cmdloop_without_startup_commands(monkeypatch) -> None: # Need to patch sys.argv so cmd2 doesn't think it was called with arguments equal to the py.test args testargs = ["prog"] - with mock.patch.object(sys, 'argv', testargs): + with mock.patch.object(sys, "argv", testargs): app = create_outsim_app() - app.intro = 'Hello World, this is an intro ...' + app.intro = "Hello World, this is an intro ..." - read_command_mock = mock.MagicMock(name='_read_command_line', return_value='quit') + read_command_mock = mock.MagicMock(name="_read_command_line", return_value="quit") monkeypatch.setattr("cmd2.Cmd._read_command_line", read_command_mock) - expected = app.intro + '\n' + expected = app.intro + "\n" # Run the command loop app.cmdloop() @@ -1045,26 +1045,26 @@ def test_base_cmdloop_without_startup_commands(monkeypatch) -> None: def test_cmdfinalizations_runs(base_app, monkeypatch) -> None: """Make sure _run_cmdfinalization_hooks is run after each command.""" with ( - mock.patch('sys.stdin.isatty', mock.MagicMock(name='isatty', return_value=True)), - mock.patch('sys.stdin.fileno', mock.MagicMock(name='fileno', return_value=0)), + mock.patch("sys.stdin.isatty", mock.MagicMock(name="isatty", return_value=True)), + mock.patch("sys.stdin.fileno", mock.MagicMock(name="fileno", return_value=0)), ): monkeypatch.setattr(base_app.stdin, "fileno", lambda: 0) monkeypatch.setattr(base_app.stdin, "isatty", lambda: True) - cmd_fin = mock.MagicMock(name='cmdfinalization') + cmd_fin = mock.MagicMock(name="cmdfinalization") monkeypatch.setattr("cmd2.Cmd._run_cmdfinalization_hooks", cmd_fin) - base_app.onecmd_plus_hooks('help') + base_app.onecmd_plus_hooks("help") cmd_fin.assert_called_once() -@pytest.mark.skipif(sys.platform.startswith('win'), reason="termios is not available on Windows") +@pytest.mark.skipif(sys.platform.startswith("win"), reason="termios is not available on Windows") @pytest.mark.parametrize( - ('is_tty', 'settings_set', 'raised_exception', 'should_call'), + ("is_tty", "settings_set", "raised_exception", "should_call"), [ (True, True, None, True), - (True, True, 'termios_error', True), - (True, True, 'unsupported_operation', True), + (True, True, "termios_error", True), + (True, True, "unsupported_operation", True), (False, True, None, False), (True, False, None, False), ], @@ -1077,12 +1077,12 @@ def test_restore_termios_settings(base_app, monkeypatch, is_tty, settings_set, r termios_mock = mock.MagicMock() # The error attribute needs to be the actual exception for isinstance checks termios_mock.error = termios.error - monkeypatch.setitem(sys.modules, 'termios', termios_mock) + monkeypatch.setitem(sys.modules, "termios", termios_mock) # Set the exception to be raised by tcsetattr - if raised_exception == 'termios_error': + if raised_exception == "termios_error": termios_mock.tcsetattr.side_effect = termios.error("test termios error") - elif raised_exception == 'unsupported_operation': + elif raised_exception == "unsupported_operation": termios_mock.tcsetattr.side_effect = io.UnsupportedOperation("test io error") # Set initial termios settings so the logic will run @@ -1099,7 +1099,7 @@ def test_restore_termios_settings(base_app, monkeypatch, is_tty, settings_set, r # Run a command to trigger _run_cmdfinalization_hooks # This should not raise an exception - base_app.onecmd_plus_hooks('help') + base_app.onecmd_plus_hooks("help") # Verify that tcsetattr was called with the correct arguments if should_call: @@ -1121,10 +1121,10 @@ def test_sigint_handler(base_app) -> None: def test_raise_keyboard_interrupt(base_app) -> None: with pytest.raises(KeyboardInterrupt) as excinfo: base_app._raise_keyboard_interrupt() - assert 'Got a keyboard interrupt' in str(excinfo.value) + assert "Got a keyboard interrupt" in str(excinfo.value) -@pytest.mark.skipif(sys.platform.startswith('win'), reason="SIGTERM only handled on Linux/Mac") +@pytest.mark.skipif(sys.platform.startswith("win"), reason="SIGTERM only handled on Linux/Mac") def test_termination_signal_handler(base_app) -> None: with pytest.raises(SystemExit) as excinfo: base_app.termination_signal_handler(signal.SIGHUP, 1) @@ -1153,12 +1153,12 @@ def hook_failure(): def test_precmd_hook_success(base_app) -> None: - out = base_app.onecmd_plus_hooks('help') + out = base_app.onecmd_plus_hooks("help") assert out is False def test_precmd_hook_failure(hook_failure) -> None: - out = hook_failure.onecmd_plus_hooks('help') + out = hook_failure.onecmd_plus_hooks("help") assert out is True @@ -1178,35 +1178,35 @@ def say_app(): def test_ctrl_c_at_prompt(say_app, monkeypatch) -> None: - read_command_mock = mock.MagicMock(name='_read_command_line') - read_command_mock.side_effect = ['say hello', KeyboardInterrupt(), 'say goodbye', 'quit'] + read_command_mock = mock.MagicMock(name="_read_command_line") + read_command_mock.side_effect = ["say hello", KeyboardInterrupt(), "say goodbye", "quit"] monkeypatch.setattr("cmd2.Cmd._read_command_line", read_command_mock) say_app.cmdloop() # And verify the expected output to stdout out = say_app.stdout.getvalue() - assert out == 'hello\n^C\ngoodbye\n' + assert out == "hello\n^C\ngoodbye\n" def test_ctrl_d_at_prompt(say_app, monkeypatch) -> None: - read_command_mock = mock.MagicMock(name='_read_command_line') - read_command_mock.side_effect = ['say hello', EOFError()] + read_command_mock = mock.MagicMock(name="_read_command_line") + read_command_mock.side_effect = ["say hello", EOFError()] monkeypatch.setattr("cmd2.Cmd._read_command_line", read_command_mock) say_app.cmdloop() # And verify the expected output to stdout out = say_app.stdout.getvalue() - assert out == 'hello\n\n' + assert out == "hello\n\n" @pytest.mark.skipif( - sys.platform.startswith('win'), + sys.platform.startswith("win"), reason="Don't have a real Windows console with how we are currently running tests in GitHub Actions", ) @pytest.mark.parametrize( - ('msg', 'prompt', 'is_stale'), + ("msg", "prompt", "is_stale"), [ ("msg_text", None, False), ("msg_text", "new_prompt> ", False), @@ -1222,8 +1222,8 @@ def test_async_alert(base_app, msg, prompt, is_stale) -> None: import time with ( - mock.patch('cmd2.cmd2.print_formatted_text') as mock_print, - mock.patch('cmd2.cmd2.get_app') as mock_get_app, + mock.patch("cmd2.cmd2.print_formatted_text") as mock_print, + mock.patch("cmd2.cmd2.get_app") as mock_get_app, ): # Set up the chained mock: get_app() returns mock_app, which has invalidate() mock_app = mock.MagicMock() @@ -1289,15 +1289,15 @@ def test_visible_prompt() -> None: app = cmd2.Cmd() # This prompt has nothing which needs to be stripped - app.prompt = '(Cmd) ' + app.prompt = "(Cmd) " assert app.visible_prompt == app.prompt assert su.str_width(app.prompt) == len(app.prompt) # This prompt has color which needs to be stripped - color_prompt = stylize('InColor', style=Color.CYAN) + '> ' + color_prompt = stylize("InColor", style=Color.CYAN) + "> " app.prompt = color_prompt - assert app.visible_prompt == 'InColor> ' - assert su.str_width(app.prompt) == len('InColor> ') + assert app.visible_prompt == "InColor> " + assert su.str_width(app.prompt) == len("InColor> ") class HelpApp(cmd2.Cmd): @@ -1314,7 +1314,7 @@ def do_squat(self, arg) -> None: """This docstring help will never be shown because the help_squat method overrides it.""" def help_squat(self) -> None: - self.stdout.write('This command does diddly squat...\n') + self.stdout.write("This command does diddly squat...\n") def do_edit(self, arg) -> None: """This overrides the edit command and does nothing.""" @@ -1348,7 +1348,7 @@ def help_app(): def test_help_headers(capsys) -> None: help_app = HelpApp() - help_app.onecmd_plus_hooks('help') + help_app.onecmd_plus_hooks("help") out, _err = capsys.readouterr() assert help_app.doc_leader in out @@ -1358,47 +1358,47 @@ def test_help_headers(capsys) -> None: def test_custom_command_help(help_app) -> None: - out, _err = run_cmd(help_app, 'help squat') - expected = normalize('This command does diddly squat...') + out, _err = run_cmd(help_app, "help squat") + expected = normalize("This command does diddly squat...") assert out == expected assert help_app.last_result is True def test_custom_help_menu(help_app) -> None: - out, _err = run_cmd(help_app, 'help') + out, _err = run_cmd(help_app, "help") verify_help_text(help_app, out) assert help_app.last_result is True def test_help_no_help(help_app) -> None: - _out, err = run_cmd(help_app, 'help no_help') + _out, err = run_cmd(help_app, "help no_help") assert err[0].startswith("No help on no_help") assert help_app.last_result is False def test_help_overridden_method(help_app) -> None: - out, _err = run_cmd(help_app, 'help edit') - expected = normalize('This overrides the edit command and does nothing.') + out, _err = run_cmd(help_app, "help edit") + expected = normalize("This overrides the edit command and does nothing.") assert out == expected assert help_app.last_result is True def test_help_multiline_docstring(help_app) -> None: - out, _err = run_cmd(help_app, 'help multiline_docstr') - expected = normalize('This documentation\nis multiple lines\nand there are no\ntabs') + out, _err = run_cmd(help_app, "help multiline_docstr") + expected = normalize("This documentation\nis multiple lines\nand there are no\ntabs") assert out == expected assert help_app.last_result is True def test_miscellaneous_help_topic(help_app) -> None: - out, _err = run_cmd(help_app, 'help physics') + out, _err = run_cmd(help_app, "help physics") expected = normalize("Here is some help on physics.") assert out == expected assert help_app.last_result is True def test_help_verbose_uses_parser_description(help_app: HelpApp) -> None: - out, _err = run_cmd(help_app, 'help --verbose') + out, _err = run_cmd(help_app, "help --verbose") expected_verbose = utils.strip_doc_annotations(help_app.do_parser_cmd.__doc__) verify_help_text(help_app, out, verbose_strings=[expected_verbose]) @@ -1454,7 +1454,7 @@ class HelpCategoriesApp(cmd2.Cmd): def __init__(self, *args, **kwargs) -> None: super().__init__(*args, **kwargs) - @cmd2.with_category('Some Category') + @cmd2.with_category("Some Category") def do_diddly(self, arg) -> None: """This command does diddly""" @@ -1468,7 +1468,7 @@ def do_squat(self, arg) -> None: """This docstring help will never be shown because the help_squat method overrides it.""" def help_squat(self) -> None: - self.stdout.write('This command does diddly squat...\n') + self.stdout.write("This command does diddly squat...\n") def do_edit(self, arg) -> None: """This overrides the edit command and does nothing.""" @@ -1482,22 +1482,22 @@ def helpcat_app(): def test_help_cat_base(helpcat_app) -> None: - out, _err = run_cmd(helpcat_app, 'help') + out, _err = run_cmd(helpcat_app, "help") assert helpcat_app.last_result is True verify_help_text(helpcat_app, out) - help_text = ''.join(out) + help_text = "".join(out) assert helpcat_app.CUSTOM_CATEGORY in help_text assert helpcat_app.SOME_CATEGORY in help_text assert helpcat_app.DEFAULT_CATEGORY in help_text def test_help_cat_verbose(helpcat_app) -> None: - out, _err = run_cmd(helpcat_app, 'help --verbose') + out, _err = run_cmd(helpcat_app, "help --verbose") assert helpcat_app.last_result is True verify_help_text(helpcat_app, out) - help_text = ''.join(out) + help_text = "".join(out) assert helpcat_app.CUSTOM_CATEGORY in help_text assert helpcat_app.SOME_CATEGORY in help_text assert helpcat_app.DEFAULT_CATEGORY in help_text @@ -1507,38 +1507,38 @@ class SelectApp(cmd2.Cmd): def do_eat(self, arg) -> None: """Eat something, with a selection of sauces to choose from.""" # Pass in a single string of space-separated selections - sauce = self.select('sweet salty', 'Sauce? ') - result = '{food} with {sauce} sauce, yum!' + sauce = self.select("sweet salty", "Sauce? ") + result = "{food} with {sauce} sauce, yum!" result = result.format(food=arg, sauce=sauce) - self.stdout.write(result + '\n') + self.stdout.write(result + "\n") def do_study(self, arg) -> None: """Learn something, with a selection of subjects to choose from.""" # Pass in a list of strings for selections - subject = self.select(['math', 'science'], 'Subject? ') - result = f'Good luck learning {subject}!\n' + subject = self.select(["math", "science"], "Subject? ") + result = f"Good luck learning {subject}!\n" self.stdout.write(result) def do_procrastinate(self, arg) -> None: """Waste time in your manner of choice.""" # Pass in a list of tuples for selections leisure_activity = self.select( - [('Netflix and chill', 'Netflix'), ('YouTube', 'WebSurfing')], 'How would you like to procrastinate? ' + [("Netflix and chill", "Netflix"), ("YouTube", "WebSurfing")], "How would you like to procrastinate? " ) - result = f'Have fun procrasinating with {leisure_activity}!\n' + result = f"Have fun procrasinating with {leisure_activity}!\n" self.stdout.write(result) def do_play(self, arg) -> None: """Play your favorite musical instrument.""" # Pass in an uneven list of tuples for selections - instrument = self.select([('Guitar', 'Electric Guitar'), ('Drums',)], 'Instrument? ') - result = f'Charm us with the {instrument}...\n' + instrument = self.select([("Guitar", "Electric Guitar"), ("Drums",)], "Instrument? ") + result = f"Charm us with the {instrument}...\n" self.stdout.write(result) def do_return_type(self, arg) -> None: """Test that return values can be non-strings""" - choice = self.select([(1, 'Integer'), ("test_str", 'String'), (self.do_play, 'Method')], 'Choice? ') - result = f'The return type is {type(choice)}\n' + choice = self.select([(1, "Integer"), ("test_str", "String"), (self.do_play, "Method")], "Choice? ") + result = f"The return type is {type(choice)}\n" self.stdout.write(result) @@ -1548,10 +1548,10 @@ def select_app(): def test_select_options(select_app, monkeypatch) -> None: - read_input_mock = mock.MagicMock(name='read_input', return_value='2') + read_input_mock = mock.MagicMock(name="read_input", return_value="2") monkeypatch.setattr("cmd2.Cmd.read_input", read_input_mock) - food = 'bacon' + food = "bacon" out, _err = run_cmd(select_app, f"eat {food}") expected = normalize( f""" @@ -1562,20 +1562,20 @@ def test_select_options(select_app, monkeypatch) -> None: ) # Make sure our mock was called with the expected arguments - read_input_mock.assert_called_once_with('Sauce? ') + read_input_mock.assert_called_once_with("Sauce? ") # And verify the expected output to stdout assert out == expected def test_select_invalid_option_too_big(select_app, monkeypatch) -> None: - read_input_mock = mock.MagicMock(name='read_input') + read_input_mock = mock.MagicMock(name="read_input") # If side_effect is an iterable then each call to the mock will return the next value from the iterable. - read_input_mock.side_effect = ['3', '1'] # First pass an invalid selection, then pass a valid one + read_input_mock.side_effect = ["3", "1"] # First pass an invalid selection, then pass a valid one monkeypatch.setattr("cmd2.Cmd.read_input", read_input_mock) - food = 'fish' + food = "fish" out, _err = run_cmd(select_app, f"eat {food}") expected = normalize( f""" @@ -1587,7 +1587,7 @@ def test_select_invalid_option_too_big(select_app, monkeypatch) -> None: ) # Make sure our mock was called exactly twice with the expected arguments - arg = 'Sauce? ' + arg = "Sauce? " calls = [mock.call(arg), mock.call(arg)] read_input_mock.assert_has_calls(calls) assert read_input_mock.call_count == 2 @@ -1597,13 +1597,13 @@ def test_select_invalid_option_too_big(select_app, monkeypatch) -> None: def test_select_invalid_option_too_small(select_app, monkeypatch) -> None: - read_input_mock = mock.MagicMock(name='read_input') + read_input_mock = mock.MagicMock(name="read_input") # If side_effect is an iterable then each call to the mock will return the next value from the iterable. - read_input_mock.side_effect = ['0', '1'] # First pass an invalid selection, then pass a valid one + read_input_mock.side_effect = ["0", "1"] # First pass an invalid selection, then pass a valid one monkeypatch.setattr("cmd2.Cmd.read_input", read_input_mock) - food = 'fish' + food = "fish" out, _err = run_cmd(select_app, f"eat {food}") expected = normalize( f""" @@ -1615,7 +1615,7 @@ def test_select_invalid_option_too_small(select_app, monkeypatch) -> None: ) # Make sure our mock was called exactly twice with the expected arguments - arg = 'Sauce? ' + arg = "Sauce? " calls = [mock.call(arg), mock.call(arg)] read_input_mock.assert_has_calls(calls) assert read_input_mock.call_count == 2 @@ -1625,7 +1625,7 @@ def test_select_invalid_option_too_small(select_app, monkeypatch) -> None: def test_select_list_of_strings(select_app, monkeypatch) -> None: - read_input_mock = mock.MagicMock(name='read_input', return_value='2') + read_input_mock = mock.MagicMock(name="read_input", return_value="2") monkeypatch.setattr("cmd2.Cmd.read_input", read_input_mock) out, _err = run_cmd(select_app, "study") @@ -1634,18 +1634,18 @@ def test_select_list_of_strings(select_app, monkeypatch) -> None: 1. math 2. science Good luck learning {}! -""".format('science') +""".format("science") ) # Make sure our mock was called with the expected arguments - read_input_mock.assert_called_once_with('Subject? ') + read_input_mock.assert_called_once_with("Subject? ") # And verify the expected output to stdout assert out == expected def test_select_list_of_tuples(select_app, monkeypatch) -> None: - read_input_mock = mock.MagicMock(name='read_input', return_value='2') + read_input_mock = mock.MagicMock(name="read_input", return_value="2") monkeypatch.setattr("cmd2.Cmd.read_input", read_input_mock) out, _err = run_cmd(select_app, "procrastinate") @@ -1654,18 +1654,18 @@ def test_select_list_of_tuples(select_app, monkeypatch) -> None: 1. Netflix 2. WebSurfing Have fun procrasinating with {}! -""".format('YouTube') +""".format("YouTube") ) # Make sure our mock was called with the expected arguments - read_input_mock.assert_called_once_with('How would you like to procrastinate? ') + read_input_mock.assert_called_once_with("How would you like to procrastinate? ") # And verify the expected output to stdout assert out == expected def test_select_uneven_list_of_tuples(select_app, monkeypatch) -> None: - read_input_mock = mock.MagicMock(name='read_input', return_value='2') + read_input_mock = mock.MagicMock(name="read_input", return_value="2") monkeypatch.setattr("cmd2.Cmd.read_input", read_input_mock) out, _err = run_cmd(select_app, "play") @@ -1674,26 +1674,26 @@ def test_select_uneven_list_of_tuples(select_app, monkeypatch) -> None: 1. Electric Guitar 2. Drums Charm us with the {}... -""".format('Drums') +""".format("Drums") ) # Make sure our mock was called with the expected arguments - read_input_mock.assert_called_once_with('Instrument? ') + read_input_mock.assert_called_once_with("Instrument? ") # And verify the expected output to stdout assert out == expected @pytest.mark.parametrize( - ('selection', 'type_str'), + ("selection", "type_str"), [ - ('1', ""), - ('2', ""), - ('3', ""), + ("1", ""), + ("2", ""), + ("3", ""), ], ) def test_select_return_type(select_app, monkeypatch, selection, type_str) -> None: - read_input_mock = mock.MagicMock(name='read_input', return_value=selection) + read_input_mock = mock.MagicMock(name="read_input", return_value=selection) monkeypatch.setattr("cmd2.Cmd.read_input", read_input_mock) out, _err = run_cmd(select_app, "return_type") @@ -1707,7 +1707,7 @@ def test_select_return_type(select_app, monkeypatch, selection, type_str) -> Non ) # Make sure our mock was called with the expected arguments - read_input_mock.assert_called_once_with('Choice? ') + read_input_mock.assert_called_once_with("Choice? ") # And verify the expected output to stdout assert out == expected @@ -1715,14 +1715,14 @@ def test_select_return_type(select_app, monkeypatch, selection, type_str) -> Non def test_select_eof(select_app, monkeypatch) -> None: # Ctrl-D during select causes an EOFError that just reprompts the user - read_input_mock = mock.MagicMock(name='read_input', side_effect=[EOFError, 2]) + read_input_mock = mock.MagicMock(name="read_input", side_effect=[EOFError, 2]) monkeypatch.setattr("cmd2.Cmd.read_input", read_input_mock) - food = 'fish' + food = "fish" _out, _err = run_cmd(select_app, f"eat {food}") # Make sure our mock was called exactly twice with the expected arguments - arg = 'Sauce? ' + arg = "Sauce? " calls = [mock.call(arg), mock.call(arg)] read_input_mock.assert_has_calls(calls) assert read_input_mock.call_count == 2 @@ -1730,23 +1730,23 @@ def test_select_eof(select_app, monkeypatch) -> None: def test_select_ctrl_c(outsim_app, monkeypatch) -> None: # Ctrl-C during select prints ^C and raises a KeyboardInterrupt - read_input_mock = mock.MagicMock(name='read_input', side_effect=KeyboardInterrupt) + read_input_mock = mock.MagicMock(name="read_input", side_effect=KeyboardInterrupt) monkeypatch.setattr("cmd2.Cmd.read_input", read_input_mock) with pytest.raises(KeyboardInterrupt): - outsim_app.select([('Guitar', 'Electric Guitar'), ('Drums',)], 'Instrument? ') + outsim_app.select([("Guitar", "Electric Guitar"), ("Drums",)], "Instrument? ") out = outsim_app.stdout.getvalue() - assert out.rstrip().endswith('^C') + assert out.rstrip().endswith("^C") def test_select_choice_tty(outsim_app, monkeypatch) -> None: # Mock choice to return the first option - choice_mock = mock.MagicMock(name='choice', return_value='sweet') + choice_mock = mock.MagicMock(name="choice", return_value="sweet") monkeypatch.setattr("cmd2.cmd2.choice", choice_mock) - prompt = 'Sauce? ' - options = ['sweet', 'salty'] + prompt = "Sauce? " + options = ["sweet", "salty"] with create_pipe_input() as pipe_input: outsim_app.main_session = PromptSession( @@ -1756,17 +1756,17 @@ def test_select_choice_tty(outsim_app, monkeypatch) -> None: result = outsim_app.select(options, prompt) - assert result == 'sweet' - choice_mock.assert_called_once_with(message=prompt, options=[('sweet', 'sweet'), ('salty', 'salty')]) + assert result == "sweet" + choice_mock.assert_called_once_with(message=prompt, options=[("sweet", "sweet"), ("salty", "salty")]) def test_select_choice_tty_ctrl_c(outsim_app, monkeypatch) -> None: # Mock choice to raise KeyboardInterrupt - choice_mock = mock.MagicMock(name='choice', side_effect=KeyboardInterrupt) + choice_mock = mock.MagicMock(name="choice", side_effect=KeyboardInterrupt) monkeypatch.setattr("cmd2.cmd2.choice", choice_mock) - prompt = 'Sauce? ' - options = ['sweet', 'salty'] + prompt = "Sauce? " + options = ["sweet", "salty"] # Mock isatty to be True for both stdin and stdout with create_pipe_input() as pipe_input: @@ -1779,7 +1779,7 @@ def test_select_choice_tty_ctrl_c(outsim_app, monkeypatch) -> None: outsim_app.select(options, prompt) out = outsim_app.stdout.getvalue() - assert out.rstrip().endswith('^C') + assert out.rstrip().endswith("^C") def test_select_uneven_tuples_labels(outsim_app, monkeypatch) -> None: @@ -1787,19 +1787,19 @@ def test_select_uneven_tuples_labels(outsim_app, monkeypatch) -> None: # Case 1: (value, label) - normal # Case 2: (value,) - label should be value # Case 3: (value, None) - label should be value - options = [('v1', 'l1'), ('v2',), ('v3', None)] + options = [("v1", "l1"), ("v2",), ("v3", None)] # Mock read_input to return '1' - read_input_mock = mock.MagicMock(name='read_input', return_value='1') + read_input_mock = mock.MagicMock(name="read_input", return_value="1") monkeypatch.setattr("cmd2.Cmd.read_input", read_input_mock) - result = outsim_app.select(options, 'Choice? ') - assert result == 'v1' + result = outsim_app.select(options, "Choice? ") + assert result == "v1" out = outsim_app.stdout.getvalue() - assert '1. l1' in out - assert '2. v2' in out - assert '3. v3' in out + assert "1. l1" in out + assert "2. v2" in out + assert "3. v3" in out def test_select_indexable_no_len(outsim_app, monkeypatch) -> None: @@ -1808,38 +1808,38 @@ def test_select_indexable_no_len(outsim_app, monkeypatch) -> None: class IndexableNoLen: def __getitem__(self, item: int) -> str: if item == 0: - return 'value' + return "value" raise IndexError # Mock read_input to return '1' - read_input_mock = mock.MagicMock(name='read_input', return_value='1') + read_input_mock = mock.MagicMock(name="read_input", return_value="1") monkeypatch.setattr("cmd2.Cmd.read_input", read_input_mock) options = [IndexableNoLen()] - result = outsim_app.select(options, 'Choice? ') - assert result == 'value' + result = outsim_app.select(options, "Choice? ") + assert result == "value" out = outsim_app.stdout.getvalue() - assert '1. value' in out + assert "1. value" in out class HelpNoDocstringApp(cmd2.Cmd): greet_parser = cmd2.Cmd2ArgumentParser() - greet_parser.add_argument('-s', '--shout', action="store_true", help="N00B EMULATION MODE") + greet_parser.add_argument("-s", "--shout", action="store_true", help="N00B EMULATION MODE") @cmd2.with_argparser(greet_parser, with_unknown_args=True) def do_greet(self, opts, arg) -> None: - arg = ''.join(arg) + arg = "".join(arg) if opts.shout: arg = arg.upper() - self.stdout.write(arg + '\n') + self.stdout.write(arg + "\n") def test_help_with_no_docstring(capsys) -> None: app = HelpNoDocstringApp() - app.onecmd_plus_hooks('greet -h') + app.onecmd_plus_hooks("greet -h") out, err = capsys.readouterr() - assert err == '' + assert err == "" assert ( out == """Usage: greet [-h] [-s] @@ -1854,17 +1854,17 @@ def test_help_with_no_docstring(capsys) -> None: class MultilineApp(cmd2.Cmd): def __init__(self, *args, **kwargs) -> None: - super().__init__(*args, multiline_commands=['orate'], **kwargs) + super().__init__(*args, multiline_commands=["orate"], **kwargs) orate_parser = cmd2.Cmd2ArgumentParser() - orate_parser.add_argument('-s', '--shout', action="store_true", help="N00B EMULATION MODE") + orate_parser.add_argument("-s", "--shout", action="store_true", help="N00B EMULATION MODE") @cmd2.with_argparser(orate_parser, with_unknown_args=True) def do_orate(self, opts, arg) -> None: - arg = ''.join(arg) + arg = "".join(arg) if opts.shout: arg = arg.upper() - self.stdout.write(arg + '\n') + self.stdout.write(arg + "\n") @pytest.fixture @@ -1874,16 +1874,16 @@ def multiline_app(): def test_multiline_complete_empty_statement_raises_exception(multiline_app) -> None: with pytest.raises(exceptions.EmptyStatement): - multiline_app._complete_statement('') + multiline_app._complete_statement("") def test_multiline_complete_statement_without_terminator(multiline_app, monkeypatch) -> None: - read_command_mock = mock.MagicMock(name='_read_command_line', return_value='\n') + read_command_mock = mock.MagicMock(name="_read_command_line", return_value="\n") monkeypatch.setattr("cmd2.Cmd._read_command_line", read_command_mock) - command = 'orate' - args = 'hello world' - line = f'{command} {args}' + command = "orate" + args = "hello world" + line = f"{command} {args}" statement = multiline_app._complete_statement(line) assert statement == args assert statement.command == command @@ -1891,27 +1891,27 @@ def test_multiline_complete_statement_without_terminator(multiline_app, monkeypa def test_multiline_complete_statement_with_unclosed_quotes(multiline_app, monkeypatch) -> None: - read_command_mock = mock.MagicMock(name='_read_command_line', side_effect=['quotes', '" now closed;']) + read_command_mock = mock.MagicMock(name="_read_command_line", side_effect=["quotes", '" now closed;']) monkeypatch.setattr("cmd2.Cmd._read_command_line", read_command_mock) line = 'orate hi "partially open' statement = multiline_app._complete_statement(line) assert statement == 'hi "partially open\nquotes\n" now closed' - assert statement.command == 'orate' + assert statement.command == "orate" assert statement.multiline_command - assert statement.terminator == ';' + assert statement.terminator == ";" def test_multiline_input_line_to_statement(multiline_app, monkeypatch) -> None: # Verify _input_line_to_statement saves the fully entered input line for multiline commands - read_command_mock = mock.MagicMock(name='_read_command_line', side_effect=['person', '\n']) + read_command_mock = mock.MagicMock(name="_read_command_line", side_effect=["person", "\n"]) monkeypatch.setattr("cmd2.Cmd._read_command_line", read_command_mock) - line = 'orate hi' + line = "orate hi" statement = multiline_app._input_line_to_statement(line) - assert statement.raw == 'orate hi\nperson\n\n' - assert statement == 'hi person' - assert statement.command == 'orate' + assert statement.raw == "orate hi\nperson\n\n" + assert statement == "hi person" + assert statement.command == "orate" assert statement.multiline_command @@ -1919,7 +1919,7 @@ def test_multiline_history_added(multiline_app, monkeypatch) -> None: # Test that multiline commands are added to history as a single item run_cmd(multiline_app, "history --clear") - read_command_mock = mock.MagicMock(name='_read_command_line', side_effect=['person', '\n']) + read_command_mock = mock.MagicMock(name="_read_command_line", side_effect=["person", "\n"]) monkeypatch.setattr("cmd2.Cmd._read_command_line", read_command_mock) # run_cmd calls onecmd_plus_hooks which triggers history addition @@ -1933,7 +1933,7 @@ def test_multiline_history_with_quotes(multiline_app, monkeypatch) -> None: # Test combined multiline command with quotes is added to history correctly run_cmd(multiline_app, "history --clear") - read_command_mock = mock.MagicMock(name='_read_command_line', side_effect=[' and spaces ', ' "', ' in', 'quotes.', ';']) + read_command_mock = mock.MagicMock(name="_read_command_line", side_effect=[" and spaces ", ' "', " in", "quotes.", ";"]) monkeypatch.setattr("cmd2.Cmd._read_command_line", read_command_mock) line = 'orate Look, "There are newlines' @@ -1943,24 +1943,24 @@ def test_multiline_history_with_quotes(multiline_app, monkeypatch) -> None: history_item = multiline_app.history.get(1) history_lines = history_item.raw.splitlines() assert history_lines[0] == 'orate Look, "There are newlines' - assert history_lines[1] == ' and spaces ' + assert history_lines[1] == " and spaces " assert history_lines[2] == ' "' - assert history_lines[3] == ' in' - assert history_lines[4] == 'quotes.' - assert history_lines[5] == ';' + assert history_lines[3] == " in" + assert history_lines[4] == "quotes." + assert history_lines[5] == ";" def test_multiline_complete_statement_eof(multiline_app, monkeypatch): # Mock poutput to verify it's called - poutput_mock = mock.MagicMock(name='poutput') - monkeypatch.setattr(multiline_app, 'poutput', poutput_mock) + poutput_mock = mock.MagicMock(name="poutput") + monkeypatch.setattr(multiline_app, "poutput", poutput_mock) - read_raw_mock = mock.MagicMock(name='_read_raw_input', side_effect=EOFError) + read_raw_mock = mock.MagicMock(name="_read_raw_input", side_effect=EOFError) monkeypatch.setattr("cmd2.Cmd._read_raw_input", read_raw_mock) - command = 'orate' - args = 'hello world' - line = f'{command} {args}' + command = "orate" + args = "hello world" + line = f"{command} {args}" # This should call _read_command_line, get 'eof', set nextline to '\n', # and then parse the line with the newline terminator. @@ -1968,10 +1968,10 @@ def test_multiline_complete_statement_eof(multiline_app, monkeypatch): assert statement.command == command assert statement.args == args - assert statement.terminator == '\n' + assert statement.terminator == "\n" # Verify that poutput('\n') was called - poutput_mock.assert_called_once_with('\n') + poutput_mock.assert_called_once_with("\n") class CommandResultApp(cmd2.Cmd): @@ -1988,7 +1988,7 @@ def do_affirmative_no_data(self, arg) -> None: self.last_result = cmd2.CommandResult(arg) def do_negative_no_data(self, arg) -> None: - self.last_result = cmd2.CommandResult('', arg) + self.last_result = cmd2.CommandResult("", arg) @pytest.fixture @@ -1997,62 +1997,62 @@ def commandresult_app(): def test_commandresult_truthy(commandresult_app) -> None: - arg = 'foo' - run_cmd(commandresult_app, f'affirmative {arg}') + arg = "foo" + run_cmd(commandresult_app, f"affirmative {arg}") assert commandresult_app.last_result assert commandresult_app.last_result == cmd2.CommandResult(arg, data=True) - run_cmd(commandresult_app, f'affirmative_no_data {arg}') + run_cmd(commandresult_app, f"affirmative_no_data {arg}") assert commandresult_app.last_result assert commandresult_app.last_result == cmd2.CommandResult(arg) def test_commandresult_falsy(commandresult_app) -> None: - arg = 'bar' - run_cmd(commandresult_app, f'negative {arg}') + arg = "bar" + run_cmd(commandresult_app, f"negative {arg}") assert not commandresult_app.last_result assert commandresult_app.last_result == cmd2.CommandResult(arg, data=False) - run_cmd(commandresult_app, f'negative_no_data {arg}') + run_cmd(commandresult_app, f"negative_no_data {arg}") assert not commandresult_app.last_result - assert commandresult_app.last_result == cmd2.CommandResult('', arg) + assert commandresult_app.last_result == cmd2.CommandResult("", arg) -@pytest.mark.skipif(sys.platform.startswith('win'), reason="Test is problematic on GitHub Actions Windows runners") +@pytest.mark.skipif(sys.platform.startswith("win"), reason="Test is problematic on GitHub Actions Windows runners") def test_is_text_file_bad_input(base_app) -> None: # Test with a non-existent file with pytest.raises(FileNotFoundError): - utils.is_text_file('does_not_exist.txt') + utils.is_text_file("does_not_exist.txt") # Test with a directory with pytest.raises(IsADirectoryError): - utils.is_text_file('.') + utils.is_text_file(".") def test__eof(base_app) -> None: base_app.do_quit = mock.MagicMock(return_value=True) - assert base_app.do__eof('') - base_app.do_quit.assert_called_once_with('') + assert base_app.do__eof("") + base_app.do_quit.assert_called_once_with("") def test_quit(base_app) -> None: - assert base_app.do_quit('') + assert base_app.do_quit("") assert base_app.last_result is True def test_echo(capsys) -> None: app = cmd2.Cmd() app.echo = True - commands = ['help history'] + commands = ["help history"] app.runcmds_plus_hooks(commands) out, _err = capsys.readouterr() - assert out.startswith(f'{app.prompt}{commands[0]}\nUsage: history') + assert out.startswith(f"{app.prompt}{commands[0]}\nUsage: history") @pytest.mark.skipif( - sys.platform.startswith('win'), + sys.platform.startswith("win"), reason="Don't have a real Windows console with how we are currently running tests in GitHub Actions", ) def test_read_raw_input_tty(base_app: cmd2.Cmd) -> None: @@ -2121,7 +2121,7 @@ def test_resolve_completer_none(base_app: cmd2.Cmd) -> None: def test_resolve_completer_with_choices(base_app: cmd2.Cmd) -> None: from cmd2.pt_utils import Cmd2Completer - choices = ['apple', 'banana', 'cherry'] + choices = ["apple", "banana", "cherry"] completer = base_app._resolve_completer(choices=choices) assert isinstance(completer, Cmd2Completer) @@ -2206,16 +2206,16 @@ def test_custom_stdout() -> None: my_app = cmd2.Cmd(stdout=custom_output) # Simulate a command - my_app.onecmd('help') + my_app.onecmd("help") # Retrieve the output from the custom_output buffer captured_output = custom_output.getvalue() - assert 'history' in captured_output + assert "history" in captured_output def test_read_command_line_eof(base_app, monkeypatch) -> None: """Test that _read_command_line passes up EOFErrors.""" - read_raw_mock = mock.MagicMock(name='_read_raw_input', side_effect=EOFError) + read_raw_mock = mock.MagicMock(name="_read_raw_input", side_effect=EOFError) monkeypatch.setattr("cmd2.Cmd._read_raw_input", read_raw_mock) with pytest.raises(EOFError): @@ -2224,7 +2224,7 @@ def test_read_command_line_eof(base_app, monkeypatch) -> None: def test_read_input_eof(base_app, monkeypatch) -> None: """Test that read_input passes up EOFErrors.""" - read_raw_mock = mock.MagicMock(name='_read_raw_input', side_effect=EOFError) + read_raw_mock = mock.MagicMock(name="_read_raw_input", side_effect=EOFError) monkeypatch.setattr("cmd2.Cmd._read_raw_input", read_raw_mock) with pytest.raises(EOFError): @@ -2233,7 +2233,7 @@ def test_read_input_eof(base_app, monkeypatch) -> None: def test_read_secret(base_app, monkeypatch): """Test read_secret passes is_password=True to _read_raw_input.""" - with mock.patch.object(base_app, '_read_raw_input') as mock_reader: + with mock.patch.object(base_app, "_read_raw_input") as mock_reader: mock_reader.return_value = "my_secret" secret = base_app.read_secret("Secret: ") @@ -2242,12 +2242,12 @@ def test_read_secret(base_app, monkeypatch): # Verify it called _read_raw_input with is_password=True args, kwargs = mock_reader.call_args assert args[0] == "Secret: " - assert kwargs['is_password'] is True + assert kwargs["is_password"] is True def test_read_secret_eof(base_app, monkeypatch): """Test that read_secret passes up EOFErrors.""" - read_raw_mock = mock.MagicMock(name='_read_raw_input', side_effect=EOFError) + read_raw_mock = mock.MagicMock(name="_read_raw_input", side_effect=EOFError) monkeypatch.setattr("cmd2.Cmd._read_raw_input", read_raw_mock) with pytest.raises(EOFError): @@ -2261,8 +2261,8 @@ def test_read_input_passes_all_arguments_to_resolver(base_app): mock_parser = mock.MagicMock(name="parser") with ( - mock.patch.object(base_app, '_resolve_completer') as mock_resolver, - mock.patch.object(base_app, '_read_raw_input') as mock_reader, + mock.patch.object(base_app, "_resolve_completer") as mock_resolver, + mock.patch.object(base_app, "_read_raw_input") as mock_reader, ): mock_resolver.return_value = mock.MagicMock() mock_reader.return_value = mock.MagicMock() @@ -2287,9 +2287,9 @@ def test_read_input_passes_all_arguments_to_resolver(base_app): def test_read_input_history_is_passed_to_session(base_app, monkeypatch, mocker): - mock_session_cls = mocker.patch('cmd2.cmd2.PromptSession') - mock_history_cls = mocker.patch('cmd2.cmd2.InMemoryHistory') - read_raw_mock = mocker.MagicMock(name='_read_raw_input', return_value='command') + mock_session_cls = mocker.patch("cmd2.cmd2.PromptSession") + mock_history_cls = mocker.patch("cmd2.cmd2.InMemoryHistory") + read_raw_mock = mocker.MagicMock(name="_read_raw_input", return_value="command") monkeypatch.setattr("cmd2.Cmd._read_raw_input", read_raw_mock) # Test with custom history first @@ -2298,7 +2298,7 @@ def test_read_input_history_is_passed_to_session(base_app, monkeypatch, mocker): mock_history_cls.assert_called_once_with(my_history_list) called_kwargs = mock_session_cls.call_args.kwargs - assert called_kwargs['history'] == mock_history_cls.return_value + assert called_kwargs["history"] == mock_history_cls.return_value # Test with no history mock_history_cls.reset_mock() @@ -2308,7 +2308,7 @@ def test_read_input_history_is_passed_to_session(base_app, monkeypatch, mocker): mock_history_cls.assert_called_once_with() called_kwargs = mock_session_cls.call_args.kwargs - assert called_kwargs['history'] == mock_history_cls.return_value + assert called_kwargs["history"] == mock_history_cls.return_value def test_read_raw_input_session_usage_and_restore(base_app, mocker): @@ -2316,7 +2316,7 @@ def test_read_raw_input_session_usage_and_restore(base_app, mocker): base_app.main_session = mocker.MagicMock(name="main_session") # Make sure we look like a terminal - mocker.patch.object(base_app, '_is_tty_session', return_value=True) + mocker.patch.object(base_app, "_is_tty_session", return_value=True) command_text = "help alias" @@ -2329,7 +2329,7 @@ def check_and_return_input(*args, **kwargs): # Mock patch_stdout to prevent it from attempting to access the Windows # console buffer in a Windows test environment. - with mock.patch('cmd2.cmd2.patch_stdout'): + with mock.patch("cmd2.cmd2.patch_stdout"): result = base_app._read_raw_input("prompt> ", mock_session) assert result == command_text @@ -2346,7 +2346,7 @@ def test_read_raw_input_restores_on_error(base_app, mocker): base_app.main_session = mocker.MagicMock(name="main_session") # Make sure we look like a terminal - mocker.patch.object(base_app, '_is_tty_session', return_value=True) + mocker.patch.object(base_app, "_is_tty_session", return_value=True) def check_and_raise(*args, **kwargs): # Check if the active session was the one we passed in @@ -2357,7 +2357,7 @@ def check_and_raise(*args, **kwargs): # Mock patch_stdout to prevent it from attempting to access the Windows # console buffer in a Windows test environment. - with mock.patch('cmd2.cmd2.patch_stdout'), pytest.raises(KeyboardInterrupt): + with mock.patch("cmd2.cmd2.patch_stdout"), pytest.raises(KeyboardInterrupt): base_app._read_raw_input("prompt> ", mock_session) # Even though an error occurred, the finally block restored active session @@ -2365,10 +2365,10 @@ def check_and_raise(*args, **kwargs): def test_poutput_string(outsim_app) -> None: - msg = 'This is a test' + msg = "This is a test" outsim_app.poutput(msg) out = outsim_app.stdout.getvalue() - expected = msg + '\n' + expected = msg + "\n" assert out == expected @@ -2376,15 +2376,15 @@ def test_poutput_zero(outsim_app) -> None: msg = 0 outsim_app.poutput(msg) out = outsim_app.stdout.getvalue() - expected = str(msg) + '\n' + expected = str(msg) + "\n" assert out == expected def test_poutput_empty_string(outsim_app) -> None: - msg = '' + msg = "" outsim_app.poutput(msg) out = outsim_app.stdout.getvalue() - expected = '\n' + expected = "\n" assert out == expected @@ -2392,14 +2392,14 @@ def test_poutput_none(outsim_app) -> None: msg = None outsim_app.poutput(msg) out = outsim_app.stdout.getvalue() - expected = 'None\n' + expected = "None\n" assert out == expected @with_ansi_style(ru.AllowStyle.ALWAYS) @pytest.mark.parametrize( # Test a Rich Text and a string. - ('styled_msg', 'expected'), + ("styled_msg", "expected"), [ (Text("A Text object", style="cyan"), "\x1b[36mA Text object\x1b[0m\n"), (su.stylize("A str object", style="blue"), "\x1b[34mA str object\x1b[0m\n"), @@ -2414,7 +2414,7 @@ def test_poutput_ansi_always(styled_msg, expected, outsim_app) -> None: @with_ansi_style(ru.AllowStyle.NEVER) @pytest.mark.parametrize( # Test a Rich Text and a string. - ('styled_msg', 'expected'), + ("styled_msg", "expected"), [ (Text("A Text object", style="cyan"), "A Text object\n"), (su.stylize("A str object", style="blue"), "A str object\n"), @@ -2429,12 +2429,12 @@ def test_poutput_ansi_never(styled_msg, expected, outsim_app) -> None: @with_ansi_style(ru.AllowStyle.TERMINAL) def test_poutput_ansi_terminal(outsim_app) -> None: """Test that AllowStyle.TERMINAL strips style when redirecting.""" - msg = 'testing...' + msg = "testing..." colored_msg = Text(msg, style="cyan") outsim_app._redirecting = True outsim_app.poutput(colored_msg) out = outsim_app.stdout.getvalue() - expected = msg + '\n' + expected = msg + "\n" assert out == expected @@ -2489,7 +2489,7 @@ def test_poutput_no_wrap_and_overflow(outsim_app): @with_ansi_style(ru.AllowStyle.ALWAYS) def test_poutput_pretty_print(outsim_app): """Test that cmd2 passes objects through so they can be pretty-printed when highlighting is enabled.""" - dictionary = {1: 'hello', 2: 'person', 3: 'who', 4: 'codes'} + dictionary = {1: "hello", 2: "person", 3: "who", 4: "codes"} outsim_app.poutput(dictionary, highlight=True) out = outsim_app.stdout.getvalue() @@ -2520,11 +2520,11 @@ def test_poutput_all_keyword_args(outsim_app): @pytest.mark.parametrize( - 'stream', - ['stdout', 'stderr'], + "stream", + ["stdout", "stderr"], ) @pytest.mark.parametrize( - ('emoji', 'markup', 'highlight'), + ("emoji", "markup", "highlight"), [ (True, True, True), (False, False, False), @@ -2533,7 +2533,7 @@ def test_poutput_all_keyword_args(outsim_app): ) def test_get_core_print_console_caching(base_app: cmd2.Cmd, stream: str, emoji: bool, markup: bool, highlight: bool) -> None: """Test that printing consoles are cached and reused when settings match.""" - file = sys.stderr if stream == 'stderr' else base_app.stdout + file = sys.stderr if stream == "stderr" else base_app.stdout # Initial creation console1 = base_app._get_core_print_console( @@ -2558,12 +2558,12 @@ def test_get_core_print_console_caching(base_app: cmd2.Cmd, stream: str, emoji: @pytest.mark.parametrize( - 'stream', - ['stdout', 'stderr'], + "stream", + ["stdout", "stderr"], ) def test_get_core_print_console_invalidation(base_app: cmd2.Cmd, stream: str) -> None: """Test that changing settings, theme, or ALLOW_STYLE invalidates the cache.""" - file = sys.stderr if stream == 'stderr' else base_app.stdout + file = sys.stderr if stream == "stderr" else base_app.stdout # Initial creation console1 = base_app._get_core_print_console( @@ -2678,7 +2678,7 @@ def test_broken_pipe_error(outsim_app, monkeypatch, capsys): invalid_command_name = [ '""', # Blank name constants.COMMENT_CHAR, - '!no_shortcut', + "!no_shortcut", '">"', '"no>pe"', '"no spaces"', @@ -2689,8 +2689,8 @@ def test_broken_pipe_error(outsim_app, monkeypatch, capsys): def test_get_alias_choices(base_app: cmd2.Cmd) -> None: - run_cmd(base_app, 'alias create fake run_pyscript') - run_cmd(base_app, 'alias create ls !ls -hal') + run_cmd(base_app, "alias create fake run_pyscript") + run_cmd(base_app, "alias create ls !ls -hal") choices = base_app._get_alias_choices() @@ -2704,8 +2704,8 @@ def test_get_alias_choices(base_app: cmd2.Cmd) -> None: def test_get_macro_choices(base_app: cmd2.Cmd) -> None: - run_cmd(base_app, 'macro create foo !echo foo') - run_cmd(base_app, 'macro create bar !echo bar') + run_cmd(base_app, "macro create foo !echo foo") + run_cmd(base_app, "macro create bar !echo bar") choices = base_app._get_macro_choices() @@ -2720,8 +2720,8 @@ def test_get_macro_choices(base_app: cmd2.Cmd) -> None: def test_get_commands_aliases_and_macros_choices(base_app: cmd2.Cmd) -> None: # Add an alias and a macro - run_cmd(base_app, 'alias create fake_alias help') - run_cmd(base_app, 'macro create fake_macro !echo macro') + run_cmd(base_app, "alias create fake_alias help") + run_cmd(base_app, "macro create fake_macro !echo macro") # Add a command without a docstring import types @@ -2738,23 +2738,23 @@ def do_no_doc(self, arg): assert len(choices) == expected_count # Verify alias - alias_item = next((item for item in choices if item == 'fake_alias'), None) + alias_item = next((item for item in choices if item == "fake_alias"), None) assert alias_item is not None assert alias_item.display_meta == "Alias for: help" # Verify macro - macro_item = next((item for item in choices if item == 'fake_macro'), None) + macro_item = next((item for item in choices if item == "fake_macro"), None) assert macro_item is not None assert macro_item.display_meta == "Macro: !echo macro" # Verify command with docstring (help) - help_item = next((item for item in choices if item == 'help'), None) + help_item = next((item for item in choices if item == "help"), None) assert help_item is not None # First line of help docstring assert "List available commands" in help_item.display_meta # Verify command without docstring - no_doc_item = next((item for item in choices if item == 'no_doc'), None) + no_doc_item = next((item for item in choices if item == "no_doc"), None) assert no_doc_item is not None assert no_doc_item.display_meta == "" @@ -2784,48 +2784,48 @@ def test_get_settable_choices(base_app: cmd2.Cmd) -> None: def test_alias_no_subcommand(base_app) -> None: - _out, err = run_cmd(base_app, 'alias') + _out, err = run_cmd(base_app, "alias") assert "Usage: alias [-h]" in err[0] assert "Error: the following arguments are required: SUBCOMMAND" in err[1] def test_alias_create(base_app) -> None: # Create the alias - out, err = run_cmd(base_app, 'alias create fake run_pyscript') + out, err = run_cmd(base_app, "alias create fake run_pyscript") assert out == normalize("Alias 'fake' created") assert base_app.last_result is True # Use the alias - out, err = run_cmd(base_app, 'fake') + out, err = run_cmd(base_app, "fake") assert "the following arguments are required: script_path" in err[1] # See a list of aliases - out, err = run_cmd(base_app, 'alias list') - assert out == normalize('alias create fake run_pyscript') + out, err = run_cmd(base_app, "alias list") + assert out == normalize("alias create fake run_pyscript") assert len(base_app.last_result) == len(base_app.aliases) - assert base_app.last_result['fake'] == "run_pyscript" + assert base_app.last_result["fake"] == "run_pyscript" # Look up the new alias - out, err = run_cmd(base_app, 'alias list fake') - assert out == normalize('alias create fake run_pyscript') + out, err = run_cmd(base_app, "alias list fake") + assert out == normalize("alias create fake run_pyscript") assert len(base_app.last_result) == 1 - assert base_app.last_result['fake'] == "run_pyscript" + assert base_app.last_result["fake"] == "run_pyscript" # Overwrite alias - out, err = run_cmd(base_app, 'alias create fake help') + out, err = run_cmd(base_app, "alias create fake help") assert out == normalize("Alias 'fake' overwritten") assert base_app.last_result is True # Look up the updated alias - out, err = run_cmd(base_app, 'alias list fake') - assert out == normalize('alias create fake help') + out, err = run_cmd(base_app, "alias list fake") + assert out == normalize("alias create fake help") assert len(base_app.last_result) == 1 - assert base_app.last_result['fake'] == "help" + assert base_app.last_result["fake"] == "help" def test_nested_alias_usage(base_app) -> None: - run_cmd(base_app, 'alias create nested help') - run_cmd(base_app, 'alias create wrapper nested') + run_cmd(base_app, "alias create nested help") + run_cmd(base_app, "alias create wrapper nested") nested_out = run_cmd(base_app, "nested") wrapper_out = run_cmd(base_app, "wrapper") help_out = run_cmd(base_app, "help") @@ -2844,84 +2844,84 @@ def test_alias_create_with_quoted_tokens(base_app) -> None: assert out == normalize("Alias 'fake' created") # Look up the new alias and verify all quotes are preserved - out, _err = run_cmd(base_app, 'alias list fake') + out, _err = run_cmd(base_app, "alias list fake") assert out == normalize(create_command) assert len(base_app.last_result) == 1 assert base_app.last_result[alias_name] == alias_command -@pytest.mark.parametrize('alias_name', invalid_command_name) +@pytest.mark.parametrize("alias_name", invalid_command_name) def test_alias_create_invalid_name(base_app, alias_name, capsys) -> None: - _out, err = run_cmd(base_app, f'alias create {alias_name} help') + _out, err = run_cmd(base_app, f"alias create {alias_name} help") assert "Invalid alias name" in err[0] assert base_app.last_result is False def test_alias_create_with_command_name(base_app) -> None: - _out, err = run_cmd(base_app, 'alias create help stuff') + _out, err = run_cmd(base_app, "alias create help stuff") assert "Alias cannot have the same name as a command" in err[0] assert base_app.last_result is False def test_alias_create_with_macro_name(base_app) -> None: macro = "my_macro" - run_cmd(base_app, f'macro create {macro} help') - _out, err = run_cmd(base_app, f'alias create {macro} help') + run_cmd(base_app, f"macro create {macro} help") + _out, err = run_cmd(base_app, f"alias create {macro} help") assert "Alias cannot have the same name as a macro" in err[0] assert base_app.last_result is False def test_alias_that_resolves_into_comment(base_app) -> None: # Create the alias - out, err = run_cmd(base_app, 'alias create fake ' + constants.COMMENT_CHAR + ' blah blah') + out, err = run_cmd(base_app, "alias create fake " + constants.COMMENT_CHAR + " blah blah") assert out == normalize("Alias 'fake' created") # Use the alias - out, err = run_cmd(base_app, 'fake') + out, err = run_cmd(base_app, "fake") assert not out assert not err def test_alias_list_invalid_alias(base_app) -> None: # Look up invalid alias - _out, err = run_cmd(base_app, 'alias list invalid') + _out, err = run_cmd(base_app, "alias list invalid") assert "Alias 'invalid' not found" in err[0] assert base_app.last_result == {} def test_alias_delete(base_app) -> None: # Create an alias - run_cmd(base_app, 'alias create fake run_pyscript') + run_cmd(base_app, "alias create fake run_pyscript") # Delete the alias - out, _err = run_cmd(base_app, 'alias delete fake') + out, _err = run_cmd(base_app, "alias delete fake") assert out == normalize("Alias 'fake' deleted") assert base_app.last_result is True def test_alias_delete_all(base_app) -> None: - out, _err = run_cmd(base_app, 'alias delete --all') + out, _err = run_cmd(base_app, "alias delete --all") assert out == normalize("All aliases deleted") assert base_app.last_result is True def test_alias_delete_non_existing(base_app) -> None: - _out, err = run_cmd(base_app, 'alias delete fake') + _out, err = run_cmd(base_app, "alias delete fake") assert "Alias 'fake' does not exist" in err[0] assert base_app.last_result is True def test_alias_delete_no_name(base_app) -> None: - _out, err = run_cmd(base_app, 'alias delete') + _out, err = run_cmd(base_app, "alias delete") assert "Either --all or alias name(s)" in err[0] assert base_app.last_result is False def test_multiple_aliases(base_app) -> None: - alias1 = 'h1' - alias2 = 'h2' - run_cmd(base_app, f'alias create {alias1} help') - run_cmd(base_app, f'alias create {alias2} help -v') + alias1 = "h1" + alias2 = "h2" + run_cmd(base_app, f"alias create {alias1} help") + run_cmd(base_app, f"alias create {alias2} help -v") out, _err = run_cmd(base_app, alias1) verify_help_text(base_app, out) @@ -2930,43 +2930,43 @@ def test_multiple_aliases(base_app) -> None: def test_macro_no_subcommand(base_app) -> None: - _out, err = run_cmd(base_app, 'macro') + _out, err = run_cmd(base_app, "macro") assert "Usage: macro [-h]" in err[0] assert "Error: the following arguments are required: SUBCOMMAND" in err[1] def test_macro_create(base_app) -> None: # Create the macro - out, err = run_cmd(base_app, 'macro create fake run_pyscript') + out, err = run_cmd(base_app, "macro create fake run_pyscript") assert out == normalize("Macro 'fake' created") assert base_app.last_result is True # Use the macro - out, err = run_cmd(base_app, 'fake') + out, err = run_cmd(base_app, "fake") assert "the following arguments are required: script_path" in err[1] # See a list of macros - out, err = run_cmd(base_app, 'macro list') - assert out == normalize('macro create fake run_pyscript') + out, err = run_cmd(base_app, "macro list") + assert out == normalize("macro create fake run_pyscript") assert len(base_app.last_result) == len(base_app.macros) - assert base_app.last_result['fake'] == "run_pyscript" + assert base_app.last_result["fake"] == "run_pyscript" # Look up the new macro - out, err = run_cmd(base_app, 'macro list fake') - assert out == normalize('macro create fake run_pyscript') + out, err = run_cmd(base_app, "macro list fake") + assert out == normalize("macro create fake run_pyscript") assert len(base_app.last_result) == 1 - assert base_app.last_result['fake'] == "run_pyscript" + assert base_app.last_result["fake"] == "run_pyscript" # Overwrite macro - out, err = run_cmd(base_app, 'macro create fake help') + out, err = run_cmd(base_app, "macro create fake help") assert out == normalize("Macro 'fake' overwritten") assert base_app.last_result is True # Look up the updated macro - out, err = run_cmd(base_app, 'macro list fake') - assert out == normalize('macro create fake help') + out, err = run_cmd(base_app, "macro list fake") + assert out == normalize("macro create fake help") assert len(base_app.last_result) == 1 - assert base_app.last_result['fake'] == "help" + assert base_app.last_result["fake"] == "help" def test_macro_create_with_quoted_tokens(base_app) -> None: @@ -2980,76 +2980,76 @@ def test_macro_create_with_quoted_tokens(base_app) -> None: assert out == normalize("Macro 'fake' created") # Look up the new macro and verify all quotes are preserved - out, _err = run_cmd(base_app, 'macro list fake') + out, _err = run_cmd(base_app, "macro list fake") assert out == normalize(create_command) assert len(base_app.last_result) == 1 assert base_app.last_result[macro_name] == macro_command -@pytest.mark.parametrize('macro_name', invalid_command_name) +@pytest.mark.parametrize("macro_name", invalid_command_name) def test_macro_create_invalid_name(base_app, macro_name) -> None: - _out, err = run_cmd(base_app, f'macro create {macro_name} help') + _out, err = run_cmd(base_app, f"macro create {macro_name} help") assert "Invalid macro name" in err[0] assert base_app.last_result is False def test_macro_create_with_command_name(base_app) -> None: - _out, err = run_cmd(base_app, 'macro create help stuff') + _out, err = run_cmd(base_app, "macro create help stuff") assert "Macro cannot have the same name as a command" in err[0] assert base_app.last_result is False def test_macro_create_with_alias_name(base_app) -> None: macro = "my_macro" - run_cmd(base_app, f'alias create {macro} help') - _out, err = run_cmd(base_app, f'macro create {macro} help') + run_cmd(base_app, f"alias create {macro} help") + _out, err = run_cmd(base_app, f"macro create {macro} help") assert "Macro cannot have the same name as an alias" in err[0] assert base_app.last_result is False def test_macro_create_with_args(base_app) -> None: # Create the macro - out, _err = run_cmd(base_app, 'macro create fake {1} {2}') + out, _err = run_cmd(base_app, "macro create fake {1} {2}") assert out == normalize("Macro 'fake' created") # Run the macro - out, _err = run_cmd(base_app, 'fake help -v') + out, _err = run_cmd(base_app, "fake help -v") verify_help_text(base_app, out) def test_macro_create_with_escaped_args(base_app) -> None: # Create the macro - out, err = run_cmd(base_app, 'macro create fake help {{1}}') + out, err = run_cmd(base_app, "macro create fake help {{1}}") assert out == normalize("Macro 'fake' created") # Run the macro - out, err = run_cmd(base_app, 'fake') - assert err[0].startswith('No help on {1}') + out, err = run_cmd(base_app, "fake") + assert err[0].startswith("No help on {1}") def test_macro_usage_with_missing_args(base_app) -> None: # Create the macro - out, err = run_cmd(base_app, 'macro create fake help {1} {2}') + out, err = run_cmd(base_app, "macro create fake help {1} {2}") assert out == normalize("Macro 'fake' created") # Run the macro - out, err = run_cmd(base_app, 'fake arg1') + out, err = run_cmd(base_app, "fake arg1") assert "expects at least 2 arguments" in err[0] def test_macro_usage_with_extra_args(base_app) -> None: # Create the macro - out, _err = run_cmd(base_app, 'macro create fake help {1}') + out, _err = run_cmd(base_app, "macro create fake help {1}") assert out == normalize("Macro 'fake' created") # Run the macro - out, _err = run_cmd(base_app, 'fake alias create') + out, _err = run_cmd(base_app, "fake alias create") assert "Usage: alias create" in out[0] def test_nested_macro_usage(base_app) -> None: - run_cmd(base_app, 'macro create nested help') - run_cmd(base_app, 'macro create wrapper nested {1}') + run_cmd(base_app, "macro create nested help") + run_cmd(base_app, "macro create wrapper nested {1}") nested_out = run_cmd(base_app, "nested") help_out = run_cmd(base_app, "help") assert nested_out == help_out @@ -3062,85 +3062,85 @@ def test_nested_macro_usage(base_app) -> None: def test_macro_create_with_missing_arg_nums(base_app) -> None: # Create the macro - _out, err = run_cmd(base_app, 'macro create fake help {1} {3}') + _out, err = run_cmd(base_app, "macro create fake help {1} {3}") assert "Not all numbers between 1 and 3" in err[0] assert base_app.last_result is False def test_macro_create_with_invalid_arg_num(base_app) -> None: # Create the macro - _out, err = run_cmd(base_app, 'macro create fake help {1} {-1} {0}') + _out, err = run_cmd(base_app, "macro create fake help {1} {-1} {0}") assert "Argument numbers must be greater than 0" in err[0] assert base_app.last_result is False def test_macro_create_with_unicode_numbered_arg(base_app) -> None: # Create the macro expecting 1 argument - out, err = run_cmd(base_app, 'macro create fake help {\N{ARABIC-INDIC DIGIT ONE}}') + out, err = run_cmd(base_app, "macro create fake help {\N{ARABIC-INDIC DIGIT ONE}}") assert out == normalize("Macro 'fake' created") # Run the macro - out, err = run_cmd(base_app, 'fake') + out, err = run_cmd(base_app, "fake") assert "expects at least 1 argument" in err[0] def test_macro_create_with_missing_unicode_arg_nums(base_app) -> None: - _out, err = run_cmd(base_app, 'macro create fake help {1} {\N{ARABIC-INDIC DIGIT THREE}}') + _out, err = run_cmd(base_app, "macro create fake help {1} {\N{ARABIC-INDIC DIGIT THREE}}") assert "Not all numbers between 1 and 3" in err[0] assert base_app.last_result is False def test_macro_that_resolves_into_comment(base_app) -> None: # Create the macro - out, err = run_cmd(base_app, 'macro create fake {1} blah blah') + out, err = run_cmd(base_app, "macro create fake {1} blah blah") assert out == normalize("Macro 'fake' created") # Use the macro - out, err = run_cmd(base_app, 'fake ' + constants.COMMENT_CHAR) + out, err = run_cmd(base_app, "fake " + constants.COMMENT_CHAR) assert not out assert not err def test_macro_list_invalid_macro(base_app) -> None: # Look up invalid macro - _out, err = run_cmd(base_app, 'macro list invalid') + _out, err = run_cmd(base_app, "macro list invalid") assert "Macro 'invalid' not found" in err[0] assert base_app.last_result == {} def test_macro_delete(base_app) -> None: # Create an macro - run_cmd(base_app, 'macro create fake run_pyscript') + run_cmd(base_app, "macro create fake run_pyscript") # Delete the macro - out, _err = run_cmd(base_app, 'macro delete fake') + out, _err = run_cmd(base_app, "macro delete fake") assert out == normalize("Macro 'fake' deleted") assert base_app.last_result is True def test_macro_delete_all(base_app) -> None: - out, _err = run_cmd(base_app, 'macro delete --all') + out, _err = run_cmd(base_app, "macro delete --all") assert out == normalize("All macros deleted") assert base_app.last_result is True def test_macro_delete_non_existing(base_app) -> None: - _out, err = run_cmd(base_app, 'macro delete fake') + _out, err = run_cmd(base_app, "macro delete fake") assert "Macro 'fake' does not exist" in err[0] assert base_app.last_result is True def test_macro_delete_no_name(base_app) -> None: - _out, err = run_cmd(base_app, 'macro delete') + _out, err = run_cmd(base_app, "macro delete") assert "Either --all or macro name(s)" in err[0] assert base_app.last_result is False def test_multiple_macros(base_app) -> None: - macro1 = 'h1' - macro2 = 'h2' - run_cmd(base_app, f'macro create {macro1} help') - run_cmd(base_app, f'macro create {macro2} help -v') + macro1 = "h1" + macro2 = "h2" + run_cmd(base_app, f"macro create {macro1} help") + run_cmd(base_app, f"macro create {macro2} help -v") out, _err = run_cmd(base_app, macro1) verify_help_text(base_app, out) @@ -3157,7 +3157,7 @@ def test_nonexistent_macro(base_app) -> None: exception = None try: - base_app._resolve_macro(StatementParser().parse('fake')) + base_app._resolve_macro(StatementParser().parse("fake")) except KeyError as e: exception = e @@ -3166,7 +3166,7 @@ def test_nonexistent_macro(base_app) -> None: @pytest.mark.parametrize( # The line of text and whether to continue prompting to finish a multiline command. - ('line', 'should_continue'), + ("line", "should_continue"), [ # Empty lines ("", False), @@ -3210,13 +3210,13 @@ def test_should_continue_multiline(multiline_app: MultilineApp, line: str, shoul run_cmd(multiline_app, "macro create multi_mac orate {1}") run_cmd(multiline_app, "macro create wrapper_mac multi_mac {1} {2}") - with mock.patch('cmd2.cmd2.get_app', return_value=mock_app): + with mock.patch("cmd2.cmd2.get_app", return_value=mock_app): assert multiline_app._should_continue_multiline() is should_continue @with_ansi_style(ru.AllowStyle.ALWAYS) def test_perror_style(base_app, capsys) -> None: - msg = 'testing...' + msg = "testing..." base_app.perror(msg) _out, err = capsys.readouterr() assert err == "\x1b[91mtesting...\x1b[0m\n" @@ -3224,8 +3224,8 @@ def test_perror_style(base_app, capsys) -> None: @with_ansi_style(ru.AllowStyle.ALWAYS) def test_perror_no_style(base_app, capsys) -> None: - msg = 'testing...' - end = '\n' + msg = "testing..." + end = "\n" base_app.perror(msg, style=None) _out, err = capsys.readouterr() assert err == msg + end @@ -3233,8 +3233,8 @@ def test_perror_no_style(base_app, capsys) -> None: @with_ansi_style(ru.AllowStyle.ALWAYS) def test_psuccess(outsim_app) -> None: - msg = 'testing...' - end = '\n' + msg = "testing..." + end = "\n" outsim_app.psuccess(msg) expected = su.stylize(msg + end, style=Cmd2Style.SUCCESS) @@ -3243,8 +3243,8 @@ def test_psuccess(outsim_app) -> None: @with_ansi_style(ru.AllowStyle.ALWAYS) def test_pwarning(base_app, capsys) -> None: - msg = 'testing...' - end = '\n' + msg = "testing..." + end = "\n" base_app.pwarning(msg) expected = su.stylize(msg + end, style=Cmd2Style.WARNING) @@ -3254,7 +3254,7 @@ def test_pwarning(base_app, capsys) -> None: @with_ansi_style(ru.AllowStyle.ALWAYS) def test_pexcept_style(base_app, capsys) -> None: - msg = Exception('testing...') + msg = Exception("testing...") base_app.pexcept(msg) _out, err = capsys.readouterr() @@ -3264,14 +3264,14 @@ def test_pexcept_style(base_app, capsys) -> None: @with_ansi_style(ru.AllowStyle.NEVER) def test_pexcept_no_style(base_app, capsys) -> None: - msg = Exception('testing...') + msg = Exception("testing...") base_app.pexcept(msg) _out, err = capsys.readouterr() assert err.startswith("Exception: testing...") -@pytest.mark.parametrize('chop', [True, False]) +@pytest.mark.parametrize("chop", [True, False]) def test_ppaged_with_pager(outsim_app, monkeypatch, chop) -> None: """Force ppaged() to run the pager by mocking an actual terminal state.""" @@ -3284,11 +3284,11 @@ def test_ppaged_with_pager(outsim_app, monkeypatch, chop) -> None: stdout_mock.isatty.return_value = True monkeypatch.setattr(outsim_app, "stdout", stdout_mock) - if not sys.platform.startswith('win') and os.environ.get("TERM") is None: - monkeypatch.setenv('TERM', 'simulated') + if not sys.platform.startswith("win") and os.environ.get("TERM") is None: + monkeypatch.setenv("TERM", "simulated") # This will force ppaged to call Popen to run a pager - popen_mock = mock.MagicMock(name='Popen') + popen_mock = mock.MagicMock(name="Popen") monkeypatch.setattr("subprocess.Popen", popen_mock) outsim_app.ppaged("Test", chop=chop) @@ -3300,15 +3300,15 @@ def test_ppaged_with_pager(outsim_app, monkeypatch, chop) -> None: def test_ppaged_no_pager(outsim_app) -> None: """Since we're not in a fully-functional terminal, ppaged() will just call poutput().""" - msg = 'testing...' - end = '\n' + msg = "testing..." + end = "\n" outsim_app.ppaged(msg) out = outsim_app.stdout.getvalue() assert out == msg + end -@pytest.mark.skipif(sys.platform.startswith('win'), reason="termios is not available on Windows") -@pytest.mark.parametrize('has_tcsetpgrp', [True, False]) +@pytest.mark.skipif(sys.platform.startswith("win"), reason="termios is not available on Windows") +@pytest.mark.parametrize("has_tcsetpgrp", [True, False]) def test_ppaged_terminal_restoration(outsim_app, monkeypatch, has_tcsetpgrp) -> None: """Test terminal restoration in ppaged() after pager exits.""" # Make it look like we're in a terminal @@ -3321,8 +3321,8 @@ def test_ppaged_terminal_restoration(outsim_app, monkeypatch, has_tcsetpgrp) -> stdout_mock.isatty.return_value = True monkeypatch.setattr(outsim_app, "stdout", stdout_mock) - if not sys.platform.startswith('win') and os.environ.get("TERM") is None: - monkeypatch.setenv('TERM', 'simulated') + if not sys.platform.startswith("win") and os.environ.get("TERM") is None: + monkeypatch.setenv("TERM", "simulated") # Mock termios and signal since they are imported within the method termios_mock = mock.MagicMock() @@ -3330,10 +3330,10 @@ def test_ppaged_terminal_restoration(outsim_app, monkeypatch, has_tcsetpgrp) -> import termios termios_mock.error = termios.error - monkeypatch.setitem(sys.modules, 'termios', termios_mock) + monkeypatch.setitem(sys.modules, "termios", termios_mock) signal_mock = mock.MagicMock() - monkeypatch.setitem(sys.modules, 'signal', signal_mock) + monkeypatch.setitem(sys.modules, "signal", signal_mock) # Mock os.tcsetpgrp and os.getpgrp if has_tcsetpgrp: @@ -3343,7 +3343,7 @@ def test_ppaged_terminal_restoration(outsim_app, monkeypatch, has_tcsetpgrp) -> monkeypatch.delattr(os, "tcsetpgrp", raising=False) # Mock subprocess.Popen - popen_mock = mock.MagicMock(name='Popen') + popen_mock = mock.MagicMock(name="Popen") monkeypatch.setattr("subprocess.Popen", popen_mock) # Set initial termios settings so the logic will run @@ -3361,7 +3361,7 @@ def test_ppaged_terminal_restoration(outsim_app, monkeypatch, has_tcsetpgrp) -> termios_mock.tcsetattr.assert_called_once_with(0, termios_mock.TCSANOW, dummy_settings) -@pytest.mark.skipif(sys.platform.startswith('win'), reason="termios is not available on Windows") +@pytest.mark.skipif(sys.platform.startswith("win"), reason="termios is not available on Windows") def test_ppaged_terminal_restoration_exceptions(outsim_app, monkeypatch) -> None: """Test that terminal restoration in ppaged() handles exceptions gracefully.""" # Make it look like we're in a terminal @@ -3374,8 +3374,8 @@ def test_ppaged_terminal_restoration_exceptions(outsim_app, monkeypatch) -> None stdout_mock.isatty.return_value = True monkeypatch.setattr(outsim_app, "stdout", stdout_mock) - if not sys.platform.startswith('win') and os.environ.get("TERM") is None: - monkeypatch.setenv('TERM', 'simulated') + if not sys.platform.startswith("win") and os.environ.get("TERM") is None: + monkeypatch.setenv("TERM", "simulated") # Mock termios and make it raise an error termios_mock = mock.MagicMock() @@ -3383,16 +3383,16 @@ def test_ppaged_terminal_restoration_exceptions(outsim_app, monkeypatch) -> None termios_mock.error = termios.error termios_mock.tcsetattr.side_effect = termios.error("Restoration failed") - monkeypatch.setitem(sys.modules, 'termios', termios_mock) + monkeypatch.setitem(sys.modules, "termios", termios_mock) - monkeypatch.setitem(sys.modules, 'signal', mock.MagicMock()) + monkeypatch.setitem(sys.modules, "signal", mock.MagicMock()) # Mock os.tcsetpgrp and os.getpgrp to prevent OSError before tcsetattr monkeypatch.setattr(os, "tcsetpgrp", mock.Mock(), raising=False) monkeypatch.setattr(os, "getpgrp", mock.Mock(return_value=123), raising=False) # Mock subprocess.Popen - popen_mock = mock.MagicMock(name='Popen') + popen_mock = mock.MagicMock(name="Popen") monkeypatch.setattr("subprocess.Popen", popen_mock) # Set initial termios settings @@ -3405,7 +3405,7 @@ def test_ppaged_terminal_restoration_exceptions(outsim_app, monkeypatch) -> None assert termios_mock.tcsetattr.called -@pytest.mark.skipif(sys.platform.startswith('win'), reason="termios is not available on Windows") +@pytest.mark.skipif(sys.platform.startswith("win"), reason="termios is not available on Windows") def test_ppaged_terminal_restoration_no_settings(outsim_app, monkeypatch) -> None: """Test that terminal restoration in ppaged() is skipped if no settings are saved.""" # Make it look like we're in a terminal @@ -3418,15 +3418,15 @@ def test_ppaged_terminal_restoration_no_settings(outsim_app, monkeypatch) -> Non stdout_mock.isatty.return_value = True monkeypatch.setattr(outsim_app, "stdout", stdout_mock) - if not sys.platform.startswith('win') and os.environ.get("TERM") is None: - monkeypatch.setenv('TERM', 'simulated') + if not sys.platform.startswith("win") and os.environ.get("TERM") is None: + monkeypatch.setenv("TERM", "simulated") # Mock termios termios_mock = mock.MagicMock() - monkeypatch.setitem(sys.modules, 'termios', termios_mock) + monkeypatch.setitem(sys.modules, "termios", termios_mock) # Mock subprocess.Popen - popen_mock = mock.MagicMock(name='Popen') + popen_mock = mock.MagicMock(name="Popen") monkeypatch.setattr("subprocess.Popen", popen_mock) # Ensure initial termios settings is None @@ -3439,7 +3439,7 @@ def test_ppaged_terminal_restoration_no_settings(outsim_app, monkeypatch) -> Non assert not termios_mock.tcsetattr.called -@pytest.mark.skipif(sys.platform.startswith('win'), reason="termios is not available on Windows") +@pytest.mark.skipif(sys.platform.startswith("win"), reason="termios is not available on Windows") def test_ppaged_terminal_restoration_oserror(outsim_app, monkeypatch) -> None: """Test that terminal restoration in ppaged() handles OSError gracefully.""" # Make it look like we're in a terminal @@ -3452,11 +3452,11 @@ def test_ppaged_terminal_restoration_oserror(outsim_app, monkeypatch) -> None: stdout_mock.isatty.return_value = True monkeypatch.setattr(outsim_app, "stdout", stdout_mock) - if not sys.platform.startswith('win') and os.environ.get("TERM") is None: - monkeypatch.setenv('TERM', 'simulated') + if not sys.platform.startswith("win") and os.environ.get("TERM") is None: + monkeypatch.setenv("TERM", "simulated") # Mock signal - monkeypatch.setitem(sys.modules, 'signal', mock.MagicMock()) + monkeypatch.setitem(sys.modules, "signal", mock.MagicMock()) # Mock os.tcsetpgrp to raise OSError monkeypatch.setattr(os, "tcsetpgrp", mock.Mock(side_effect=OSError("Permission denied")), raising=False) @@ -3467,10 +3467,10 @@ def test_ppaged_terminal_restoration_oserror(outsim_app, monkeypatch) -> None: import termios termios_mock.error = termios.error - monkeypatch.setitem(sys.modules, 'termios', termios_mock) + monkeypatch.setitem(sys.modules, "termios", termios_mock) # Mock subprocess.Popen - popen_mock = mock.MagicMock(name='Popen') + popen_mock = mock.MagicMock(name="Popen") monkeypatch.setattr("subprocess.Popen", popen_mock) # Set initial termios settings @@ -3487,7 +3487,7 @@ def test_ppaged_terminal_restoration_oserror(outsim_app, monkeypatch) -> None: def test_ppretty(base_app: cmd2.Cmd) -> None: # Mock the Pretty class and the print_to() method - with mock.patch('cmd2.cmd2.Pretty') as mock_pretty, mock.patch.object(cmd2.Cmd, 'print_to') as mock_print_to: + with mock.patch("cmd2.cmd2.Pretty") as mock_pretty, mock.patch.object(cmd2.Cmd, "print_to") as mock_print_to: # Set up the mock return value for Pretty mock_pretty_obj = mock.Mock() mock_pretty.return_value = mock_pretty_obj @@ -3530,7 +3530,7 @@ def test_ppretty(base_app: cmd2.Cmd) -> None: # 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 = '' + statement = "" command, args, line = base_app.parseline(statement) assert not command assert not args @@ -3540,7 +3540,7 @@ def test_parseline_empty(base_app) -> None: def test_parseline_quoted(base_app) -> None: statement = " command with 'partially completed quotes " command, args, line = base_app.parseline(statement) - assert command == 'command' + assert command == "command" assert args == "with 'partially completed quotes " assert line == statement.lstrip() @@ -3558,7 +3558,7 @@ def test_onecmd_raw_str_quit(outsim_app) -> None: stop = outsim_app.onecmd(line) out = outsim_app.stdout.getvalue() assert stop - assert out == '' + assert out == "" def test_onecmd_add_to_history(outsim_app) -> None: @@ -3582,21 +3582,21 @@ def test_get_all_commands(base_app) -> None: # Verify that the base app has the expected commands commands = base_app.get_all_commands() expected_commands = [ - '_eof', - '_relative_run_script', - 'alias', - 'edit', - 'help', - 'history', - 'ipy', - 'macro', - 'py', - 'quit', - 'run_pyscript', - 'run_script', - 'set', - 'shell', - 'shortcuts', + "_eof", + "_relative_run_script", + "alias", + "edit", + "help", + "history", + "ipy", + "macro", + "py", + "quit", + "run_pyscript", + "run_script", + "set", + "shell", + "shortcuts", ] assert commands == expected_commands @@ -3620,10 +3620,10 @@ def help_my_cmd(self, args) -> None: pass app = TestApp() - assert 'my_cmd' in app.get_help_topics() + assert "my_cmd" in app.get_help_topics() - app.hidden_commands.append('my_cmd') - assert 'my_cmd' not in app.get_help_topics() + app.hidden_commands.append("my_cmd") + assert "my_cmd" not in app.get_help_topics() class ReplWithExitCode(cmd2.Cmd): @@ -3653,7 +3653,7 @@ def do_exit(self, arg_list) -> bool: def postloop(self) -> None: """Hook method executed once when the cmdloop() method is about to return.""" - self.poutput(f'exiting with code: {self.exit_code}') + self.poutput(f"exiting with code: {self.exit_code}") @pytest.fixture @@ -3666,10 +3666,10 @@ def exit_code_repl(): def test_exit_code_default(exit_code_repl, monkeypatch) -> None: app = exit_code_repl - read_command_mock = mock.MagicMock(name='_read_command_line', return_value='exit') + read_command_mock = mock.MagicMock(name="_read_command_line", return_value="exit") monkeypatch.setattr("cmd2.Cmd._read_command_line", read_command_mock) - expected = 'exiting with code: 0\n' + expected = "exiting with code: 0\n" # Run the command loop app.cmdloop() @@ -3680,10 +3680,10 @@ def test_exit_code_default(exit_code_repl, monkeypatch) -> None: def test_exit_code_nonzero(exit_code_repl, monkeypatch) -> None: app = exit_code_repl - read_input_mock = mock.MagicMock(name='_read_command_line', return_value='exit 23') + read_input_mock = mock.MagicMock(name="_read_command_line", return_value="exit 23") monkeypatch.setattr("cmd2.Cmd._read_command_line", read_input_mock) - expected = 'exiting with code: 23\n' + expected = "exiting with code: 23\n" # Run the command loop app.cmdloop() @@ -3707,18 +3707,18 @@ def do_echo_error(self, args) -> None: @with_ansi_style(ru.AllowStyle.ALWAYS) def test_ansi_pouterr_always_tty(mocker, capsys) -> None: app = AnsiApp() - mocker.patch.object(app.stdout, 'isatty', return_value=True) - mocker.patch.object(sys.stderr, 'isatty', return_value=True) + mocker.patch.object(app.stdout, "isatty", return_value=True) + mocker.patch.object(sys.stderr, "isatty", return_value=True) - expected_plain = 'oopsie\n' - expected_styled = su.stylize('oopsie\n', Cmd2Style.ERROR) + expected_plain = "oopsie\n" + expected_styled = su.stylize("oopsie\n", Cmd2Style.ERROR) - app.onecmd_plus_hooks('echo_error oopsie') + app.onecmd_plus_hooks("echo_error oopsie") out, err = capsys.readouterr() assert out == expected_styled assert err == expected_styled - app.onecmd_plus_hooks('echo oopsie') + app.onecmd_plus_hooks("echo oopsie") out, err = capsys.readouterr() assert out == expected_plain assert err == expected_styled @@ -3727,18 +3727,18 @@ def test_ansi_pouterr_always_tty(mocker, capsys) -> None: @with_ansi_style(ru.AllowStyle.ALWAYS) def test_ansi_pouterr_always_notty(mocker, capsys) -> None: app = AnsiApp() - mocker.patch.object(app.stdout, 'isatty', return_value=False) - mocker.patch.object(sys.stderr, 'isatty', return_value=False) + mocker.patch.object(app.stdout, "isatty", return_value=False) + mocker.patch.object(sys.stderr, "isatty", return_value=False) - expected_plain = 'oopsie\n' - expected_styled = su.stylize('oopsie\n', Cmd2Style.ERROR) + expected_plain = "oopsie\n" + expected_styled = su.stylize("oopsie\n", Cmd2Style.ERROR) - app.onecmd_plus_hooks('echo_error oopsie') + app.onecmd_plus_hooks("echo_error oopsie") out, err = capsys.readouterr() assert out == expected_styled assert err == expected_styled - app.onecmd_plus_hooks('echo oopsie') + app.onecmd_plus_hooks("echo oopsie") out, err = capsys.readouterr() assert out == expected_plain assert err == expected_styled @@ -3747,18 +3747,18 @@ def test_ansi_pouterr_always_notty(mocker, capsys) -> None: @with_ansi_style(ru.AllowStyle.TERMINAL) def test_ansi_terminal_tty(mocker, capsys) -> None: app = AnsiApp() - mocker.patch.object(app.stdout, 'isatty', return_value=True) - mocker.patch.object(sys.stderr, 'isatty', return_value=True) + mocker.patch.object(app.stdout, "isatty", return_value=True) + mocker.patch.object(sys.stderr, "isatty", return_value=True) - expected_plain = 'oopsie\n' - expected_styled = su.stylize('oopsie\n', Cmd2Style.ERROR) + expected_plain = "oopsie\n" + expected_styled = su.stylize("oopsie\n", Cmd2Style.ERROR) - app.onecmd_plus_hooks('echo_error oopsie') + app.onecmd_plus_hooks("echo_error oopsie") out, err = capsys.readouterr() assert out == expected_styled assert err == expected_styled - app.onecmd_plus_hooks('echo oopsie') + app.onecmd_plus_hooks("echo oopsie") out, err = capsys.readouterr() assert out == expected_plain assert err == expected_styled @@ -3767,46 +3767,46 @@ def test_ansi_terminal_tty(mocker, capsys) -> None: @with_ansi_style(ru.AllowStyle.TERMINAL) def test_ansi_terminal_notty(mocker, capsys) -> None: app = AnsiApp() - mocker.patch.object(app.stdout, 'isatty', return_value=False) - mocker.patch.object(sys.stderr, 'isatty', return_value=False) + mocker.patch.object(app.stdout, "isatty", return_value=False) + mocker.patch.object(sys.stderr, "isatty", return_value=False) - app.onecmd_plus_hooks('echo_error oopsie') + app.onecmd_plus_hooks("echo_error oopsie") out, err = capsys.readouterr() - assert out == err == 'oopsie\n' + assert out == err == "oopsie\n" - app.onecmd_plus_hooks('echo oopsie') + app.onecmd_plus_hooks("echo oopsie") out, err = capsys.readouterr() - assert out == err == 'oopsie\n' + assert out == err == "oopsie\n" @with_ansi_style(ru.AllowStyle.NEVER) def test_ansi_never_tty(mocker, capsys) -> None: app = AnsiApp() - mocker.patch.object(app.stdout, 'isatty', return_value=True) - mocker.patch.object(sys.stderr, 'isatty', return_value=True) + mocker.patch.object(app.stdout, "isatty", return_value=True) + mocker.patch.object(sys.stderr, "isatty", return_value=True) - app.onecmd_plus_hooks('echo_error oopsie') + app.onecmd_plus_hooks("echo_error oopsie") out, err = capsys.readouterr() - assert out == err == 'oopsie\n' + assert out == err == "oopsie\n" - app.onecmd_plus_hooks('echo oopsie') + app.onecmd_plus_hooks("echo oopsie") out, err = capsys.readouterr() - assert out == err == 'oopsie\n' + assert out == err == "oopsie\n" @with_ansi_style(ru.AllowStyle.NEVER) def test_ansi_never_notty(mocker, capsys) -> None: app = AnsiApp() - mocker.patch.object(app.stdout, 'isatty', return_value=False) - mocker.patch.object(sys.stderr, 'isatty', return_value=False) + mocker.patch.object(app.stdout, "isatty", return_value=False) + mocker.patch.object(sys.stderr, "isatty", return_value=False) - app.onecmd_plus_hooks('echo_error oopsie') + app.onecmd_plus_hooks("echo_error oopsie") out, err = capsys.readouterr() - assert out == err == 'oopsie\n' + assert out == err == "oopsie\n" - app.onecmd_plus_hooks('echo oopsie') + app.onecmd_plus_hooks("echo oopsie") out, err = capsys.readouterr() - assert out == err == 'oopsie\n' + assert out == err == "oopsie\n" class DisableCommandsApp(cmd2.Cmd): @@ -3823,10 +3823,10 @@ def do_has_helper_func(self, arg) -> None: self.poutput("The real has_helper_func") def help_has_helper_func(self) -> None: - self.poutput('Help for has_helper_func') + self.poutput("Help for has_helper_func") def complete_has_helper_func(self, *args) -> Completions: - return Completions.from_values(['result']) + return Completions.from_values(["result"]) @cmd2.with_category(category_name) def do_has_no_helper_func(self, arg) -> None: @@ -3862,7 +3862,7 @@ def test_disable_and_enable_category(disable_commands_app: DisableCommandsApp) - ########################################################################## # Disable the category ########################################################################## - message_to_print = 'These commands are currently disabled' + message_to_print = "These commands are currently disabled" # Disable commands which are decorated with a category disable_commands_app.disable_category(disable_commands_app.category_name, message_to_print) @@ -3871,35 +3871,35 @@ def test_disable_and_enable_category(disable_commands_app: DisableCommandsApp) - disable_commands_app.disable_category(disable_commands_app.DEFAULT_CATEGORY, message_to_print) # Make sure all the commands and help on those commands displays the message - out, err = run_cmd(disable_commands_app, 'has_helper_func') + out, err = run_cmd(disable_commands_app, "has_helper_func") assert err[0].startswith(message_to_print) - out, err = run_cmd(disable_commands_app, 'help has_helper_func') + out, err = run_cmd(disable_commands_app, "help has_helper_func") assert err[0].startswith(message_to_print) - out, err = run_cmd(disable_commands_app, 'has_no_helper_func') + out, err = run_cmd(disable_commands_app, "has_no_helper_func") assert err[0].startswith(message_to_print) - out, err = run_cmd(disable_commands_app, 'help has_no_helper_func') + out, err = run_cmd(disable_commands_app, "help has_no_helper_func") assert err[0].startswith(message_to_print) - out, err = run_cmd(disable_commands_app, 'is_not_decorated') + out, err = run_cmd(disable_commands_app, "is_not_decorated") assert err[0].startswith(message_to_print) - out, err = run_cmd(disable_commands_app, 'help is_not_decorated') + out, err = run_cmd(disable_commands_app, "help is_not_decorated") assert err[0].startswith(message_to_print) # Make sure neither function completes - text = '' - line = f'has_helper_func {text}' + text = "" + line = f"has_helper_func {text}" endidx = len(line) begidx = endidx - len(text) completions = disable_commands_app.complete(text, line, begidx, endidx) assert not completions - text = '' - line = f'has_no_helper_func {text}' + text = "" + line = f"has_no_helper_func {text}" endidx = len(line) begidx = endidx - len(text) @@ -3908,12 +3908,12 @@ def test_disable_and_enable_category(disable_commands_app: DisableCommandsApp) - # Make sure both commands are invisible visible_commands = disable_commands_app.get_visible_commands() - assert 'has_helper_func' not in visible_commands - assert 'has_no_helper_func' not in visible_commands + assert "has_helper_func" not in visible_commands + assert "has_no_helper_func" not in visible_commands # Make sure get_help_topics() filters out disabled commands help_topics = disable_commands_app.get_help_topics() - assert 'has_helper_func' not in help_topics + assert "has_helper_func" not in help_topics ########################################################################## # Enable the category @@ -3925,24 +3925,24 @@ def test_disable_and_enable_category(disable_commands_app: DisableCommandsApp) - disable_commands_app.enable_category(disable_commands_app.DEFAULT_CATEGORY) # Make sure all the commands and help on those commands are restored - out, err = run_cmd(disable_commands_app, 'has_helper_func') + out, err = run_cmd(disable_commands_app, "has_helper_func") assert out[0] == "The real has_helper_func" - out, err = run_cmd(disable_commands_app, 'help has_helper_func') + out, err = run_cmd(disable_commands_app, "help has_helper_func") assert out[0] == "Help for has_helper_func" - out, err = run_cmd(disable_commands_app, 'has_no_helper_func') + out, err = run_cmd(disable_commands_app, "has_no_helper_func") assert out[0] == "The real has_no_helper_func" - out, err = run_cmd(disable_commands_app, 'help has_no_helper_func') + out, err = run_cmd(disable_commands_app, "help has_no_helper_func") assert out[0] == "Help for has_no_helper_func" - out, err = run_cmd(disable_commands_app, 'is_not_decorated') + out, err = run_cmd(disable_commands_app, "is_not_decorated") assert out[0] == "The real is_not_decorated" # has_helper_func should complete now - text = '' - line = f'has_helper_func {text}' + text = "" + line = f"has_helper_func {text}" endidx = len(line) begidx = endidx - len(text) @@ -3950,8 +3950,8 @@ def test_disable_and_enable_category(disable_commands_app: DisableCommandsApp) - assert completions[0].text == "result" # has_no_helper_func had no completer originally, so there should be no results - text = '' - line = f'has_no_helper_func {text}' + text = "" + line = f"has_no_helper_func {text}" endidx = len(line) begidx = endidx - len(text) @@ -3960,19 +3960,19 @@ def test_disable_and_enable_category(disable_commands_app: DisableCommandsApp) - # Make sure all commands are visible visible_commands = disable_commands_app.get_visible_commands() - assert 'has_helper_func' in visible_commands - assert 'has_no_helper_func' in visible_commands - assert 'is_not_decorated' in visible_commands + assert "has_helper_func" in visible_commands + assert "has_no_helper_func" in visible_commands + assert "is_not_decorated" in visible_commands # Make sure get_help_topics() contains our help function help_topics = disable_commands_app.get_help_topics() - assert 'has_helper_func' in help_topics + assert "has_helper_func" in help_topics def test_enable_enabled_command(disable_commands_app) -> None: # Test enabling a command that is not disabled saved_len = len(disable_commands_app.disabled_commands) - disable_commands_app.enable_command('has_helper_func') + disable_commands_app.enable_command("has_helper_func") # The number of disabled commands should not have changed assert saved_len == len(disable_commands_app.disabled_commands) @@ -3980,13 +3980,13 @@ def test_enable_enabled_command(disable_commands_app) -> None: def test_disable_fake_command(disable_commands_app) -> None: with pytest.raises(AttributeError): - disable_commands_app.disable_command('fake', 'fake message') + disable_commands_app.disable_command("fake", "fake message") def test_disable_command_twice(disable_commands_app) -> None: saved_len = len(disable_commands_app.disabled_commands) - message_to_print = 'These commands are currently disabled' - disable_commands_app.disable_command('has_helper_func', message_to_print) + message_to_print = "These commands are currently disabled" + disable_commands_app.disable_command("has_helper_func", message_to_print) # The number of disabled commands should have increased one new_len = len(disable_commands_app.disabled_commands) @@ -3994,26 +3994,26 @@ def test_disable_command_twice(disable_commands_app) -> None: saved_len = new_len # Disable again and the length should not change - disable_commands_app.disable_command('has_helper_func', message_to_print) + disable_commands_app.disable_command("has_helper_func", message_to_print) new_len = len(disable_commands_app.disabled_commands) assert saved_len == new_len def test_disabled_command_not_in_history(disable_commands_app) -> None: - message_to_print = 'These commands are currently disabled' - disable_commands_app.disable_command('has_helper_func', message_to_print) + message_to_print = "These commands are currently disabled" + disable_commands_app.disable_command("has_helper_func", message_to_print) saved_len = len(disable_commands_app.history) - run_cmd(disable_commands_app, 'has_helper_func') + run_cmd(disable_commands_app, "has_helper_func") assert saved_len == len(disable_commands_app.history) def test_disabled_message_command_name(disable_commands_app) -> None: - message_to_print = f'{COMMAND_NAME} is currently disabled' - disable_commands_app.disable_command('has_helper_func', message_to_print) + message_to_print = f"{COMMAND_NAME} is currently disabled" + disable_commands_app.disable_command("has_helper_func", message_to_print) - _out, err = run_cmd(disable_commands_app, 'has_helper_func') - assert err[0].startswith('has_helper_func is currently disabled') + _out, err = run_cmd(disable_commands_app, "has_helper_func") + assert err[0].startswith("has_helper_func is currently disabled") def test_register_command_in_enabled_category(disable_commands_app) -> None: @@ -4026,10 +4026,10 @@ def test_register_command_in_enabled_category(disable_commands_app) -> None: cs = DisableCommandSet() disable_commands_app.register_command_set(cs) - out, _err = run_cmd(disable_commands_app, 'new_command') + out, _err = run_cmd(disable_commands_app, "new_command") assert out[0] == "The real new_command" - out, _err = run_cmd(disable_commands_app, 'cs_is_not_decorated') + out, _err = run_cmd(disable_commands_app, "cs_is_not_decorated") assert out[0] == "The real cs_is_not_decorated" @@ -4045,17 +4045,17 @@ def test_register_command_in_disabled_category(disable_commands_app) -> None: cs = DisableCommandSet() disable_commands_app.register_command_set(cs) - _out, err = run_cmd(disable_commands_app, 'new_command') + _out, err = run_cmd(disable_commands_app, "new_command") assert err[0] == message_to_print - _out, err = run_cmd(disable_commands_app, 'cs_is_not_decorated') + _out, err = run_cmd(disable_commands_app, "cs_is_not_decorated") assert err[0] == message_to_print def test_enable_enabled_category(disable_commands_app) -> None: # Test enabling a category that is not disabled saved_len = len(disable_commands_app.disabled_categories) - disable_commands_app.enable_category('Test Category') + disable_commands_app.enable_category("Test Category") # The number of disabled categories should not have changed assert saved_len == len(disable_commands_app.disabled_categories) @@ -4063,8 +4063,8 @@ def test_enable_enabled_category(disable_commands_app) -> None: def test_disable_category_twice(disable_commands_app) -> None: saved_len = len(disable_commands_app.disabled_categories) - message_to_print = 'These commands are currently disabled' - disable_commands_app.disable_category('Test Category', message_to_print) + message_to_print = "These commands are currently disabled" + disable_commands_app.disable_category("Test Category", message_to_print) # The number of disabled categories should have increased one new_len = len(disable_commands_app.disabled_categories) @@ -4072,18 +4072,18 @@ def test_disable_category_twice(disable_commands_app) -> None: saved_len = new_len # Disable again and the length should not change - disable_commands_app.disable_category('Test Category', message_to_print) + disable_commands_app.disable_category("Test Category", message_to_print) new_len = len(disable_commands_app.disabled_categories) assert saved_len == new_len -@pytest.mark.parametrize('silence_startup_script', [True, False]) +@pytest.mark.parametrize("silence_startup_script", [True, False]) def test_startup_script(request, capsys, silence_startup_script) -> None: test_dir = os.path.dirname(request.module.__file__) - startup_script = os.path.join(test_dir, '.cmd2rc') + startup_script = os.path.join(test_dir, ".cmd2rc") app = cmd2.Cmd(allow_cli_args=False, startup_script=startup_script, silence_startup_script=silence_startup_script) assert len(app._startup_commands) == 1 - app._startup_commands.append('quit') + app._startup_commands.append("quit") app.cmdloop() out, _err = capsys.readouterr() @@ -4092,17 +4092,17 @@ def test_startup_script(request, capsys, silence_startup_script) -> None: else: assert out - out, _err = run_cmd(app, 'alias list') + out, _err = run_cmd(app, "alias list") assert len(out) > 1 - assert 'alias create ls' in out[0] + assert "alias create ls" in out[0] -@pytest.mark.parametrize('startup_script', odd_file_names) +@pytest.mark.parametrize("startup_script", odd_file_names) def test_startup_script_with_odd_file_names(startup_script) -> None: """Test file names with various patterns""" # Mock os.path.exists to trick cmd2 into adding this script to its startup commands saved_exists = os.path.exists - os.path.exists = mock.MagicMock(name='exists', return_value=True) + os.path.exists = mock.MagicMock(name="exists", return_value=True) app = cmd2.Cmd(allow_cli_args=False, startup_script=startup_script) assert len(app._startup_commands) == 1 @@ -4140,7 +4140,7 @@ def test_custom_completekey_ctrl_k(): # Test setting a custom completekey to + K # In prompt_toolkit, this is 'c-k' - app = cmd2.Cmd(completekey='c-k') + app = cmd2.Cmd(completekey="c-k") assert app.main_session.key_bindings is not None @@ -4157,9 +4157,9 @@ def test_custom_completekey_ctrl_k(): def test_completekey_empty_string() -> None: # Test that an empty string for completekey defaults to DEFAULT_COMPLETEKEY - with mock.patch('cmd2.Cmd._create_main_session', autospec=True) as create_session_mock: + with mock.patch("cmd2.Cmd._create_main_session", autospec=True) as create_session_mock: create_session_mock.return_value = mock.MagicMock(spec=PromptSession) - app = cmd2.Cmd(completekey='') + app = cmd2.Cmd(completekey="") # Verify it was called with DEFAULT_COMPLETEKEY # auto_suggest is the second arg and it defaults to True @@ -4175,8 +4175,8 @@ def test_create_main_session_exception(monkeypatch): # Mock isatty to ensure we enter the try block with ( - mock.patch('sys.stdin.isatty', return_value=True), - mock.patch('sys.stdout.isatty', return_value=True), + mock.patch("sys.stdin.isatty", return_value=True), + mock.patch("sys.stdout.isatty", return_value=True), ): cmd2.Cmd() @@ -4186,33 +4186,33 @@ def test_create_main_session_exception(monkeypatch): # Check args of second call call_args = mock_session.call_args_list[1] kwargs = call_args[1] - assert isinstance(kwargs['input'], DummyInput) - assert isinstance(kwargs['output'], DummyOutput) + assert isinstance(kwargs["input"], DummyInput) + assert isinstance(kwargs["output"], DummyOutput) @pytest.mark.skipif( - not sys.platform.startswith('win'), + not sys.platform.startswith("win"), reason="This tests how app.pager is set when running on Windows.", ) def test_pager_on_windows(monkeypatch): app = cmd2.Cmd() - assert app.pager == 'more' - assert app.pager_chop == 'more' + assert app.pager == "more" + assert app.pager_chop == "more" @pytest.mark.skipif( - not sys.platform.startswith('win'), + not sys.platform.startswith("win"), reason="This tests how Cmd._complete_users() behaves on Windows.", ) def test_path_complete_users_windows(monkeypatch, base_app): # Mock os.path.expanduser and isdir - monkeypatch.setattr("os.path.expanduser", lambda p: '/home/user' if p == '~user' else p) - monkeypatch.setattr("os.path.isdir", lambda p: p == '/home/user') + monkeypatch.setattr("os.path.expanduser", lambda p: "/home/user" if p == "~user" else p) + monkeypatch.setattr("os.path.isdir", lambda p: p == "/home/user") - matches = base_app.path_complete('~user', 'cmd ~user', 0, 9) + matches = base_app.path_complete("~user", "cmd ~user", 0, 9) # Should contain ~user/ (or ~user\ depending on sep) # Since we didn't mock os.path.sep, it will use system separator. - expected = '~user' + os.path.sep + expected = "~user" + os.path.sep assert expected in matches @@ -4222,11 +4222,11 @@ def test_get_bottom_toolbar(base_app, monkeypatch): # Test enabled base_app.bottom_toolbar = True - monkeypatch.setattr(sys, 'argv', ['myapp.py']) + monkeypatch.setattr(sys, "argv", ["myapp.py"]) toolbar = base_app.get_bottom_toolbar() assert isinstance(toolbar, list) - assert toolbar[0] == ('ansigreen', 'myapp.py') - assert toolbar[2][0] == 'ansicyan' + assert toolbar[0] == ("ansigreen", "myapp.py") + assert toolbar[2][0] == "ansicyan" def test_get_rprompt(base_app): @@ -4240,24 +4240,24 @@ def test_get_rprompt(base_app): base_app.get_rprompt = lambda: expected_text assert base_app.get_rprompt() == expected_text - expected_formatted = FormattedText([('class:status', 'OK')]) + expected_formatted = FormattedText([("class:status", "OK")]) base_app.get_rprompt = lambda: expected_formatted assert base_app.get_rprompt() == expected_formatted def test_multiline_complete_statement_keyboard_interrupt(multiline_app, monkeypatch): # Mock _read_command_line to raise KeyboardInterrupt - read_command_mock = mock.MagicMock(name='_read_command_line', side_effect=KeyboardInterrupt) + read_command_mock = mock.MagicMock(name="_read_command_line", side_effect=KeyboardInterrupt) monkeypatch.setattr("cmd2.Cmd._read_command_line", read_command_mock) # Mock poutput to verify ^C is printed - poutput_mock = mock.MagicMock(name='poutput') - monkeypatch.setattr(multiline_app, 'poutput', poutput_mock) + poutput_mock = mock.MagicMock(name="poutput") + monkeypatch.setattr(multiline_app, "poutput", poutput_mock) with pytest.raises(exceptions.EmptyStatement): - multiline_app._complete_statement('orate incomplete') + multiline_app._complete_statement("orate incomplete") - poutput_mock.assert_called_with('^C') + poutput_mock.assert_called_with("^C") def test_create_main_session_no_console_error(monkeypatch): @@ -4270,8 +4270,8 @@ def test_create_main_session_no_console_error(monkeypatch): # Mock isatty to ensure we enter the try block with ( - mock.patch('sys.stdin.isatty', return_value=True), - mock.patch('sys.stdout.isatty', return_value=True), + mock.patch("sys.stdin.isatty", return_value=True), + mock.patch("sys.stdout.isatty", return_value=True), ): cmd2.Cmd() @@ -4281,8 +4281,8 @@ def test_create_main_session_no_console_error(monkeypatch): # Check args of second call call_args = mock_session.call_args_list[1] kwargs = call_args[1] - assert isinstance(kwargs['input'], DummyInput) - assert isinstance(kwargs['output'], DummyOutput) + assert isinstance(kwargs["input"], DummyInput) + assert isinstance(kwargs["output"], DummyOutput) def test_create_main_session_with_custom_tty() -> None: @@ -4298,8 +4298,8 @@ def test_create_main_session_with_custom_tty() -> None: # Check if the streams were wrapped with ( - mock.patch('cmd2.cmd2.create_input') as mock_create_input, - mock.patch('cmd2.cmd2.create_output') as mock_create_output, + mock.patch("cmd2.cmd2.create_input") as mock_create_input, + mock.patch("cmd2.cmd2.create_output") as mock_create_output, ): app = cmd2.Cmd() app.stdin = custom_stdin @@ -4343,7 +4343,7 @@ def test_read_command_line_dynamic_prompt(base_app: cmd2.Cmd) -> None: # Mock patch_stdout to prevent it from attempting to access the Windows # console buffer in a Windows test environment. - with mock.patch('cmd2.cmd2.patch_stdout'): + with mock.patch("cmd2.cmd2.patch_stdout"): # Set input to something other than DummyInput so _read_raw_input() # will go down the TTY route. mock_session = mock.MagicMock() @@ -4369,7 +4369,7 @@ def test_read_input_history_isolation(base_app: cmd2.Cmd) -> None: # Mock _read_raw_input to prevent actual blocking # We want to inspect the session object passed to it - with mock.patch.object(base_app, '_read_raw_input') as mock_raw: + with mock.patch.object(base_app, "_read_raw_input") as mock_raw: mock_raw.return_value = "user_input" base_app.read_input("prompt> ", history=local_history) @@ -4390,7 +4390,7 @@ def test_read_input_history_isolation(base_app: cmd2.Cmd) -> None: @pytest.mark.skipif( - sys.platform.startswith('win'), + sys.platform.startswith("win"), reason="Don't have a real Windows console with how we are currently running tests in GitHub Actions", ) def test_pre_prompt_running_loop(base_app): @@ -4407,14 +4407,14 @@ def test_pre_prompt_running_loop(base_app): completer=base_app.main_session.completer, ) - loop_check = {'running': False} + loop_check = {"running": False} def my_pre_prompt(): try: asyncio.get_running_loop() - loop_check['running'] = True + loop_check["running"] = True except RuntimeError: - loop_check['running'] = False + loop_check["running"] = False base_app.pre_prompt = my_pre_prompt @@ -4424,7 +4424,7 @@ def my_pre_prompt(): # Ensure self.session.prompt is used base_app._read_command_line("prompt> ") - assert loop_check['running'] + assert loop_check["running"] def test_get_bottom_toolbar_narrow_terminal(base_app, monkeypatch): @@ -4432,18 +4432,18 @@ def test_get_bottom_toolbar_narrow_terminal(base_app, monkeypatch): import shutil base_app.bottom_toolbar = True - monkeypatch.setattr(sys, 'argv', ['myapp.py']) + monkeypatch.setattr(sys, "argv", ["myapp.py"]) # Mock shutil.get_terminal_size to return a very small width (e.g. 5) # Calculated padding_size = 5 - len('myapp.py') - len(now) - 1 # Since len(now) is ~29, this will definitely be < 1 - monkeypatch.setattr(shutil, 'get_terminal_size', lambda: os.terminal_size((5, 20))) + monkeypatch.setattr(shutil, "get_terminal_size", lambda: os.terminal_size((5, 20))) toolbar = base_app.get_bottom_toolbar() assert isinstance(toolbar, list) # The padding (index 1) should be exactly 1 space - assert toolbar[1] == ('', ' ') + assert toolbar[1] == ("", " ") def test_auto_suggest_true(): diff --git a/tests/test_commandset.py b/tests/test_commandset.py index 07deeeb40..1beba737b 100644 --- a/tests/test_commandset.py +++ b/tests/test_commandset.py @@ -26,7 +26,7 @@ class CommandSetBase(cmd2.CommandSet): class CommandSetA(CommandSetBase): - DEFAULT_CATEGORY = 'Fruits' + DEFAULT_CATEGORY = "Fruits" def on_register(self, cmd) -> None: super().on_register(cmd) @@ -46,53 +46,53 @@ def on_unregistered(self) -> None: def do_apple(self, statement: cmd2.Statement) -> None: """Apple Command""" - self._cmd.poutput('Apple!') + self._cmd.poutput("Apple!") def do_banana(self, statement: cmd2.Statement) -> None: """Banana Command""" - self._cmd.poutput('Banana!!') + self._cmd.poutput("Banana!!") cranberry_parser = cmd2.Cmd2ArgumentParser() - cranberry_parser.add_argument('arg1', choices=['lemonade', 'juice', 'sauce']) + cranberry_parser.add_argument("arg1", choices=["lemonade", "juice", "sauce"]) @cmd2.with_argparser(cranberry_parser, with_unknown_args=True) def do_cranberry(self, ns: argparse.Namespace, unknown: list[str]) -> None: """Cranberry Command""" - self._cmd.poutput(f'Cranberry {ns.arg1}!!') + self._cmd.poutput(f"Cranberry {ns.arg1}!!") if unknown and len(unknown): - self._cmd.poutput('Unknown: ' + ', '.join(['{}'] * len(unknown)).format(*unknown)) - self._cmd.last_result = {'arg1': ns.arg1, 'unknown': unknown} + self._cmd.poutput("Unknown: " + ", ".join(["{}"] * len(unknown)).format(*unknown)) + self._cmd.last_result = {"arg1": ns.arg1, "unknown": unknown} def help_cranberry(self) -> None: - self._cmd.stdout.write('This command does diddly squat...\n') + self._cmd.stdout.write("This command does diddly squat...\n") @cmd2.with_argument_list - @cmd2.with_category('Also Alone') + @cmd2.with_category("Also Alone") def do_durian(self, args: list[str]) -> None: """Durian Command""" - self._cmd.poutput(f'{len(args)} Arguments: ') - self._cmd.poutput(', '.join(['{}'] * len(args)).format(*args)) - self._cmd.last_result = {'args': args} + self._cmd.poutput(f"{len(args)} Arguments: ") + self._cmd.poutput(", ".join(["{}"] * len(args)).format(*args)) + self._cmd.last_result = {"args": args} def complete_durian(self, text: str, line: str, begidx: int, endidx: int) -> list[str]: - return self._cmd.basic_complete(text, line, begidx, endidx, ['stinks', 'smells', 'disgusting']) + return self._cmd.basic_complete(text, line, begidx, endidx, ["stinks", "smells", "disgusting"]) elderberry_parser = cmd2.Cmd2ArgumentParser() - elderberry_parser.add_argument('arg1') + elderberry_parser.add_argument("arg1") - @cmd2.with_category('Alone') + @cmd2.with_category("Alone") @cmd2.with_argparser(elderberry_parser) def do_elderberry(self, ns: argparse.Namespace) -> None: """Elderberry Command""" - self._cmd.poutput(f'Elderberry {ns.arg1}!!') - self._cmd.last_result = {'arg1': ns.arg1} + self._cmd.poutput(f"Elderberry {ns.arg1}!!") + self._cmd.last_result = {"arg1": ns.arg1} # Test that CommandSet with as_subcommand_to decorator successfully loads # during `cmd2.Cmd.__init__()`. main_parser = cmd2.Cmd2ArgumentParser(description="Main Command") - main_parser.add_subparsers(dest='subcommand', metavar='SUBCOMMAND', required=True) + main_parser.add_subparsers(dest="subcommand", metavar="SUBCOMMAND", required=True) - @cmd2.with_category('Alone') + @cmd2.with_category("Alone") @cmd2.with_argparser(main_parser) def do_main(self, args: argparse.Namespace) -> None: # Call handler for whatever subcommand was selected @@ -102,13 +102,13 @@ def do_main(self, args: argparse.Namespace) -> None: subcmd_parser = cmd2.Cmd2ArgumentParser(description="Sub Command") # Include aliases to cover the alias check in cmd2's check_parser_uninstallable(). - @cmd2.as_subcommand_to('main', 'sub', subcmd_parser, help="sub command", aliases=["sub_alias"]) + @cmd2.as_subcommand_to("main", "sub", subcmd_parser, help="sub command", aliases=["sub_alias"]) def subcmd_func(self, args: argparse.Namespace) -> None: self._cmd.poutput("Subcommand Ran") class CommandSetB(CommandSetBase): - DEFAULT_CATEGORY = 'Command Set B' + DEFAULT_CATEGORY = "Command Set B" def __init__(self, arg1) -> None: super().__init__() @@ -116,15 +116,15 @@ def __init__(self, arg1) -> None: def do_aardvark(self, statement: cmd2.Statement) -> None: """Aardvark Command""" - self._cmd.poutput('Aardvark!') + self._cmd.poutput("Aardvark!") def do_bat(self, statement: cmd2.Statement) -> None: """Bat Command""" - self._cmd.poutput('Bat!!') + self._cmd.poutput("Bat!!") def do_crocodile(self, statement: cmd2.Statement) -> None: """Crocodile Command""" - self._cmd.poutput('Crocodile!!') + self._cmd.poutput("Crocodile!!") def test_autoload_commands(autoload_command_sets_app) -> None: @@ -132,21 +132,21 @@ def test_autoload_commands(autoload_command_sets_app) -> None: cmds_cats, _help_topics = autoload_command_sets_app._build_command_info() - assert 'Alone' in cmds_cats - assert 'elderberry' in cmds_cats['Alone'] - assert 'main' in cmds_cats['Alone'] + assert "Alone" in cmds_cats + assert "elderberry" in cmds_cats["Alone"] + assert "main" in cmds_cats["Alone"] # Test subcommand was autoloaded - result = autoload_command_sets_app.app_cmd('main sub') - assert 'Subcommand Ran' in result.stdout + result = autoload_command_sets_app.app_cmd("main sub") + assert "Subcommand Ran" in result.stdout - assert 'Also Alone' in cmds_cats - assert 'durian' in cmds_cats['Also Alone'] + assert "Also Alone" in cmds_cats + assert "durian" in cmds_cats["Also Alone"] - assert 'Fruits' in cmds_cats - assert 'cranberry' in cmds_cats['Fruits'] + assert "Fruits" in cmds_cats + assert "cranberry" in cmds_cats["Fruits"] - assert 'Command Set B' not in cmds_cats + assert "Command Set B" not in cmds_cats def test_command_synonyms() -> None: @@ -191,12 +191,12 @@ def do_builtin(self, _) -> None: # Make sure the alias command still exists, has the same parser, and works. assert alias_parser is app._command_parsers.get(cmd2.Cmd.do_alias) - out, _err = run_cmd(app, 'alias --help') + out, _err = run_cmd(app, "alias --help") assert normalize(alias_parser.format_help())[0] in out def test_custom_construct_commandsets() -> None: - command_set_b = CommandSetB('foo') + command_set_b = CommandSetB("foo") # Verify that _cmd cannot be accessed until CommandSet is registered. with pytest.raises(CommandSetRegistrationError) as excinfo: @@ -207,10 +207,10 @@ def test_custom_construct_commandsets() -> None: app = WithCommandSets(command_sets=[command_set_b]) cmds_cats, _help_topics = app._build_command_info() - assert 'Command Set B' in cmds_cats + assert "Command Set B" in cmds_cats # Verifies that the same CommandSet cannot be loaded twice - command_set_2 = CommandSetB('bar') + command_set_2 = CommandSetB("bar") with pytest.raises(CommandSetRegistrationError): assert app.register_command_set(command_set_2) @@ -225,11 +225,11 @@ def test_custom_construct_commandsets() -> None: app2.register_command_set(command_set_b) - assert hasattr(app2, 'do_apple') - assert hasattr(app2, 'do_aardvark') + assert hasattr(app2, "do_apple") + assert hasattr(app2, "do_aardvark") - assert app2.find_commandset_for_command('aardvark') is command_set_b - assert app2.find_commandset_for_command('apple') is command_set_a + assert app2.find_commandset_for_command("aardvark") is command_set_b + assert app2.find_commandset_for_command("apple") is command_set_a matches = app2.find_commandsets(CommandSetBase, subclass_match=True) assert command_set_a in matches @@ -241,16 +241,16 @@ def test_load_commands(manual_command_sets_app, capsys) -> None: # now install a command set and verify the commands are now present cmd_set = CommandSetA() - assert manual_command_sets_app.find_commandset_for_command('elderberry') is None + assert manual_command_sets_app.find_commandset_for_command("elderberry") is None assert not manual_command_sets_app.find_commandsets(CommandSetA) manual_command_sets_app.register_command_set(cmd_set) assert manual_command_sets_app.find_commandsets(CommandSetA)[0] is cmd_set - assert manual_command_sets_app.find_commandset_for_command('elderberry') is cmd_set + assert manual_command_sets_app.find_commandset_for_command("elderberry") is cmd_set - out = manual_command_sets_app.app_cmd('apple') - assert 'Apple!' in out.stdout + out = manual_command_sets_app.app_cmd("apple") + assert "Apple!" in out.stdout # Make sure registration callbacks ran out, _err = capsys.readouterr() @@ -259,24 +259,24 @@ def test_load_commands(manual_command_sets_app, capsys) -> None: cmds_cats, _help_topics = manual_command_sets_app._build_command_info() - assert 'Alone' in cmds_cats - assert 'elderberry' in cmds_cats['Alone'] - assert 'main' in cmds_cats['Alone'] + assert "Alone" in cmds_cats + assert "elderberry" in cmds_cats["Alone"] + assert "main" in cmds_cats["Alone"] # Test subcommand was loaded - result = manual_command_sets_app.app_cmd('main sub') - assert 'Subcommand Ran' in result.stdout + result = manual_command_sets_app.app_cmd("main sub") + assert "Subcommand Ran" in result.stdout - assert 'Fruits' in cmds_cats - assert 'cranberry' in cmds_cats['Fruits'] + assert "Fruits" in cmds_cats + assert "cranberry" in cmds_cats["Fruits"] # uninstall the command set and verify it is now also no longer accessible manual_command_sets_app.unregister_command_set(cmd_set) cmds_cats, _help_topics = manual_command_sets_app._build_command_info() - assert 'Alone' not in cmds_cats - assert 'Fruits' not in cmds_cats + assert "Alone" not in cmds_cats + assert "Fruits" not in cmds_cats # Make sure unregistration callbacks ran out, _err = capsys.readouterr() @@ -291,45 +291,45 @@ def test_load_commands(manual_command_sets_app, capsys) -> None: cmds_cats, _help_topics = manual_command_sets_app._build_command_info() - assert 'Alone' in cmds_cats - assert 'elderberry' in cmds_cats['Alone'] - assert 'main' in cmds_cats['Alone'] + assert "Alone" in cmds_cats + assert "elderberry" in cmds_cats["Alone"] + assert "main" in cmds_cats["Alone"] # Test subcommand was loaded - result = manual_command_sets_app.app_cmd('main sub') - assert 'Subcommand Ran' in result.stdout + result = manual_command_sets_app.app_cmd("main sub") + assert "Subcommand Ran" in result.stdout - assert 'Fruits' in cmds_cats - assert 'cranberry' in cmds_cats['Fruits'] + assert "Fruits" in cmds_cats + assert "cranberry" in cmds_cats["Fruits"] def test_commandset_decorators(autoload_command_sets_app) -> None: - result = autoload_command_sets_app.app_cmd('cranberry juice extra1 extra2') + result = autoload_command_sets_app.app_cmd("cranberry juice extra1 extra2") assert result is not None assert result.data is not None - assert len(result.data['unknown']) == 2 - assert 'extra1' in result.data['unknown'] - assert 'extra2' in result.data['unknown'] - assert result.data['arg1'] == 'juice' + assert len(result.data["unknown"]) == 2 + assert "extra1" in result.data["unknown"] + assert "extra2" in result.data["unknown"] + assert result.data["arg1"] == "juice" assert not result.stderr - result = autoload_command_sets_app.app_cmd('durian juice extra1 extra2') - assert len(result.data['args']) == 3 - assert 'juice' in result.data['args'] - assert 'extra1' in result.data['args'] - assert 'extra2' in result.data['args'] + result = autoload_command_sets_app.app_cmd("durian juice extra1 extra2") + assert len(result.data["args"]) == 3 + assert "juice" in result.data["args"] + assert "extra1" in result.data["args"] + assert "extra2" in result.data["args"] assert not result.stderr - result = autoload_command_sets_app.app_cmd('durian') - assert len(result.data['args']) == 0 + result = autoload_command_sets_app.app_cmd("durian") + assert len(result.data["args"]) == 0 assert not result.stderr - result = autoload_command_sets_app.app_cmd('elderberry') - assert 'arguments are required' in result.stderr + result = autoload_command_sets_app.app_cmd("elderberry") + assert "arguments are required" in result.stderr assert result.data is None - result = autoload_command_sets_app.app_cmd('elderberry a b') - assert 'unrecognized arguments' in result.stderr + result = autoload_command_sets_app.app_cmd("elderberry a b") + assert "unrecognized arguments" in result.stderr assert result.data is None @@ -337,22 +337,22 @@ def test_load_commandset_errors(manual_command_sets_app, capsys) -> None: cmd_set = CommandSetA() # create a conflicting command before installing CommandSet to verify rollback behavior - manual_command_sets_app._install_command_function('do_durian', cmd_set.do_durian) + manual_command_sets_app._install_command_function("do_durian", cmd_set.do_durian) with pytest.raises(CommandSetRegistrationError): manual_command_sets_app.register_command_set(cmd_set) # verify that the commands weren't installed cmds_cats, _help_topics = manual_command_sets_app._build_command_info() - assert 'Alone' not in cmds_cats - assert 'Fruits' not in cmds_cats + assert "Alone" not in cmds_cats + assert "Fruits" not in cmds_cats assert not manual_command_sets_app._installed_command_sets - delattr(manual_command_sets_app, 'do_durian') + delattr(manual_command_sets_app, "do_durian") # pre-create intentionally conflicting macro and alias names - manual_command_sets_app.app_cmd('macro create apple run_pyscript') - manual_command_sets_app.app_cmd('alias create banana run_pyscript') + manual_command_sets_app.app_cmd("macro create apple run_pyscript") + manual_command_sets_app.app_cmd("alias create banana run_pyscript") # now install a command set and verify the commands are now present manual_command_sets_app.register_command_set(cmd_set) @@ -364,27 +364,27 @@ def test_load_commandset_errors(manual_command_sets_app, capsys) -> None: # verify command functions which don't start with "do_" raise an exception with pytest.raises(CommandSetRegistrationError): - manual_command_sets_app._install_command_function('new_cmd', cmd_set.do_banana) + manual_command_sets_app._install_command_function("new_cmd", cmd_set.do_banana) # verify methods which don't start with "do_" raise an exception with pytest.raises(CommandSetRegistrationError): - manual_command_sets_app._install_command_function('do_new_cmd', cmd_set.on_register) + manual_command_sets_app._install_command_function("do_new_cmd", cmd_set.on_register) # verify duplicate commands are detected with pytest.raises(CommandSetRegistrationError): - manual_command_sets_app._install_command_function('do_banana', cmd_set.do_banana) + manual_command_sets_app._install_command_function("do_banana", cmd_set.do_banana) # verify bad command names are detected with pytest.raises(CommandSetRegistrationError): - manual_command_sets_app._install_command_function('do_bad command', cmd_set.do_banana) + manual_command_sets_app._install_command_function("do_bad command", cmd_set.do_banana) # verify error conflict with existing completer function with pytest.raises(CommandSetRegistrationError): - manual_command_sets_app._install_completer_function('durian', cmd_set.complete_durian) + manual_command_sets_app._install_completer_function("durian", cmd_set.complete_durian) # verify error conflict with existing help function with pytest.raises(CommandSetRegistrationError): - manual_command_sets_app._install_help_function('cranberry', cmd_set.help_cranberry) + manual_command_sets_app._install_help_function("cranberry", cmd_set.help_cranberry) class LoadableBase(cmd2.CommandSet): @@ -394,7 +394,7 @@ def __init__(self, dummy) -> None: self._cut_called = False cut_parser = cmd2.Cmd2ArgumentParser() - cut_subparsers = cut_parser.add_subparsers(title='item', help='item to cut') + cut_subparsers = cut_parser.add_subparsers(title="item", help="item to cut") def namespace_provider(self) -> argparse.Namespace: ns = argparse.Namespace() @@ -411,17 +411,17 @@ def do_cut(self, ns: argparse.Namespace) -> None: self._cut_called = True else: # No subcommand was provided, so call help - self._cmd.pwarning('This command does nothing without sub-parsers registered') - self._cmd.do_help('cut') + self._cmd.pwarning("This command does nothing without sub-parsers registered") + self._cmd.do_help("cut") stir_parser = cmd2.Cmd2ArgumentParser() - stir_subparsers = stir_parser.add_subparsers(title='item', help='what to stir') + stir_subparsers = stir_parser.add_subparsers(title="item", help="what to stir") @cmd2.with_argparser(stir_parser, ns_provider=namespace_provider) def do_stir(self, ns: argparse.Namespace) -> None: """Stir something""" if not ns.cut_called: - self._cmd.poutput('Need to cut before stirring') + self._cmd.poutput("Need to cut before stirring") return handler = ns.cmd2_subcmd_handler @@ -430,21 +430,21 @@ def do_stir(self, ns: argparse.Namespace) -> None: handler(ns) else: # No subcommand was provided, so call help - self._cmd.pwarning('This command does nothing without sub-parsers registered') - self._cmd.do_help('stir') + self._cmd.pwarning("This command does nothing without sub-parsers registered") + self._cmd.do_help("stir") stir_pasta_parser = cmd2.Cmd2ArgumentParser() - stir_pasta_parser.add_argument('--option', '-o') - stir_pasta_parser.add_subparsers(title='style', help='Stir style') + stir_pasta_parser.add_argument("--option", "-o") + stir_pasta_parser.add_subparsers(title="style", help="Stir style") - @cmd2.as_subcommand_to('stir', 'pasta', stir_pasta_parser) + @cmd2.as_subcommand_to("stir", "pasta", stir_pasta_parser) def stir_pasta(self, ns: argparse.Namespace) -> None: handler = ns.cmd2_subcmd_handler if handler is not None: # Call whatever subcommand function was selected handler(ns) else: - self._cmd.poutput('Stir pasta haphazardly') + self._cmd.poutput("Stir pasta haphazardly") class LoadableBadBase(cmd2.CommandSet): @@ -460,12 +460,12 @@ def do_cut(self, ns: argparse.Namespace) -> None: handler(ns) else: # No subcommand was provided, so call help - self._cmd.poutput('This command does nothing without sub-parsers registered') - self._cmd.do_help('cut') + self._cmd.poutput("This command does nothing without sub-parsers registered") + self._cmd.do_help("cut") class LoadableFruits(cmd2.CommandSet): - DEFAULT_CATEGORY = 'Fruits' + DEFAULT_CATEGORY = "Fruits" def __init__(self, dummy) -> None: super().__init__() @@ -473,15 +473,15 @@ def __init__(self, dummy) -> None: def do_apple(self, _: cmd2.Statement) -> None: """Apple Command""" - self._cmd.poutput('Apple') + self._cmd.poutput("Apple") banana_parser = cmd2.Cmd2ArgumentParser() - banana_parser.add_argument('direction', choices=['discs', 'lengthwise']) + banana_parser.add_argument("direction", choices=["discs", "lengthwise"]) - @cmd2.as_subcommand_to('cut', 'banana', banana_parser, help='Cut banana', aliases=['bananer']) + @cmd2.as_subcommand_to("cut", "banana", banana_parser, help="Cut banana", aliases=["bananer"]) def cut_banana(self, ns: argparse.Namespace) -> None: """Cut banana""" - self._cmd.poutput('cutting banana: ' + ns.direction) + self._cmd.poutput("cutting banana: " + ns.direction) class LoadablePastaStir(cmd2.CommandSet): @@ -490,15 +490,15 @@ def __init__(self, dummy) -> None: self._dummy = dummy # prevents autoload stir_pasta_vigor_parser = cmd2.Cmd2ArgumentParser() - stir_pasta_vigor_parser.add_argument('frequency') + stir_pasta_vigor_parser.add_argument("frequency") - @cmd2.as_subcommand_to('stir pasta', 'vigorously', stir_pasta_vigor_parser) + @cmd2.as_subcommand_to("stir pasta", "vigorously", stir_pasta_vigor_parser) def stir_pasta_vigorously(self, ns: argparse.Namespace) -> None: - self._cmd.poutput('stir the pasta vigorously') + self._cmd.poutput("stir the pasta vigorously") class LoadableVegetables(cmd2.CommandSet): - DEFAULT_CATEGORY = 'Vegetables' + DEFAULT_CATEGORY = "Vegetables" def __init__(self, dummy) -> None: super().__init__() @@ -506,17 +506,17 @@ def __init__(self, dummy) -> None: def do_arugula(self, _: cmd2.Statement) -> None: """Arugula Command""" - self._cmd.poutput('Arugula') + self._cmd.poutput("Arugula") def complete_style_arg(self, text: str, line: str, begidx: int, endidx: int) -> Completions: - return Completions.from_values(['quartered', 'diced']) + return Completions.from_values(["quartered", "diced"]) bokchoy_parser = cmd2.Cmd2ArgumentParser() - bokchoy_parser.add_argument('style', completer=complete_style_arg) + bokchoy_parser.add_argument("style", completer=complete_style_arg) - @cmd2.as_subcommand_to('cut', 'bokchoy', bokchoy_parser) + @cmd2.as_subcommand_to("cut", "bokchoy", bokchoy_parser) def cut_bokchoy(self, ns: argparse.Namespace) -> None: - self._cmd.poutput('Bok Choy: ' + ns.style) + self._cmd.poutput("Bok Choy: " + ns.style) def test_subcommands(manual_command_sets_app) -> None: @@ -536,8 +536,8 @@ def test_subcommands(manual_command_sets_app) -> None: # verify that the Fruit commands weren't installed cmds_cats, _help_topics = manual_command_sets_app._build_command_info() - assert 'Fruits' not in cmds_cats - assert 'cut' in manual_command_sets_app.get_all_commands() + assert "Fruits" not in cmds_cats + assert "cut" in manual_command_sets_app.get_all_commands() # Now install the good base commands manual_command_sets_app.unregister_command_set(badbase_cmds) @@ -547,40 +547,40 @@ def test_subcommands(manual_command_sets_app) -> None: with pytest.raises(CommandSetRegistrationError): manual_command_sets_app._register_subcommands(fruit_cmds) - cmd_result = manual_command_sets_app.app_cmd('cut') - assert 'This command does nothing without sub-parsers registered' in cmd_result.stderr + cmd_result = manual_command_sets_app.app_cmd("cut") + assert "This command does nothing without sub-parsers registered" in cmd_result.stderr # verify that command set install without problems manual_command_sets_app.register_command_set(fruit_cmds) manual_command_sets_app.register_command_set(veg_cmds) cmds_cats, _help_topics = manual_command_sets_app._build_command_info() - assert 'Fruits' in cmds_cats + assert "Fruits" in cmds_cats - text = '' - line = f'cut {text}' + text = "" + line = f"cut {text}" endidx = len(line) begidx = endidx completions = manual_command_sets_app.complete(text, line, begidx, endidx) # check that the alias shows up correctly - assert completions.to_strings() == Completions.from_values(['banana', 'bananer', 'bokchoy']).to_strings() + assert completions.to_strings() == Completions.from_values(["banana", "bananer", "bokchoy"]).to_strings() - cmd_result = manual_command_sets_app.app_cmd('cut banana discs') - assert 'cutting banana: discs' in cmd_result.stdout + cmd_result = manual_command_sets_app.app_cmd("cut banana discs") + assert "cutting banana: discs" in cmd_result.stdout - text = '' - line = f'cut bokchoy {text}' + text = "" + line = f"cut bokchoy {text}" endidx = len(line) begidx = endidx completions = manual_command_sets_app.complete(text, line, begidx, endidx) # verify that argparse completer in commandset functions correctly - assert completions.to_strings() == Completions.from_values(['diced', 'quartered']).to_strings() + assert completions.to_strings() == Completions.from_values(["diced", "quartered"]).to_strings() # verify that command set uninstalls without problems manual_command_sets_app.unregister_command_set(fruit_cmds) cmds_cats, _help_topics = manual_command_sets_app._build_command_info() - assert 'Fruits' not in cmds_cats + assert "Fruits" not in cmds_cats # verify a double-unregister raises exception with pytest.raises(CommandSetRegistrationError): @@ -588,42 +588,42 @@ def test_subcommands(manual_command_sets_app) -> None: manual_command_sets_app.unregister_command_set(veg_cmds) # Disable command and verify subcommands still load and unload - manual_command_sets_app.disable_command('cut', 'disabled for test') + manual_command_sets_app.disable_command("cut", "disabled for test") # verify that command set install without problems manual_command_sets_app.register_command_set(fruit_cmds) manual_command_sets_app.register_command_set(veg_cmds) - manual_command_sets_app.enable_command('cut') + manual_command_sets_app.enable_command("cut") cmds_cats, _help_topics = manual_command_sets_app._build_command_info() - assert 'Fruits' in cmds_cats + assert "Fruits" in cmds_cats - text = '' - line = f'cut {text}' + text = "" + line = f"cut {text}" endidx = len(line) begidx = endidx completions = manual_command_sets_app.complete(text, line, begidx, endidx) # check that the alias shows up correctly - assert completions.to_strings() == Completions.from_values(['banana', 'bananer', 'bokchoy']).to_strings() + assert completions.to_strings() == Completions.from_values(["banana", "bananer", "bokchoy"]).to_strings() - text = '' - line = f'cut bokchoy {text}' + text = "" + line = f"cut bokchoy {text}" endidx = len(line) begidx = endidx completions = manual_command_sets_app.complete(text, line, begidx, endidx) # verify that argparse completer in commandset functions correctly - assert completions.to_strings() == Completions.from_values(['diced', 'quartered']).to_strings() + assert completions.to_strings() == Completions.from_values(["diced", "quartered"]).to_strings() # disable again and verify can still uninstnall - manual_command_sets_app.disable_command('cut', 'disabled for test') + manual_command_sets_app.disable_command("cut", "disabled for test") # verify that command set uninstalls without problems manual_command_sets_app.unregister_command_set(fruit_cmds) cmds_cats, _help_topics = manual_command_sets_app._build_command_info() - assert 'Fruits' not in cmds_cats + assert "Fruits" not in cmds_cats # verify a double-unregister raises exception with pytest.raises(CommandSetRegistrationError): @@ -642,32 +642,32 @@ def test_commandset_sigint(manual_command_sets_app) -> None: class SigintHandledCommandSet(cmd2.CommandSet): def do_foo(self, _) -> None: """Foo Command""" - self._cmd.poutput('in foo') + self._cmd.poutput("in foo") self._cmd.sigint_handler(signal.SIGINT, None) - self._cmd.poutput('end of foo') + self._cmd.poutput("end of foo") def sigint_handler(self) -> bool: return True cs1 = SigintHandledCommandSet() manual_command_sets_app.register_command_set(cs1) - out = manual_command_sets_app.app_cmd('foo') - assert 'in foo' in out.stdout - assert 'end of foo' in out.stdout + out = manual_command_sets_app.app_cmd("foo") + assert "in foo" in out.stdout + assert "end of foo" in out.stdout # shows that the command is interrupted if we don't report we've handled the sigint class SigintUnhandledCommandSet(cmd2.CommandSet): def do_bar(self, _) -> None: """Bar Command""" - self._cmd.poutput('in do bar') + self._cmd.poutput("in do bar") self._cmd.sigint_handler(signal.SIGINT, None) - self._cmd.poutput('end of do bar') + self._cmd.poutput("end of do bar") cs2 = SigintUnhandledCommandSet() manual_command_sets_app.register_command_set(cs2) - out = manual_command_sets_app.app_cmd('bar') - assert 'in do bar' in out.stdout - assert 'end of do bar' not in out.stdout + out = manual_command_sets_app.app_cmd("bar") + assert "in do bar" in out.stdout + assert "end of do bar" not in out.stdout def test_nested_subcommands(manual_command_sets_app) -> None: @@ -690,12 +690,12 @@ def __init__(self, dummy) -> None: self._dummy = dummy # prevents autoload stir_pasta_vigor_parser = cmd2.Cmd2ArgumentParser() - stir_pasta_vigor_parser.add_argument('frequency') + stir_pasta_vigor_parser.add_argument("frequency") # stir sauce doesn't exist anywhere, this should fail - @cmd2.as_subcommand_to('stir sauce', 'vigorously', stir_pasta_vigor_parser) + @cmd2.as_subcommand_to("stir sauce", "vigorously", stir_pasta_vigor_parser) def stir_pasta_vigorously(self, ns: argparse.Namespace) -> None: - self._cmd.poutput('stir the pasta vigorously') + self._cmd.poutput("stir the pasta vigorously") with pytest.raises(CommandSetRegistrationError): manual_command_sets_app.register_command_set(BadNestedSubcommands(1)) @@ -705,14 +705,14 @@ def stir_pasta_vigorously(self, ns: argparse.Namespace) -> None: # validates custom namespace provider works correctly. Stir command will fail until # the cut command is called - result = manual_command_sets_app.app_cmd('stir pasta vigorously everyminute') - assert 'Need to cut before stirring' in result.stdout + result = manual_command_sets_app.app_cmd("stir pasta vigorously everyminute") + assert "Need to cut before stirring" in result.stdout - result = manual_command_sets_app.app_cmd('cut banana discs') - assert 'cutting banana: discs' in result.stdout + result = manual_command_sets_app.app_cmd("cut banana discs") + assert "cutting banana: discs" in result.stdout - result = manual_command_sets_app.app_cmd('stir pasta vigorously everyminute') - assert 'stir the pasta vigorously' in result.stdout + result = manual_command_sets_app.app_cmd("stir pasta vigorously everyminute") + assert "stir the pasta vigorously" in result.stdout class AppWithSubCommands(cmd2.Cmd): @@ -722,7 +722,7 @@ def __init__(self, *args, **kwargs) -> None: super().__init__(*args, **kwargs) cut_parser = cmd2.Cmd2ArgumentParser() - cut_subparsers = cut_parser.add_subparsers(title='item', help='item to cut') + cut_subparsers = cut_parser.add_subparsers(title="item", help="item to cut") @cmd2.with_argparser(cut_parser) def do_cut(self, ns: argparse.Namespace) -> None: @@ -733,26 +733,26 @@ def do_cut(self, ns: argparse.Namespace) -> None: handler(ns) else: # No subcommand was provided, so call help - self.poutput('This command does nothing without sub-parsers registered') - self.do_help('cut') + self.poutput("This command does nothing without sub-parsers registered") + self.do_help("cut") banana_parser = cmd2.Cmd2ArgumentParser() - banana_parser.add_argument('direction', choices=['discs', 'lengthwise']) + banana_parser.add_argument("direction", choices=["discs", "lengthwise"]) - @cmd2.as_subcommand_to('cut', 'banana', banana_parser, help='Cut banana', aliases=['bananer']) + @cmd2.as_subcommand_to("cut", "banana", banana_parser, help="Cut banana", aliases=["bananer"]) def cut_banana(self, ns: argparse.Namespace) -> None: """Cut banana""" - self.poutput('cutting banana: ' + ns.direction) + self.poutput("cutting banana: " + ns.direction) def complete_style_arg(self, text: str, line: str, begidx: int, endidx: int) -> Completions: - return Completions.from_values(['quartered', 'diced']) + return Completions.from_values(["quartered", "diced"]) bokchoy_parser = cmd2.Cmd2ArgumentParser() - bokchoy_parser.add_argument('style', completer=complete_style_arg) + bokchoy_parser.add_argument("style", completer=complete_style_arg) - @cmd2.as_subcommand_to('cut', 'bokchoy', bokchoy_parser) + @cmd2.as_subcommand_to("cut", "bokchoy", bokchoy_parser) def cut_bokchoy(self, _: argparse.Namespace) -> None: - self.poutput('Bok Choy') + self.poutput("Bok Choy") @pytest.fixture @@ -762,25 +762,25 @@ def static_subcommands_app(): def test_static_subcommands(static_subcommands_app) -> None: cmds_cats, _help_topics = static_subcommands_app._build_command_info() - assert 'Fruits' in cmds_cats + assert "Fruits" in cmds_cats - text = '' - line = f'cut {text}' + text = "" + line = f"cut {text}" endidx = len(line) begidx = endidx completions = static_subcommands_app.complete(text, line, begidx, endidx) # check that the alias shows up correctly - assert completions.to_strings() == Completions.from_values(['banana', 'bananer', 'bokchoy']).to_strings() + assert completions.to_strings() == Completions.from_values(["banana", "bananer", "bokchoy"]).to_strings() - text = '' - line = f'cut bokchoy {text}' + text = "" + line = f"cut bokchoy {text}" endidx = len(line) begidx = endidx completions = static_subcommands_app.complete(text, line, begidx, endidx) # verify that argparse completer in commandset functions correctly - assert completions.to_strings() == Completions.from_values(['diced', 'quartered']).to_strings() + assert completions.to_strings() == Completions.from_values(["diced", "quartered"]).to_strings() complete_states_expected_self = None @@ -789,8 +789,8 @@ def test_static_subcommands(static_subcommands_app) -> None: class SupportFuncProvider(cmd2.CommandSet): """CommandSet which provides a support function (complete_states) to other CommandSets""" - DEFAULT_CATEGORY = 'With Completer' - states = ('alabama', 'alaska', 'arizona', 'arkansas', 'california', 'colorado', 'connecticut', 'delaware') + DEFAULT_CATEGORY = "With Completer" + states = ("alabama", "alaska", "arizona", "arkansas", "california", "colorado", "connecticut", "delaware") def __init__(self, dummy) -> None: """Dummy variable prevents this from being autoloaded in other tests""" @@ -805,24 +805,24 @@ class SupportFuncUserSubclass1(SupportFuncProvider): """A sub-class of SupportFuncProvider which uses its support function""" parser = cmd2.Cmd2ArgumentParser() - parser.add_argument('state', type=str, completer=SupportFuncProvider.complete_states) + parser.add_argument("state", type=str, completer=SupportFuncProvider.complete_states) @cmd2.with_argparser(parser) def do_user_sub1(self, ns: argparse.Namespace) -> None: """User Sub1 Command""" - self._cmd.poutput(f'something {ns.state}') + self._cmd.poutput(f"something {ns.state}") class SupportFuncUserSubclass2(SupportFuncProvider): """A second sub-class of SupportFuncProvider which uses its support function""" parser = cmd2.Cmd2ArgumentParser() - parser.add_argument('state', type=str, completer=SupportFuncProvider.complete_states) + parser.add_argument("state", type=str, completer=SupportFuncProvider.complete_states) @cmd2.with_argparser(parser) def do_user_sub2(self, ns: argparse.Namespace) -> None: """User sub2 Command""" - self._cmd.poutput(f'something {ns.state}') + self._cmd.poutput(f"something {ns.state}") class SupportFuncUserUnrelated(cmd2.CommandSet): @@ -833,12 +833,12 @@ def __init__(self, dummy) -> None: super().__init__() parser = cmd2.Cmd2ArgumentParser() - parser.add_argument('state', type=str, completer=SupportFuncProvider.complete_states) + parser.add_argument("state", type=str, completer=SupportFuncProvider.complete_states) @cmd2.with_argparser(parser) def do_user_unrelated(self, ns: argparse.Namespace) -> None: """User Unrelated Command""" - self._cmd.poutput(f'something {ns.state}') + self._cmd.poutput(f"something {ns.state}") def test_cross_commandset_completer(manual_command_sets_app) -> None: @@ -863,8 +863,8 @@ def test_cross_commandset_completer(manual_command_sets_app) -> None: manual_command_sets_app.register_command_set(user_sub1) manual_command_sets_app.register_command_set(user_sub2) - text = '' - line = f'user_sub1 {text}' + text = "" + line = f"user_sub1 {text}" endidx = len(line) begidx = endidx complete_states_expected_self = user_sub1 @@ -874,7 +874,7 @@ def test_cross_commandset_completer(manual_command_sets_app) -> None: assert completions.to_strings() == Completions.from_values(SupportFuncProvider.states).to_strings() cmds_cats, _help_topics = manual_command_sets_app._build_command_info() - assert 'user_sub1' in cmds_cats['With Completer'] + assert "user_sub1" in cmds_cats["With Completer"] manual_command_sets_app.unregister_command_set(user_sub2) manual_command_sets_app.unregister_command_set(user_sub1) @@ -887,8 +887,8 @@ def test_cross_commandset_completer(manual_command_sets_app) -> None: manual_command_sets_app.register_command_set(func_provider) manual_command_sets_app.register_command_set(user_unrelated) - text = '' - line = f'user_unrelated {text}' + text = "" + line = f"user_unrelated {text}" endidx = len(line) begidx = endidx complete_states_expected_self = func_provider @@ -909,8 +909,8 @@ def test_cross_commandset_completer(manual_command_sets_app) -> None: manual_command_sets_app.register_command_set(user_sub1) manual_command_sets_app.register_command_set(user_unrelated) - text = '' - line = f'user_unrelated {text}' + text = "" + line = f"user_unrelated {text}" endidx = len(line) begidx = endidx complete_states_expected_self = user_sub1 @@ -930,8 +930,8 @@ def test_cross_commandset_completer(manual_command_sets_app) -> None: manual_command_sets_app.register_command_set(user_unrelated) - text = '' - line = f'user_unrelated {text}' + text = "" + line = f"user_unrelated {text}" endidx = len(line) begidx = endidx completions = manual_command_sets_app.complete(text, line, begidx, endidx) @@ -951,8 +951,8 @@ def test_cross_commandset_completer(manual_command_sets_app) -> None: manual_command_sets_app.register_command_set(user_sub2) manual_command_sets_app.register_command_set(user_unrelated) - text = '' - line = f'user_unrelated {text}' + text = "" + line = f"user_unrelated {text}" endidx = len(line) begidx = endidx completions = manual_command_sets_app.complete(text, line, begidx, endidx) @@ -971,7 +971,7 @@ def __init__(self, dummy) -> None: super().__init__() parser = cmd2.Cmd2ArgumentParser() - parser.add_argument('path', nargs='+', help='paths', completer=cmd2.Cmd.path_complete) + parser.add_argument("path", nargs="+", help="paths", completer=cmd2.Cmd.path_complete) @cmd2.with_argparser(parser) def do_path(self, app: cmd2.Cmd, args) -> None: @@ -984,8 +984,8 @@ def test_path_complete(manual_command_sets_app) -> None: manual_command_sets_app.register_command_set(test_set) - text = '' - line = f'path {text}' + text = "" + line = f"path {text}" endidx = len(line) begidx = endidx completions = manual_command_sets_app.complete(text, line, begidx, endidx) @@ -1001,19 +1001,19 @@ def __init__(self, *args, **kwargs) -> None: super().__init__(*args, **kwargs) cut_parser = cmd2.Cmd2ArgumentParser() - cut_subparsers = cut_parser.add_subparsers(title='item', help='item to cut') + cut_subparsers = cut_parser.add_subparsers(title="item", help="item to cut") @cmd2.with_argparser(cut_parser) def do_cut(self, ns: argparse.Namespace) -> None: """Cut something""" banana_parser = cmd2.Cmd2ArgumentParser() - banana_parser.add_argument('direction', choices=['discs', 'lengthwise']) + banana_parser.add_argument("direction", choices=["discs", "lengthwise"]) - @cmd2.as_subcommand_to('cut', 'bad name', banana_parser, help='This should fail') + @cmd2.as_subcommand_to("cut", "bad name", banana_parser, help="This should fail") def cut_banana(self, ns: argparse.Namespace) -> None: """Cut banana""" - self.poutput('cutting banana: ' + ns.direction) + self.poutput("cutting banana: " + ns.direction) with pytest.raises(CommandSetRegistrationError): BadSubcommandApp() @@ -1031,16 +1031,16 @@ def __init__(self) -> None: super().__init__() self._arbitrary = Arbitrary() - self._settable_prefix = 'addon' + self._settable_prefix = "addon" self.my_int = 11 self.add_settable( Settable( - 'arbitrary_value', + "arbitrary_value", int, - 'Some settable value', + "Some settable value", settable_object=self._arbitrary, - settable_attrib_name='some_value', + settable_attrib_name="some_value", ) ) @@ -1050,16 +1050,16 @@ def __init__(self) -> None: super().__init__() self._arbitrary = Arbitrary() - self._settable_prefix = '' + self._settable_prefix = "" self.my_int = 11 self.add_settable( Settable( - 'another_value', + "another_value", float, - 'Some settable value', + "Some settable value", settable_object=self._arbitrary, - settable_attrib_name='some_value', + settable_attrib_name="some_value", ) ) @@ -1069,16 +1069,16 @@ def __init__(self) -> None: super().__init__() self._arbitrary = Arbitrary() - self._settable_prefix = 'some' + self._settable_prefix = "some" self.my_int = 11 self.add_settable( Settable( - 'arbitrary_value', + "arbitrary_value", int, - 'Some settable value', + "Some settable value", settable_object=self._arbitrary, - settable_attrib_name='some_value', + settable_attrib_name="some_value", ) ) @@ -1086,46 +1086,46 @@ def __init__(self) -> None: cmdset = WithSettablesA() arbitrary2 = Arbitrary() app = cmd2.Cmd(command_sets=[cmdset], auto_load_commands=False) - app.str_value = '' - app.add_settable(Settable('always_prefix_settables', bool, 'Prefix settables', app)) - app._settables['str_value'] = Settable('str_value', str, 'String value', app) + app.str_value = "" + app.add_settable(Settable("always_prefix_settables", bool, "Prefix settables", app)) + app._settables["str_value"] = Settable("str_value", str, "String value", app) - assert 'arbitrary_value' in app.settables - assert 'always_prefix_settables' in app.settables - assert 'str_value' in app.settables + assert "arbitrary_value" in app.settables + assert "always_prefix_settables" in app.settables + assert "str_value" in app.settables # verify the settable shows up - out, err = run_cmd(app, 'set') - any('arbitrary_value' in line and '5' in line for line in out) + out, err = run_cmd(app, "set") + any("arbitrary_value" in line and "5" in line for line in out) - out, err = run_cmd(app, 'set arbitrary_value') - any('arbitrary_value' in line and '5' in line for line in out) + out, err = run_cmd(app, "set arbitrary_value") + any("arbitrary_value" in line and "5" in line for line in out) # change the value and verify the value changed - out, err = run_cmd(app, 'set arbitrary_value 10') + out, err = run_cmd(app, "set arbitrary_value 10") expected = """ arbitrary_value - was: 5 now: 10 """ assert out == normalize(expected) - out, err = run_cmd(app, 'set arbitrary_value') - any('arbitrary_value' in line and '10' in line for line in out) + out, err = run_cmd(app, "set arbitrary_value") + any("arbitrary_value" in line and "10" in line for line in out) # can't add to cmd2 now because commandset already has this settable with pytest.raises(KeyError): - app.add_settable(Settable('arbitrary_value', int, 'This should fail', app)) + app.add_settable(Settable("arbitrary_value", int, "This should fail", app)) cmdset.add_settable( - Settable('arbitrary_value', int, 'Replaced settable', settable_object=arbitrary2, settable_attrib_name='some_value') + Settable("arbitrary_value", int, "Replaced settable", settable_object=arbitrary2, settable_attrib_name="some_value") ) # Can't add a settable to the commandset that already exists in cmd2 with pytest.raises(KeyError): - cmdset.add_settable(Settable('always_prefix_settables', int, 'This should also fail', cmdset)) + cmdset.add_settable(Settable("always_prefix_settables", int, "This should also fail", cmdset)) # Can't remove a settable from the CommandSet if it is elsewhere and not in the CommandSet with pytest.raises(KeyError): - cmdset.remove_settable('always_prefix_settables') + cmdset.remove_settable("always_prefix_settables") # verify registering a commandset with duplicate settable names fails cmdset_dupname = WithSettablesB() @@ -1134,9 +1134,9 @@ def __init__(self) -> None: # unregister the CommandSet and verify the settable is now gone app.unregister_command_set(cmdset) - out, err = run_cmd(app, 'set') - assert 'arbitrary_value' not in out - out, err = run_cmd(app, 'set arbitrary_value') + out, err = run_cmd(app, "set") + assert "arbitrary_value" not in out + out, err = run_cmd(app, "set arbitrary_value") expected = """ Parameter 'arbitrary_value' not supported (type 'set' for list of parameters). """ @@ -1163,35 +1163,35 @@ def __init__(self) -> None: app.register_command_set(cmdset) # Verify the settable is back with the defined prefix. - assert 'addon.arbitrary_value' in app.settables + assert "addon.arbitrary_value" in app.settables # rename the prefix and verify that the prefix changes everywhere - cmdset._settable_prefix = 'some' - assert 'addon.arbitrary_value' not in app.settables - assert 'some.arbitrary_value' in app.settables + cmdset._settable_prefix = "some" + assert "addon.arbitrary_value" not in app.settables + assert "some.arbitrary_value" in app.settables - out, err = run_cmd(app, 'set') - any('some.arbitrary_value' in line and '5' in line for line in out) + out, err = run_cmd(app, "set") + any("some.arbitrary_value" in line and "5" in line for line in out) - out, err = run_cmd(app, 'set some.arbitrary_value') - any('some.arbitrary_value' in line and '5' in line for line in out) + out, err = run_cmd(app, "set some.arbitrary_value") + any("some.arbitrary_value" in line and "5" in line for line in out) # verify registering a commandset with duplicate prefix and settable names fails with pytest.raises(CommandSetRegistrationError): app.register_command_set(cmdset_dupname) - cmdset_dupname.remove_settable('arbitrary_value') + cmdset_dupname.remove_settable("arbitrary_value") app.register_command_set(cmdset_dupname) with pytest.raises(KeyError): cmdset_dupname.add_settable( Settable( - 'arbitrary_value', + "arbitrary_value", int, - 'Some settable value', + "Some settable value", settable_object=cmdset_dupname._arbitrary, - settable_attrib_name='some_value', + settable_attrib_name="some_value", ) ) diff --git a/tests/test_completion.py b/tests/test_completion.py index 992ba4fdd..f1c910759 100644 --- a/tests/test_completion.py +++ b/tests/test_completion.py @@ -31,56 +31,56 @@ class SubcommandsExample(cmd2.Cmd): and the "sport" subcommand has tab completion enabled. """ - sport_item_strs = ('Bat', 'Basket', 'Basketball', 'Football', 'Space Ball') + sport_item_strs = ("Bat", "Basket", "Basketball", "Football", "Space Ball") # create the top-level parser for the base command base_parser = cmd2.Cmd2ArgumentParser() - base_subparsers = base_parser.add_subparsers(title='subcommands', help='subcommand help') + base_subparsers = base_parser.add_subparsers(title="subcommands", help="subcommand help") # create the parser for the "foo" subcommand - parser_foo = base_subparsers.add_parser('foo', help='foo help') - parser_foo.add_argument('-x', type=int, default=1, help='integer') - parser_foo.add_argument('y', type=float, help='float') - parser_foo.add_argument('input_file', type=str, help='Input File') + parser_foo = base_subparsers.add_parser("foo", help="foo help") + parser_foo.add_argument("-x", type=int, default=1, help="integer") + parser_foo.add_argument("y", type=float, help="float") + parser_foo.add_argument("input_file", type=str, help="Input File") # create the parser for the "bar" subcommand - parser_bar = base_subparsers.add_parser('bar', help='bar help') + parser_bar = base_subparsers.add_parser("bar", help="bar help") - bar_subparsers = parser_bar.add_subparsers(title='layer3', help='help for 3rd layer of commands') - parser_bar.add_argument('z', help='string') + bar_subparsers = parser_bar.add_subparsers(title="layer3", help="help for 3rd layer of commands") + parser_bar.add_argument("z", help="string") - bar_subparsers.add_parser('apple', help='apple help') - bar_subparsers.add_parser('artichoke', help='artichoke help') - bar_subparsers.add_parser('cranberries', help='cranberries help') + bar_subparsers.add_parser("apple", help="apple help") + bar_subparsers.add_parser("artichoke", help="artichoke help") + bar_subparsers.add_parser("cranberries", help="cranberries help") # create the parser for the "sport" subcommand - parser_sport = base_subparsers.add_parser('sport', help='sport help') - sport_arg = parser_sport.add_argument('sport', help='Enter name of a sport', choices=sport_item_strs) + parser_sport = base_subparsers.add_parser("sport", help="sport help") + sport_arg = parser_sport.add_argument("sport", help="Enter name of a sport", choices=sport_item_strs) # create the top-level parser for the alternate command # The alternate command doesn't provide its own help flag base2_parser = cmd2.Cmd2ArgumentParser(add_help=False) - base2_subparsers = base2_parser.add_subparsers(title='subcommands', help='subcommand help') + base2_subparsers = base2_parser.add_subparsers(title="subcommands", help="subcommand help") # create the parser for the "foo" subcommand - parser_foo2 = base2_subparsers.add_parser('foo', help='foo help') - parser_foo2.add_argument('-x', type=int, default=1, help='integer') - parser_foo2.add_argument('y', type=float, help='float') - parser_foo2.add_argument('input_file', type=str, help='Input File') + parser_foo2 = base2_subparsers.add_parser("foo", help="foo help") + parser_foo2.add_argument("-x", type=int, default=1, help="integer") + parser_foo2.add_argument("y", type=float, help="float") + parser_foo2.add_argument("input_file", type=str, help="Input File") # create the parser for the "bar" subcommand - parser_bar2 = base2_subparsers.add_parser('bar', help='bar help') + parser_bar2 = base2_subparsers.add_parser("bar", help="bar help") - bar2_subparsers = parser_bar2.add_subparsers(title='layer3', help='help for 3rd layer of commands') - parser_bar2.add_argument('z', help='string') + bar2_subparsers = parser_bar2.add_subparsers(title="layer3", help="help for 3rd layer of commands") + parser_bar2.add_argument("z", help="string") - bar2_subparsers.add_parser('apple', help='apple help') - bar2_subparsers.add_parser('artichoke', help='artichoke help') - bar2_subparsers.add_parser('cranberries', help='cranberries help') + bar2_subparsers.add_parser("apple", help="apple help") + bar2_subparsers.add_parser("artichoke", help="artichoke help") + bar2_subparsers.add_parser("cranberries", help="cranberries help") # create the parser for the "sport" subcommand - parser_sport2 = base2_subparsers.add_parser('sport', help='sport help') - sport2_arg = parser_sport2.add_argument('sport', help='Enter name of a sport', choices=sport_item_strs) + parser_sport2 = base2_subparsers.add_parser("sport", help="sport help") + sport2_arg = parser_sport2.add_argument("sport", help="Enter name of a sport", choices=sport_item_strs) def __init__(self) -> None: super().__init__() @@ -92,11 +92,11 @@ def base_foo(self, args) -> None: def base_bar(self, args) -> None: """Bar subcommand of base command.""" - self.poutput(f'(({args.z}))') + self.poutput(f"(({args.z}))") def base_sport(self, args) -> None: """Sport subcommand of base command.""" - self.poutput(f'Sport is {args.sport}') + self.poutput(f"Sport is {args.sport}") # Set handler functions for the subcommands parser_foo.set_defaults(func=base_foo) @@ -106,35 +106,35 @@ def base_sport(self, args) -> None: @cmd2.with_argparser(base_parser) def do_base(self, args) -> None: """Base command help.""" - func = getattr(args, 'func', None) + func = getattr(args, "func", None) if func is not None: # Call whatever subcommand function was selected func(self, args) else: # No subcommand was provided, so call help - self.do_help('base') + self.do_help("base") @cmd2.with_argparser(base2_parser) def do_alternate(self, args) -> None: """Alternate command help.""" - func = getattr(args, 'func', None) + func = getattr(args, "func", None) if func is not None: # Call whatever subcommand function was selected func(self, args) else: # No subcommand was provided, so call help - self.do_help('alternate') + self.do_help("alternate") # List of strings used with completion functions -food_item_strs = ['Pizza', 'Ham', 'Ham Sandwich', 'Potato', 'Cheese "Pizza"'] -sport_item_strs = ['Bat', 'Basket', 'Basketball', 'Football', 'Space Ball'] +food_item_strs = ["Pizza", "Ham", "Ham Sandwich", "Potato", 'Cheese "Pizza"'] +sport_item_strs = ["Bat", "Basket", "Basketball", "Football", "Space Ball"] delimited_strs = [ - '/home/user/file.txt', - '/home/user/file space.txt', - '/home/user/prog.c', - '/home/other user/maps', - '/home/other user/tests', + "/home/user/file.txt", + "/home/user/file space.txt", + "/home/user/prog.c", + "/home/other user/maps", + "/home/other user/tests", ] @@ -142,11 +142,11 @@ class CompletionsExample(cmd2.Cmd): """Example cmd2 application used to exercise tab completion tests""" def __init__(self) -> None: - cmd2.Cmd.__init__(self, multiline_commands=['test_multiline']) - self.foo = 'bar' + cmd2.Cmd.__init__(self, multiline_commands=["test_multiline"]) + self.foo = "bar" self.add_settable( utils.Settable( - 'foo', + "foo", str, description="a test settable param", settable_object=self, @@ -164,13 +164,13 @@ def do_test_delimited(self, args) -> None: pass def complete_test_delimited(self, text, line, begidx, endidx) -> Completions: - return self.delimiter_complete(text, line, begidx, endidx, delimited_strs, '/') + return self.delimiter_complete(text, line, begidx, endidx, delimited_strs, "/") def do_test_sort_key(self, args) -> None: pass def complete_test_sort_key(self, text, line, begidx, endidx) -> Completions: - num_strs = ['file2', 'file11', 'file1'] + num_strs = ["file2", "file11", "file1"] return self.basic_complete(text, line, begidx, endidx, num_strs) def do_test_raise_exception(self, args) -> None: @@ -190,7 +190,7 @@ def do_test_no_completer(self, args) -> None: def complete_foo_val(self, text, line, begidx, endidx, arg_tokens) -> Completions: """Supports unit testing cmd2.Cmd2.complete_set_val to confirm it passes all tokens in the set command""" - value = "SUCCESS" if 'param' in arg_tokens else "FAIL" + value = "SUCCESS" if "param" in arg_tokens else "FAIL" return Completions.from_values([value]) def completedefault(self, *ignored) -> Completions: @@ -198,7 +198,7 @@ def completedefault(self, *ignored) -> Completions: complete_*() method is available. """ - return Completions.from_values(['default']) + return Completions.from_values(["default"]) @pytest.fixture @@ -207,18 +207,18 @@ def cmd2_app(): def test_command_completion(cmd2_app) -> None: - text = 'run' + text = "run" line = text endidx = len(line) begidx = endidx - len(text) - expected = ['run_pyscript', 'run_script'] + expected = ["run_pyscript", "run_script"] completions = cmd2_app.complete(text, line, begidx, endidx) assert completions.to_strings() == Completions.from_values(expected).to_strings() def test_command_completion_nomatch(cmd2_app) -> None: - text = 'fakecommand' + text = "fakecommand" line = text endidx = len(line) begidx = endidx - len(text) @@ -231,19 +231,19 @@ def test_command_completion_nomatch(cmd2_app) -> None: def test_complete_bogus_command(cmd2_app) -> None: - text = '' - line = f'fizbuzz {text}' + text = "" + line = f"fizbuzz {text}" endidx = len(line) begidx = endidx - len(text) - expected = ['default'] + expected = ["default"] completions = cmd2_app.complete(text, line, begidx, endidx) assert completions.to_strings() == Completions.from_values(expected).to_strings() def test_complete_exception(cmd2_app) -> None: - text = '' - line = f'test_raise_exception {text}' + text = "" + line = f"test_raise_exception {text}" endidx = len(line) begidx = endidx - len(text) @@ -255,26 +255,26 @@ def test_complete_exception(cmd2_app) -> None: def test_complete_macro(base_app, request) -> None: # Create the macro - out, _err = run_cmd(base_app, 'macro create fake run_pyscript {1}') + out, _err = run_cmd(base_app, "macro create fake run_pyscript {1}") assert out == normalize("Macro 'fake' created") # Macros do path completion test_dir = os.path.dirname(request.module.__file__) - text = os.path.join(test_dir, 's') - line = f'fake {text}' + text = os.path.join(test_dir, "s") + line = f"fake {text}" endidx = len(line) begidx = endidx - len(text) - expected = [text + 'cript.py', text + 'cript.txt', text + 'cripts' + os.path.sep] + expected = [text + "cript.py", text + "cript.txt", text + "cripts" + os.path.sep] completions = base_app.complete(text, line, begidx, endidx) assert completions.to_strings() == Completions.from_values(expected).to_strings() def test_default_str_sort_key(cmd2_app) -> None: - text = '' - line = f'test_sort_key {text}' + text = "" + line = f"test_sort_key {text}" endidx = len(line) begidx = endidx - len(text) @@ -283,13 +283,13 @@ def test_default_str_sort_key(cmd2_app) -> None: try: # First do alphabetical sorting utils.set_default_str_sort_key(utils.ALPHABETICAL_SORT_KEY) - expected = ['file1', 'file11', 'file2'] + expected = ["file1", "file11", "file2"] completions = cmd2_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'] + expected = ["file1", "file2", "file11"] completions = cmd2_app.complete(text, line, begidx, endidx) assert completions.to_strings() == Completions.from_values(expected).to_strings() finally: @@ -297,19 +297,19 @@ def test_default_str_sort_key(cmd2_app) -> None: def test_help_completion(cmd2_app) -> None: - text = 'h' - line = f'help {text}' + text = "h" + line = f"help {text}" endidx = len(line) begidx = endidx - len(text) - expected = ['help', 'history'] + expected = ["help", "history"] completions = cmd2_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: - text = '' - line = f'help {text}' + text = "" + line = f"help {text}" endidx = len(line) begidx = endidx - len(text) @@ -319,8 +319,8 @@ def test_help_completion_empty_arg(cmd2_app) -> None: def test_help_completion_nomatch(cmd2_app) -> None: - text = 'fakecommand' - line = f'help {text}' + text = "fakecommand" + line = f"help {text}" endidx = len(line) begidx = endidx - len(text) @@ -330,8 +330,8 @@ def test_help_completion_nomatch(cmd2_app) -> None: def test_set_allow_style_completion(cmd2_app) -> None: """Confirm that completing allow_style presents AllowStyle strings""" - text = '' - line = 'set allow_style' + text = "" + line = "set allow_style" endidx = len(line) begidx = endidx - len(text) @@ -342,12 +342,12 @@ def test_set_allow_style_completion(cmd2_app) -> None: def test_set_bool_completion(cmd2_app) -> None: """Confirm that completing a boolean Settable presents true and false strings""" - text = '' - line = 'set debug' + text = "" + line = "set debug" endidx = len(line) begidx = endidx - len(text) - expected = ['false', 'true'] + expected = ["false", "true"] completions = cmd2_app.complete(text, line, begidx, endidx) assert completions.to_strings() == Completions.from_values(expected).to_strings() @@ -357,11 +357,11 @@ def test_shell_command_completion_shortcut(cmd2_app) -> None: # isn't a space between ! and the shell command. Display matches won't # begin with the !. if sys.platform == "win32": - text = '!calc' - expected_item = CompletionItem('!calc.exe', display='calc.exe') + text = "!calc" + expected_item = CompletionItem("!calc.exe", display="calc.exe") else: - text = '!egr' - expected_item = CompletionItem('!egrep', display='egrep') + text = "!egr" + expected_item = CompletionItem("!egrep", display="egrep") expected_completions = Completions([expected_item]) @@ -376,11 +376,11 @@ def test_shell_command_completion_shortcut(cmd2_app) -> None: def test_shell_command_completion_does_not_match_wildcards(cmd2_app) -> None: if sys.platform == "win32": - text = 'c*' + text = "c*" else: - text = 'e*' + text = "e*" - line = f'shell {text}' + line = f"shell {text}" endidx = len(line) begidx = endidx - len(text) @@ -390,13 +390,13 @@ def test_shell_command_completion_does_not_match_wildcards(cmd2_app) -> None: def test_shell_command_complete(cmd2_app) -> None: if sys.platform == "win32": - text = 'c' - expected = 'calc.exe' + text = "c" + expected = "calc.exe" else: - text = 'l' - expected = 'ls' + text = "l" + expected = "ls" - line = f'shell {text}' + line = f"shell {text}" endidx = len(line) begidx = endidx - len(text) @@ -405,8 +405,8 @@ def test_shell_command_complete(cmd2_app) -> None: def test_shell_command_completion_nomatch(cmd2_app) -> None: - text = 'zzzz' - line = f'shell {text}' + text = "zzzz" + line = f"shell {text}" endidx = len(line) begidx = endidx - len(text) @@ -415,8 +415,8 @@ def test_shell_command_completion_nomatch(cmd2_app) -> None: def test_shell_command_completion_does_not_complete_when_just_shell(cmd2_app) -> None: - text = '' - line = f'shell {text}' + text = "" + line = f"shell {text}" endidx = len(line) begidx = endidx - len(text) @@ -427,13 +427,13 @@ def test_shell_command_completion_does_not_complete_when_just_shell(cmd2_app) -> def test_shell_command_completion_does_path_completion_when_after_command(cmd2_app, request) -> None: test_dir = os.path.dirname(request.module.__file__) - text = os.path.join(test_dir, 'conftest') - line = f'shell cat {text}' + text = os.path.join(test_dir, "conftest") + line = f"shell cat {text}" endidx = len(line) begidx = endidx - len(text) - expected = [text + '.py'] + expected = [text + ".py"] completions = cmd2_app.complete(text, line, begidx, endidx) assert completions.to_strings() == Completions.from_values(expected).to_strings() @@ -441,15 +441,15 @@ def test_shell_command_completion_does_path_completion_when_after_command(cmd2_a def test_shell_command_complete_in_path(cmd2_app, request) -> None: test_dir = os.path.dirname(request.module.__file__) - text = os.path.join(test_dir, 's') - line = f'shell {text}' + text = os.path.join(test_dir, "s") + line = f"shell {text}" endidx = len(line) begidx = endidx - len(text) # Since this will look for directories and executables in the given path, # we expect to see the scripts dir among the results - expected = os.path.join(test_dir, 'scripts' + os.path.sep) + expected = os.path.join(test_dir, "scripts" + os.path.sep) completions = cmd2_app.complete(text, line, begidx, endidx) assert expected in completions.to_strings() @@ -459,13 +459,13 @@ def test_path_completion_files_and_directories(cmd2_app, request) -> None: """Test that directories include an ending slash and files do not.""" test_dir = os.path.dirname(request.module.__file__) - text = os.path.join(test_dir, 's') - line = f'shell cat {text}' + text = os.path.join(test_dir, "s") + line = f"shell cat {text}" endidx = len(line) begidx = endidx - len(text) - expected = [text + 'cript.py', text + 'cript.txt', text + 'cripts' + os.path.sep] + expected = [text + "cript.py", text + "cript.txt", text + "cripts" + os.path.sep] completions = cmd2_app.path_complete(text, line, begidx, endidx) assert completions.to_strings() == Completions.from_values(expected).to_strings() @@ -473,8 +473,8 @@ def test_path_completion_files_and_directories(cmd2_app, request) -> None: def test_path_completion_nomatch(cmd2_app, request) -> None: test_dir = os.path.dirname(request.module.__file__) - text = os.path.join(test_dir, 'fakepath') - line = f'shell cat {text}' + text = os.path.join(test_dir, "fakepath") + line = f"shell cat {text}" endidx = len(line) begidx = endidx - len(text) @@ -485,21 +485,21 @@ def test_path_completion_nomatch(cmd2_app, request) -> None: def test_path_completion_no_text(cmd2_app) -> None: # Run path complete with no search text which should show what's in cwd - text = '' - line = f'shell ls {text}' + text = "" + line = f"shell ls {text}" endidx = len(line) begidx = endidx - len(text) completions_no_text = cmd2_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}' + line = f"shell ls {text}" endidx = len(line) begidx = endidx - len(text) completions_cwd = cmd2_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] + stripped_paths = [CompletionItem(value=item.text.replace(text, "", 1)) for item in completions_cwd] completions_cwd = dataclasses.replace(completions_cwd, items=stripped_paths) # Verify that the first test gave results for entries in the cwd @@ -509,21 +509,21 @@ def test_path_completion_no_text(cmd2_app) -> None: def test_path_completion_no_path(cmd2_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}' + 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) # Run path complete with path set to the CWD text = os.getcwd() + os.path.sep + text - line = f'shell ls {text}' + line = f"shell ls {text}" endidx = len(line) begidx = endidx - len(text) completions_cwd = cmd2_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] + stripped_paths = [CompletionItem(value=item.text.replace(text[:-1], "", 1)) for item in completions_cwd] completions_cwd = dataclasses.replace(completions_cwd, items=stripped_paths) # Verify that the first test gave results for entries in the cwd @@ -531,15 +531,15 @@ def test_path_completion_no_path(cmd2_app) -> None: assert completions_cwd -@pytest.mark.skipif(sys.platform == 'win32', reason="this only applies on systems where the root directory is a slash") +@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: # Change our CWD to root dir cwd = os.getcwd() try: os.chdir(os.path.sep) - text = '' - line = f'shell ls {text}' + text = "" + line = f"shell ls {text}" endidx = len(line) begidx = endidx - len(text) completions = cmd2_app.path_complete(text, line, begidx, endidx) @@ -554,8 +554,8 @@ def test_path_completion_cwd_is_root_dir(cmd2_app) -> None: def test_path_completion_does_not_match_wildcards(cmd2_app, request) -> None: test_dir = os.path.dirname(request.module.__file__) - text = os.path.join(test_dir, 'c*') - line = f'shell cat {text}' + text = os.path.join(test_dir, "c*") + line = f"shell cat {text}" endidx = len(line) begidx = endidx - len(text) @@ -570,8 +570,8 @@ def test_path_completion_complete_user(cmd2_app) -> None: user = getpass.getuser() - text = f'~{user}' - line = f'shell fake {text}' + text = f"~{user}" + line = f"shell fake {text}" endidx = len(line) begidx = endidx - len(text) @@ -582,50 +582,50 @@ def test_path_completion_complete_user(cmd2_app) -> None: def test_path_completion_user_path_expansion(cmd2_app) -> None: # Run path with a tilde and a slash - if sys.platform.startswith('win'): - cmd = 'dir' + if sys.platform.startswith("win"): + cmd = "dir" else: - cmd = 'ls' + cmd = "ls" # Use a ~ which will be expanded into the user's home directory - text = f'~{os.path.sep}' - line = f'shell {cmd} {text}' + text = f"~{os.path.sep}" + line = f"shell {cmd} {text}" endidx = len(line) begidx = endidx - len(text) completions_tilde_slash = cmd2_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] + stripped_paths = [CompletionItem(value=item.text.replace(text, "", 1)) for item in completions_tilde_slash] completions_tilde_slash = dataclasses.replace(completions_tilde_slash, items=stripped_paths) # Run path complete on the user's home directory - text = os.path.expanduser('~') + os.path.sep - line = f'shell {cmd} {text}' + text = os.path.expanduser("~") + os.path.sep + line = f"shell {cmd} {text}" endidx = len(line) begidx = endidx - len(text) completions_home = cmd2_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] + stripped_paths = [CompletionItem(value=item.text.replace(text, "", 1)) for item in completions_home] completions_home = dataclasses.replace(completions_home, items=stripped_paths) assert completions_tilde_slash == completions_home def test_basic_completion(cmd2_app) -> None: - text = 'P' - line = f'list_food -f {text}' + text = "P" + line = f"list_food -f {text}" endidx = len(line) begidx = endidx - len(text) - expected = ['Pizza', 'Potato'] + expected = ["Pizza", "Potato"] completions = cmd2_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: - text = 'q' - line = f'list_food -f {text}' + text = "q" + line = f"list_food -f {text}" endidx = len(line) begidx = endidx - len(text) @@ -635,8 +635,8 @@ def test_basic_completion_nomatch(cmd2_app) -> None: def test_delimiter_completion_partial(cmd2_app) -> None: """Test that a delimiter is added when an item has not been fully completed""" - text = '/home/' - line = f'command {text}' + text = "/home/" + line = f"command {text}" endidx = len(line) begidx = endidx - len(text) @@ -646,7 +646,7 @@ 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 = cmd2_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] @@ -654,8 +654,8 @@ def test_delimiter_completion_partial(cmd2_app) -> None: def test_delimiter_completion_full(cmd2_app) -> None: """Test that no delimiter is added when an item has been fully completed""" - text = '/home/other user/' - line = f'command {text}' + text = "/home/other user/" + line = f"command {text}" endidx = len(line) begidx = endidx - len(text) @@ -665,30 +665,30 @@ 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 = cmd2_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: - text = '/nothing_to_see' - line = f'command {text}' + 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 = cmd2_app.delimiter_complete(text, line, begidx, endidx, delimited_strs, "/") assert not completions def test_tokens_for_completion_quoted(cmd2_app) -> None: - text = 'Pi' + text = "Pi" line = f'list_food "{text}"' endidx = len(line) begidx = endidx - expected_tokens = ['list_food', 'Pi', ''] - expected_raw_tokens = ['list_food', '"Pi"', ''] + expected_tokens = ["list_food", "Pi", ""] + expected_raw_tokens = ["list_food", '"Pi"', ""] tokens, raw_tokens = cmd2_app.tokens_for_completion(line, begidx, endidx) assert expected_tokens == tokens @@ -696,13 +696,13 @@ def test_tokens_for_completion_quoted(cmd2_app) -> None: def test_tokens_for_completion_unclosed_quote(cmd2_app) -> None: - text = 'Pi' + text = "Pi" line = f'list_food "{text}' endidx = len(line) begidx = endidx - len(text) - expected_tokens = ['list_food', 'Pi'] - expected_raw_tokens = ['list_food', '"Pi'] + expected_tokens = ["list_food", "Pi"] + expected_raw_tokens = ["list_food", '"Pi'] tokens, raw_tokens = cmd2_app.tokens_for_completion(line, begidx, endidx) assert expected_tokens == tokens @@ -711,13 +711,13 @@ def test_tokens_for_completion_unclosed_quote(cmd2_app) -> None: def test_tokens_for_completion_punctuation(cmd2_app) -> None: """Test that redirectors and terminators are word delimiters""" - text = 'file' - line = f'command | < ;>>{text}' + text = "file" + line = f"command | < ;>>{text}" endidx = len(line) begidx = endidx - len(text) - expected_tokens = ['command', '|', '<', ';', '>>', 'file'] - expected_raw_tokens = ['command', '|', '<', ';', '>>', 'file'] + expected_tokens = ["command", "|", "<", ";", ">>", "file"] + expected_raw_tokens = ["command", "|", "<", ";", ">>", "file"] tokens, raw_tokens = cmd2_app.tokens_for_completion(line, begidx, endidx) assert expected_tokens == tokens @@ -726,13 +726,13 @@ def test_tokens_for_completion_punctuation(cmd2_app) -> None: def test_tokens_for_completion_quoted_punctuation(cmd2_app) -> None: """Test that quoted punctuation characters are not word delimiters""" - text = '>file' + text = ">file" line = f'command "{text}' endidx = len(line) begidx = endidx - len(text) - expected_tokens = ['command', '>file'] - expected_raw_tokens = ['command', '">file'] + expected_tokens = ["command", ">file"] + expected_raw_tokens = ["command", '">file'] tokens, raw_tokens = cmd2_app.tokens_for_completion(line, begidx, endidx) assert expected_tokens == tokens @@ -740,8 +740,8 @@ def test_tokens_for_completion_quoted_punctuation(cmd2_app) -> None: def test_add_opening_quote_double_quote_added(cmd2_app) -> None: - text = 'Ha' - line = f'test_basic {text}' + text = "Ha" + line = f"test_basic {text}" endidx = len(line) begidx = endidx - len(text) @@ -752,8 +752,8 @@ def test_add_opening_quote_double_quote_added(cmd2_app) -> None: def test_add_opening_quote_single_quote_added(cmd2_app) -> None: - text = 'Ch' - line = f'test_basic {text}' + text = "Ch" + line = f"test_basic {text}" endidx = len(line) begidx = endidx - len(text) @@ -764,8 +764,8 @@ def test_add_opening_quote_single_quote_added(cmd2_app) -> None: def test_add_opening_quote_nothing_added(cmd2_app) -> None: - text = 'P' - line = f'test_basic {text}' + text = "P" + line = f"test_basic {text}" endidx = len(line) begidx = endidx - len(text) @@ -781,7 +781,7 @@ def test_word_break_in_quote(cmd2_app) -> None: # Cmd2Completer still performs word breaks after a quote. Since space # is word-break character, it says the search text starts at 'S' and # passes that to the complete() function. - text = 'S' + text = "S" line = 'test_basic "Ham S' endidx = len(line) begidx = endidx - len(text) @@ -797,18 +797,18 @@ def test_word_break_in_quote(cmd2_app) -> None: def test_no_completer(cmd2_app) -> None: - text = '' - line = f'test_no_completer {text}' + text = "" + line = f"test_no_completer {text}" endidx = len(line) begidx = endidx - len(text) - expected = ['default'] + expected = ["default"] completions = cmd2_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: - text = '' + text = "" line = f'"{text}' endidx = len(line) begidx = endidx - len(text) @@ -818,23 +818,23 @@ def test_word_break_in_command(cmd2_app) -> None: def test_complete_multiline_on_single_line(cmd2_app) -> None: - text = '' - line = f'test_multiline {text}' + text = "" + line = f"test_multiline {text}" endidx = len(line) begidx = endidx - len(text) - expected = ['Basket', 'Basketball', 'Bat', 'Football', 'Space Ball'] + expected = ["Basket", "Basketball", "Bat", "Football", "Space Ball"] completions = cmd2_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: - text = 'Ba' - line = f'test_multiline\n{text}' + text = "Ba" + line = f"test_multiline\n{text}" endidx = len(line) begidx = endidx - len(text) - expected = ['Bat', 'Basket', 'Basketball'] + expected = ["Bat", "Basket", "Basketball"] completions = cmd2_app.complete(text, line, begidx, endidx) assert completions.to_strings() == Completions.from_values(expected).to_strings() @@ -876,7 +876,7 @@ def test_is_sorted() -> None: @pytest.mark.parametrize( - ('values', 'numeric_display'), + ("values", "numeric_display"), [ ([2, 3], True), ([2, 3.7], True), @@ -996,46 +996,46 @@ class RedirCompType(enum.Enum): @pytest.mark.parametrize( - ('line', 'comp_type'), + ("line", "comp_type"), [ - ('fake', RedirCompType.DEFAULT), - ('fake arg', RedirCompType.DEFAULT), - ('fake |', RedirCompType.SHELL_CMD), - ('fake | grep', RedirCompType.PATH), - ('fake | grep arg', RedirCompType.PATH), - ('fake | grep >', RedirCompType.PATH), - ('fake | grep > >', RedirCompType.NONE), - ('fake | grep > file', RedirCompType.NONE), - ('fake | grep > file >', RedirCompType.NONE), - ('fake | grep > file |', RedirCompType.SHELL_CMD), - ('fake | grep > file | grep', RedirCompType.PATH), - ('fake | |', RedirCompType.NONE), - ('fake | >', RedirCompType.NONE), - ('fake >', RedirCompType.PATH), - ('fake >>', RedirCompType.PATH), - ('fake > >', RedirCompType.NONE), - ('fake > |', RedirCompType.SHELL_CMD), - ('fake >> file |', RedirCompType.SHELL_CMD), - ('fake >> file | grep', RedirCompType.PATH), - ('fake > file', RedirCompType.NONE), - ('fake > file >', RedirCompType.NONE), - ('fake > file >>', RedirCompType.NONE), + ("fake", RedirCompType.DEFAULT), + ("fake arg", RedirCompType.DEFAULT), + ("fake |", RedirCompType.SHELL_CMD), + ("fake | grep", RedirCompType.PATH), + ("fake | grep arg", RedirCompType.PATH), + ("fake | grep >", RedirCompType.PATH), + ("fake | grep > >", RedirCompType.NONE), + ("fake | grep > file", RedirCompType.NONE), + ("fake | grep > file >", RedirCompType.NONE), + ("fake | grep > file |", RedirCompType.SHELL_CMD), + ("fake | grep > file | grep", RedirCompType.PATH), + ("fake | |", RedirCompType.NONE), + ("fake | >", RedirCompType.NONE), + ("fake >", RedirCompType.PATH), + ("fake >>", RedirCompType.PATH), + ("fake > >", RedirCompType.NONE), + ("fake > |", RedirCompType.SHELL_CMD), + ("fake >> file |", RedirCompType.SHELL_CMD), + ("fake >> file | grep", RedirCompType.PATH), + ("fake > file", RedirCompType.NONE), + ("fake > file >", RedirCompType.NONE), + ("fake > file >>", RedirCompType.NONE), ], ) def test_redirect_complete(cmd2_app, monkeypatch, line, comp_type) -> None: # Test both cases of allow_redirection cmd2_app.allow_redirection = True for _ in range(2): - shell_cmd_complete_mock = mock.MagicMock(name='shell_cmd_complete') + shell_cmd_complete_mock = mock.MagicMock(name="shell_cmd_complete") monkeypatch.setattr("cmd2.Cmd.shell_cmd_complete", shell_cmd_complete_mock) - path_complete_mock = mock.MagicMock(name='path_complete') + path_complete_mock = mock.MagicMock(name="path_complete") monkeypatch.setattr("cmd2.Cmd.path_complete", path_complete_mock) - default_complete_mock = mock.MagicMock(name='fake_completer') + default_complete_mock = mock.MagicMock(name="fake_completer") - text = '' - line = f'{line} {text}' + text = "" + line = f"{line} {text}" endidx = len(line) begidx = endidx - len(text) @@ -1059,8 +1059,8 @@ def test_redirect_complete(cmd2_app, monkeypatch, line, comp_type) -> None: def test_complete_set_value(cmd2_app) -> None: - text = '' - line = f'set foo {text}' + text = "" + line = f"set foo {text}" endidx = len(line) begidx = endidx - len(text) @@ -1071,8 +1071,8 @@ def test_complete_set_value(cmd2_app) -> None: def test_complete_set_value_invalid_settable(cmd2_app) -> None: - text = '' - line = f'set fake {text}' + text = "" + line = f"set fake {text}" endidx = len(line) begidx = endidx - len(text) @@ -1089,19 +1089,19 @@ def sc_app(): def test_cmd2_subcommand_completion(sc_app) -> None: - text = '' - line = f'base {text}' + text = "" + line = f"base {text}" endidx = len(line) begidx = endidx - len(text) - expected = ['bar', 'foo', 'sport'] + expected = ["bar", "foo", "sport"] completions = sc_app.complete(text, line, begidx, endidx) assert completions.to_strings() == Completions.from_values(expected).to_strings() def test_cmd2_subcommand_completion_nomatch(sc_app) -> None: - text = 'z' - line = f'base {text}' + text = "z" + line = f"base {text}" endidx = len(line) begidx = endidx - len(text) @@ -1110,19 +1110,19 @@ def test_cmd2_subcommand_completion_nomatch(sc_app) -> None: def test_help_subcommand_completion_multiple(sc_app) -> None: - text = '' - line = f'help base {text}' + text = "" + line = f"help base {text}" endidx = len(line) begidx = endidx - len(text) - expected = ['bar', 'foo', 'sport'] + expected = ["bar", "foo", "sport"] completions = sc_app.complete(text, line, begidx, endidx) assert completions.to_strings() == Completions.from_values(expected).to_strings() def test_help_subcommand_completion_nomatch(sc_app) -> None: - text = 'z' - line = f'help base {text}' + text = "z" + line = f"help base {text}" endidx = len(line) begidx = endidx - len(text) @@ -1132,12 +1132,12 @@ def test_help_subcommand_completion_nomatch(sc_app) -> None: def test_subcommand_tab_completion(sc_app) -> None: # This makes sure the correct completer for the sport subcommand is called - text = 'Foot' - line = f'base sport {text}' + text = "Foot" + line = f"base sport {text}" endidx = len(line) begidx = endidx - len(text) - expected = ['Football'] + expected = ["Football"] completions = sc_app.complete(text, line, begidx, endidx) assert completions.to_strings() == Completions.from_values(expected).to_strings() @@ -1145,8 +1145,8 @@ def test_subcommand_tab_completion(sc_app) -> None: def test_subcommand_tab_completion_with_no_completer(sc_app) -> None: # This tests what happens when a subcommand has no completer # In this case, the foo subcommand has no completer defined - text = 'Foot' - line = f'base foo {text}' + text = "Foot" + line = f"base foo {text}" endidx = len(line) begidx = endidx - len(text) @@ -1172,41 +1172,41 @@ def base_foo(self, args) -> None: def base_bar(self, args) -> None: """Bar subcommand of base command""" - self.poutput(f'(({args.z}))') + self.poutput(f"(({args.z}))") def base_sport(self, args) -> None: """Sport subcommand of base command""" - self.poutput(f'Sport is {args.sport}') + self.poutput(f"Sport is {args.sport}") # create the top-level parser for the base command base_parser = cmd2.Cmd2ArgumentParser() - base_subparsers = base_parser.add_subparsers(title='subcommands', help='subcommand help') + base_subparsers = base_parser.add_subparsers(title="subcommands", help="subcommand help") # create the parser for the "foo" subcommand - parser_foo = base_subparsers.add_parser('foo', help='foo help') - parser_foo.add_argument('-x', type=int, default=1, help='integer') - parser_foo.add_argument('y', type=float, help='float') + parser_foo = base_subparsers.add_parser("foo", help="foo help") + parser_foo.add_argument("-x", type=int, default=1, help="integer") + parser_foo.add_argument("y", type=float, help="float") parser_foo.set_defaults(func=base_foo) # create the parser for the "bar" subcommand - parser_bar = base_subparsers.add_parser('bar', help='bar help') - parser_bar.add_argument('z', help='string') + parser_bar = base_subparsers.add_parser("bar", help="bar help") + parser_bar.add_argument("z", help="string") parser_bar.set_defaults(func=base_bar) # create the parser for the "sport" subcommand - parser_sport = base_subparsers.add_parser('sport', help='sport help') - sport_arg = parser_sport.add_argument('sport', help='Enter name of a sport', choices=sport_item_strs) + parser_sport = base_subparsers.add_parser("sport", help="sport help") + sport_arg = parser_sport.add_argument("sport", help="Enter name of a sport", choices=sport_item_strs) @cmd2.with_argparser(base_parser, with_unknown_args=True) def do_base(self, args) -> None: """Base command help""" - func = getattr(args, 'func', None) + func = getattr(args, "func", None) if func is not None: # Call whatever subcommand function was selected func(self, args) else: # No subcommand was provided, so call help - self.do_help('base') + self.do_help("base") @pytest.fixture @@ -1216,19 +1216,19 @@ def scu_app(): def test_subcmd_with_unknown_completion(scu_app) -> None: - text = '' - line = f'base {text}' + text = "" + line = f"base {text}" endidx = len(line) begidx = endidx - len(text) - expected = ['bar', 'foo', 'sport'] + expected = ["bar", "foo", "sport"] completions = scu_app.complete(text, line, begidx, endidx) assert completions.to_strings() == Completions.from_values(expected).to_strings() def test_subcmd_with_unknown_completion_nomatch(scu_app) -> None: - text = 'z' - line = f'base {text}' + text = "z" + line = f"base {text}" endidx = len(line) begidx = endidx - len(text) @@ -1237,29 +1237,29 @@ def test_subcmd_with_unknown_completion_nomatch(scu_app) -> None: def test_help_subcommand_completion_scu(scu_app) -> None: - text = '' - line = f'help base {text}' + text = "" + line = f"help base {text}" endidx = len(line) begidx = endidx - len(text) - expected = ['bar', 'foo', 'sport'] + expected = ["bar", "foo", "sport"] completions = scu_app.complete(text, line, begidx, endidx) assert completions.to_strings() == Completions.from_values(expected).to_strings() def test_help_subcommand_completion_with_flags_before_command(scu_app) -> None: - text = '' - line = f'help -h -v base {text}' + text = "" + line = f"help -h -v base {text}" endidx = len(line) begidx = endidx - len(text) - expected = ['bar', 'foo', 'sport'] + expected = ["bar", "foo", "sport"] completions = scu_app.complete(text, line, begidx, endidx) assert completions.to_strings() == Completions.from_values(expected).to_strings() def test_complete_help_subcommands_with_blank_command(scu_app) -> None: - text = '' + text = "" line = f'help "" {text}' endidx = len(line) begidx = endidx - len(text) @@ -1269,8 +1269,8 @@ def test_complete_help_subcommands_with_blank_command(scu_app) -> None: def test_help_subcommand_completion_nomatch_scu(scu_app) -> None: - text = 'z' - line = f'help base {text}' + text = "z" + line = f"help base {text}" endidx = len(line) begidx = endidx - len(text) @@ -1280,12 +1280,12 @@ def test_help_subcommand_completion_nomatch_scu(scu_app) -> None: def test_subcommand_tab_completion_scu(scu_app) -> None: # This makes sure the correct completer for the sport subcommand is called - text = 'Foot' - line = f'base sport {text}' + text = "Foot" + line = f"base sport {text}" endidx = len(line) begidx = endidx - len(text) - expected = ['Football'] + expected = ["Football"] completions = scu_app.complete(text, line, begidx, endidx) assert completions.to_strings() == Completions.from_values(expected).to_strings() @@ -1293,8 +1293,8 @@ def test_subcommand_tab_completion_scu(scu_app) -> None: def test_subcommand_tab_completion_with_no_completer_scu(scu_app) -> None: # This tests what happens when a subcommand has no completer # In this case, the foo subcommand has no completer defined - text = 'Foot' - line = f'base foo {text}' + text = "Foot" + line = f"base foo {text}" endidx = len(line) begidx = endidx - len(text) diff --git a/tests/test_dynamic_complete_style.py b/tests/test_dynamic_complete_style.py index aa03b15e5..52dc3fe66 100644 --- a/tests/test_dynamic_complete_style.py +++ b/tests/test_dynamic_complete_style.py @@ -14,7 +14,7 @@ def do_foo(self, args): def complete_foo(self, text, line, begidx, endidx) -> Completions: # Return 10 items - items = [f'item{i}' for i in range(10) if f'item{i}'.startswith(text)] + items = [f"item{i}" for i in range(10) if f"item{i}".startswith(text)] return Completions.from_values(items) def do_bar(self, args): @@ -22,7 +22,7 @@ def do_bar(self, args): def complete_bar(self, text, line, begidx, endidx) -> Completions: # Return 5 items - items = [f'item{i}' for i in range(5) if f'item{i}'.startswith(text)] + items = [f"item{i}" for i in range(5) if f"item{i}".startswith(text)] return Completions.from_values(items) @@ -44,11 +44,11 @@ def test_dynamic_complete_style(app): # Complete 'foo' which has 10 items (> 7) # text='item', state=0, line='foo item', begidx=4, endidx=8 - app.complete('item', 'foo item', 4, 8) + app.complete("item", "foo item", 4, 8) assert app.active_session.complete_style == CompleteStyle.MULTI_COLUMN # Complete 'bar' which has 5 items (<= 7) - app.complete('item', 'bar item', 4, 8) + app.complete("item", "bar item", 4, 8) assert app.active_session.complete_style == CompleteStyle.COLUMN @@ -64,12 +64,12 @@ def test_dynamic_complete_style_custom_limit(app): app.max_column_completion_results = 3 # Complete 'bar' which has 5 items (> 3) - app.complete('item', 'bar item', 4, 8) + app.complete("item", "bar item", 4, 8) assert app.active_session.complete_style == CompleteStyle.MULTI_COLUMN # Change limit to 15 app.max_column_completion_results = 15 # Complete 'foo' which has 10 items (<= 15) - app.complete('item', 'foo item', 4, 8) + app.complete("item", "foo item", 4, 8) assert app.active_session.complete_style == CompleteStyle.COLUMN diff --git a/tests/test_future_annotations.py b/tests/test_future_annotations.py index 8ba0741a6..743522f87 100644 --- a/tests/test_future_annotations.py +++ b/tests/test_future_annotations.py @@ -17,6 +17,6 @@ def hook(self: cmd2.Cmd, data: cmd2.plugin.CommandFinalizationData) -> cmd2.plug return data hook_app = HookApp() - out, _err = run_cmd(hook_app, '') - expected = normalize('') + out, _err = run_cmd(hook_app, "") + expected = normalize("") assert out == expected diff --git a/tests/test_history.py b/tests/test_history.py index 56758afc4..8fff6b7e5 100644 --- a/tests/test_history.py +++ b/tests/test_history.py @@ -38,16 +38,16 @@ def test_pt_add_history_item() -> None: # Create a history object and add some initial items history = InMemoryHistory() - history.append_string('command one') - history.append_string('command two') - assert 'command one' in history.get_strings() + history.append_string("command one") + history.append_string("command two") + assert "command one" in history.get_strings() assert len(history.get_strings()) == 2 # Start a session and use this history session = PromptSession(history=history, input=DummyInput(), output=DummyOutput()) - session.history.get_strings().append('new command') - assert 'new command' not in session.history.get_strings() + session.history.get_strings().append("new command") + assert "new command" not in session.history.get_strings() assert len(history.get_strings()) == 2 @@ -66,20 +66,20 @@ def hist(): return History( [ - HistoryItem(Statement('', raw='first')), - HistoryItem(Statement('', raw='second')), - HistoryItem(Statement('', raw='third')), - HistoryItem(Statement('', raw='fourth')), + HistoryItem(Statement("", raw="first")), + HistoryItem(Statement("", raw="second")), + HistoryItem(Statement("", raw="third")), + HistoryItem(Statement("", raw="fourth")), ] ) # Represents the hist fixture's JSON hist_json = ( - '{\n' + "{\n" ' "history_version": "4.0.0",\n' ' "history_items": [\n' - ' {\n' + " {\n" ' "statement": {\n' ' "args": "",\n' ' "raw": "first",\n' @@ -89,9 +89,9 @@ def hist(): ' "suffix": "",\n' ' "redirector": "",\n' ' "redirect_to": ""\n' - ' }\n' - ' },\n' - ' {\n' + " }\n" + " },\n" + " {\n" ' "statement": {\n' ' "args": "",\n' ' "raw": "second",\n' @@ -101,9 +101,9 @@ def hist(): ' "suffix": "",\n' ' "redirector": "",\n' ' "redirect_to": ""\n' - ' }\n' - ' },\n' - ' {\n' + " }\n" + " },\n" + " {\n" ' "statement": {\n' ' "args": "",\n' ' "raw": "third",\n' @@ -113,9 +113,9 @@ def hist(): ' "suffix": "",\n' ' "redirector": "",\n' ' "redirect_to": ""\n' - ' }\n' - ' },\n' - ' {\n' + " }\n" + " },\n" + " {\n" ' "statement": {\n' ' "args": "",\n' ' "raw": "fourth",\n' @@ -125,10 +125,10 @@ def hist(): ' "suffix": "",\n' ' "redirector": "",\n' ' "redirect_to": ""\n' - ' }\n' - ' }\n' - ' ]\n' - '}' + " }\n" + " }\n" + " ]\n" + "}" ) @@ -144,84 +144,84 @@ def persisted_hist(): h = History( [ - HistoryItem(Statement('', raw='first')), - HistoryItem(Statement('', raw='second')), - HistoryItem(Statement('', raw='third')), - HistoryItem(Statement('', raw='fourth')), + HistoryItem(Statement("", raw="first")), + HistoryItem(Statement("", raw="second")), + HistoryItem(Statement("", raw="third")), + HistoryItem(Statement("", raw="fourth")), ] ) h.start_session() - h.append(Statement('', raw='fifth')) - h.append(Statement('', raw='sixth')) + h.append(Statement("", raw="fifth")) + h.append(Statement("", raw="sixth")) return h def test_history_class_span(hist) -> None: - span = hist.span('2..') + span = hist.span("2..") assert len(span) == 3 - assert span[2].statement.raw == 'second' - assert span[3].statement.raw == 'third' - assert span[4].statement.raw == 'fourth' + assert span[2].statement.raw == "second" + assert span[3].statement.raw == "third" + assert span[4].statement.raw == "fourth" - span = hist.span('2:') + span = hist.span("2:") assert len(span) == 3 - assert span[2].statement.raw == 'second' - assert span[3].statement.raw == 'third' - assert span[4].statement.raw == 'fourth' + assert span[2].statement.raw == "second" + assert span[3].statement.raw == "third" + assert span[4].statement.raw == "fourth" - span = hist.span('-2..') + span = hist.span("-2..") assert len(span) == 2 - assert span[3].statement.raw == 'third' - assert span[4].statement.raw == 'fourth' + assert span[3].statement.raw == "third" + assert span[4].statement.raw == "fourth" - span = hist.span('-2:') + span = hist.span("-2:") assert len(span) == 2 - assert span[3].statement.raw == 'third' - assert span[4].statement.raw == 'fourth' + assert span[3].statement.raw == "third" + assert span[4].statement.raw == "fourth" - span = hist.span('1..3') + span = hist.span("1..3") assert len(span) == 3 - assert span[1].statement.raw == 'first' - assert span[2].statement.raw == 'second' - assert span[3].statement.raw == 'third' + assert span[1].statement.raw == "first" + assert span[2].statement.raw == "second" + assert span[3].statement.raw == "third" - span = hist.span('1:3') + span = hist.span("1:3") assert len(span) == 3 - assert span[1].statement.raw == 'first' - assert span[2].statement.raw == 'second' - assert span[3].statement.raw == 'third' + assert span[1].statement.raw == "first" + assert span[2].statement.raw == "second" + assert span[3].statement.raw == "third" - span = hist.span('2:-1') + span = hist.span("2:-1") assert len(span) == 3 - assert span[2].statement.raw == 'second' - assert span[3].statement.raw == 'third' - assert span[4].statement.raw == 'fourth' + assert span[2].statement.raw == "second" + assert span[3].statement.raw == "third" + assert span[4].statement.raw == "fourth" - span = hist.span('-3:4') + span = hist.span("-3:4") assert len(span) == 3 - assert span[2].statement.raw == 'second' - assert span[3].statement.raw == 'third' - assert span[4].statement.raw == 'fourth' + assert span[2].statement.raw == "second" + assert span[3].statement.raw == "third" + assert span[4].statement.raw == "fourth" - span = hist.span('-4:-2') + span = hist.span("-4:-2") assert len(span) == 3 - assert span[1].statement.raw == 'first' - assert span[2].statement.raw == 'second' - assert span[3].statement.raw == 'third' + assert span[1].statement.raw == "first" + assert span[2].statement.raw == "second" + assert span[3].statement.raw == "third" - span = hist.span(':-2') + span = hist.span(":-2") assert len(span) == 3 - assert span[1].statement.raw == 'first' - assert span[2].statement.raw == 'second' - assert span[3].statement.raw == 'third' + assert span[1].statement.raw == "first" + assert span[2].statement.raw == "second" + assert span[3].statement.raw == "third" - span = hist.span('..-2') + span = hist.span("..-2") assert len(span) == 3 - assert span[1].statement.raw == 'first' - assert span[2].statement.raw == 'second' - assert span[3].statement.raw == 'third' + assert span[1].statement.raw == "first" + assert span[2].statement.raw == "second" + assert span[3].statement.raw == "third" - value_errors = ['fred', 'fred:joe', '2', '-2', 'a..b', '2 ..', '1 : 3', '1:0', '0:3'] + value_errors = ["fred", "fred:joe", "2", "-2", "a..b", "2 ..", "1 : 3", "1:0", "0:3"] expected_err = "History indices must be positive or negative integers, and may not be zero." for tryit in value_errors: with pytest.raises(ValueError, match=expected_err): @@ -229,50 +229,50 @@ def test_history_class_span(hist) -> None: def test_persisted_history_span(persisted_hist) -> None: - span = persisted_hist.span('2..') + span = persisted_hist.span("2..") assert len(span) == 5 - assert span[2].statement.raw == 'second' - assert span[3].statement.raw == 'third' - assert span[4].statement.raw == 'fourth' - assert span[5].statement.raw == 'fifth' - assert span[6].statement.raw == 'sixth' + assert span[2].statement.raw == "second" + assert span[3].statement.raw == "third" + assert span[4].statement.raw == "fourth" + assert span[5].statement.raw == "fifth" + assert span[6].statement.raw == "sixth" - span = persisted_hist.span('-2..') + span = persisted_hist.span("-2..") assert len(span) == 2 - assert span[5].statement.raw == 'fifth' - assert span[6].statement.raw == 'sixth' + assert span[5].statement.raw == "fifth" + assert span[6].statement.raw == "sixth" - span = persisted_hist.span('1..3') + span = persisted_hist.span("1..3") assert len(span) == 3 - assert span[1].statement.raw == 'first' - assert span[2].statement.raw == 'second' - assert span[3].statement.raw == 'third' + assert span[1].statement.raw == "first" + assert span[2].statement.raw == "second" + assert span[3].statement.raw == "third" - span = persisted_hist.span('2:-1') + span = persisted_hist.span("2:-1") assert len(span) == 5 - assert span[2].statement.raw == 'second' - assert span[3].statement.raw == 'third' - assert span[4].statement.raw == 'fourth' - assert span[5].statement.raw == 'fifth' - assert span[6].statement.raw == 'sixth' + assert span[2].statement.raw == "second" + assert span[3].statement.raw == "third" + assert span[4].statement.raw == "fourth" + assert span[5].statement.raw == "fifth" + assert span[6].statement.raw == "sixth" - span = persisted_hist.span('-3:4') + span = persisted_hist.span("-3:4") assert len(span) == 1 - assert span[4].statement.raw == 'fourth' + assert span[4].statement.raw == "fourth" - span = persisted_hist.span(':-2', include_persisted=True) + span = persisted_hist.span(":-2", include_persisted=True) assert len(span) == 5 - assert span[1].statement.raw == 'first' - assert span[2].statement.raw == 'second' - assert span[3].statement.raw == 'third' - assert span[4].statement.raw == 'fourth' - assert span[5].statement.raw == 'fifth' + assert span[1].statement.raw == "first" + assert span[2].statement.raw == "second" + assert span[3].statement.raw == "third" + assert span[4].statement.raw == "fourth" + assert span[5].statement.raw == "fifth" - span = persisted_hist.span(':-2', include_persisted=False) + span = persisted_hist.span(":-2", include_persisted=False) assert len(span) == 1 - assert span[5].statement.raw == 'fifth' + assert span[5].statement.raw == "fifth" - value_errors = ['fred', 'fred:joe', '2', '-2', 'a..b', '2 ..', '1 : 3', '1:0', '0:3'] + value_errors = ["fred", "fred:joe", "2", "-2", "a..b", "2 ..", "1 : 3", "1:0", "0:3"] expected_err = "History indices must be positive or negative integers, and may not be zero." for tryit in value_errors: with pytest.raises(ValueError, match=expected_err): @@ -280,10 +280,10 @@ def test_persisted_history_span(persisted_hist) -> None: def test_history_class_get(hist) -> None: - assert hist.get(1).statement.raw == 'first' - assert hist.get(3).statement.raw == 'third' + assert hist.get(1).statement.raw == "first" + assert hist.get(3).statement.raw == "third" assert hist.get(-2) == hist[-2] - assert hist.get(-1).statement.raw == 'fourth' + assert hist.get(-1).statement.raw == "fourth" with pytest.raises(IndexError): hist.get(0) @@ -293,24 +293,24 @@ def test_history_class_get(hist) -> None: def test_history_str_search(hist) -> None: - items = hist.str_search('ir') + items = hist.str_search("ir") assert len(items) == 2 - assert items[1].statement.raw == 'first' - assert items[3].statement.raw == 'third' + assert items[1].statement.raw == "first" + assert items[3].statement.raw == "third" - items = hist.str_search('rth') + items = hist.str_search("rth") assert len(items) == 1 - assert items[4].statement.raw == 'fourth' + assert items[4].statement.raw == "fourth" def test_history_regex_search(hist) -> None: - items = hist.regex_search('/i.*d/') + items = hist.regex_search("/i.*d/") assert len(items) == 1 - assert items[3].statement.raw == 'third' + assert items[3].statement.raw == "third" - items = hist.regex_search('s[a-z]+ond') + items = hist.regex_search("s[a-z]+ond") assert len(items) == 1 - assert items[2].statement.raw == 'second' + assert items[2].statement.raw == "second" def test_history_max_length_zero(hist) -> None: @@ -326,8 +326,8 @@ def test_history_max_length_negative(hist) -> None: def test_history_max_length(hist) -> None: hist.truncate(2) assert len(hist) == 2 - assert hist.get(1).statement.raw == 'third' - assert hist.get(2).statement.raw == 'fourth' + assert hist.get(1).statement.raw == "third" + assert hist.get(2).statement.raw == "fourth" def test_history_to_json(hist) -> None: @@ -375,9 +375,9 @@ def histitem(): ) statement = Statement( - 'history', - raw='help history', - command='help', + "history", + raw="help history", + command="help", ) return HistoryItem(statement) @@ -389,16 +389,16 @@ def parser(): ) return StatementParser( - terminators=[';', '&'], - multiline_commands=['multiline'], + terminators=[";", "&"], + multiline_commands=["multiline"], aliases={ - 'helpalias': 'help', - '42': 'theanswer', - 'l': '!ls -al', - 'anothermultiline': 'multiline', - 'fake': 'run_pyscript', + "helpalias": "help", + "42": "theanswer", + "l": "!ls -al", + "anothermultiline": "multiline", + "fake": "run_pyscript", }, - shortcuts={'?': 'help', '!': 'shell'}, + shortcuts={"?": "help", "!": "shell"}, ) @@ -407,7 +407,7 @@ def test_multiline_histitem(parser) -> None: History, ) - line = 'multiline foo\nbar\n\n' + line = "multiline foo\nbar\n\n" statement = parser.parse(line) history = History() history.append(statement) @@ -415,7 +415,7 @@ def test_multiline_histitem(parser) -> None: hist_item = history[0] assert hist_item.raw == line pr_lines = hist_item.pr(1).splitlines() - assert pr_lines[0].endswith('multiline foo bar') + assert pr_lines[0].endswith("multiline foo bar") def test_multiline_with_quotes_histitem(parser) -> None: @@ -435,7 +435,7 @@ def test_multiline_with_quotes_histitem(parser) -> None: # Since spaces and newlines in quotes are preserved, this history entry spans multiple lines. pr_lines = hist_item.pr(1).splitlines() assert pr_lines[0].endswith('Look, "There are newlines') - assert pr_lines[1] == ' and spaces ' + assert pr_lines[1] == " and spaces " assert pr_lines[2] == ' " in quotes.;' @@ -444,7 +444,7 @@ def test_multiline_histitem_verbose(parser) -> None: History, ) - line = 'multiline foo\nbar\n\n' + line = "multiline foo\nbar\n\n" statement = parser.parse(line) history = History() history.append(statement) @@ -452,8 +452,8 @@ def test_multiline_histitem_verbose(parser) -> None: hist_item = history[0] assert hist_item.raw == line pr_lines = hist_item.pr(1, verbose=True).splitlines() - assert pr_lines[0].endswith('multiline foo') - assert pr_lines[1] == 'bar' + assert pr_lines[0].endswith("multiline foo") + assert pr_lines[1] == "bar" def test_single_line_format_blank(parser) -> None: @@ -475,27 +475,27 @@ def test_history_item_instantiate() -> None: ) Statement( - 'history', - raw='help history', - command='help', + "history", + raw="help history", + command="help", ) with pytest.raises(TypeError): _ = HistoryItem() def test_history_item_properties(histitem) -> None: - assert histitem.raw == 'help history' - assert histitem.expanded == 'help history' - assert str(histitem) == 'help history' + assert histitem.raw == "help history" + assert histitem.expanded == "help history" + assert str(histitem) == "help history" # # test history command # def test_base_history(base_app) -> None: - run_cmd(base_app, 'help') - run_cmd(base_app, 'shortcuts') - out, _err = run_cmd(base_app, 'history') + run_cmd(base_app, "help") + run_cmd(base_app, "shortcuts") + out, _err = run_cmd(base_app, "history") expected = normalize( """ 1 help @@ -504,7 +504,7 @@ def test_base_history(base_app) -> None: ) assert out == expected - out, _err = run_cmd(base_app, 'history he') + out, _err = run_cmd(base_app, "history he") expected = normalize( """ 1 help @@ -513,7 +513,7 @@ def test_base_history(base_app) -> None: assert out == expected verify_hi_last_result(base_app, 1) - out, _err = run_cmd(base_app, 'history sh') + out, _err = run_cmd(base_app, "history sh") expected = normalize( """ 2 shortcuts @@ -524,9 +524,9 @@ def test_base_history(base_app) -> None: def test_history_script_format(base_app) -> None: - run_cmd(base_app, 'help') - run_cmd(base_app, 'shortcuts') - out, _err = run_cmd(base_app, 'history -s') + run_cmd(base_app, "help") + run_cmd(base_app, "shortcuts") + out, _err = run_cmd(base_app, "history -s") expected = normalize( """ help @@ -538,10 +538,10 @@ def test_history_script_format(base_app) -> None: def test_history_with_string_argument(base_app) -> None: - run_cmd(base_app, 'help') - run_cmd(base_app, 'shortcuts') - run_cmd(base_app, 'help history') - out, _err = run_cmd(base_app, 'history help') + run_cmd(base_app, "help") + run_cmd(base_app, "shortcuts") + run_cmd(base_app, "help history") + out, _err = run_cmd(base_app, "history help") expected = normalize( """ 1 help @@ -553,11 +553,11 @@ def test_history_with_string_argument(base_app) -> None: def test_history_expanded_with_string_argument(base_app) -> None: - run_cmd(base_app, 'alias create sc shortcuts') - run_cmd(base_app, 'help') - run_cmd(base_app, 'help history') - run_cmd(base_app, 'sc') - out, _err = run_cmd(base_app, 'history -v shortcuts') + run_cmd(base_app, "alias create sc shortcuts") + run_cmd(base_app, "help") + run_cmd(base_app, "help history") + run_cmd(base_app, "sc") + out, _err = run_cmd(base_app, "history -v shortcuts") expected = normalize( """ 1 alias create sc shortcuts @@ -570,11 +570,11 @@ def test_history_expanded_with_string_argument(base_app) -> None: def test_history_expanded_with_regex_argument(base_app) -> None: - run_cmd(base_app, 'alias create sc shortcuts') - run_cmd(base_app, 'help') - run_cmd(base_app, 'help history') - run_cmd(base_app, 'sc') - out, _err = run_cmd(base_app, 'history -v /sh.*cuts/') + run_cmd(base_app, "alias create sc shortcuts") + run_cmd(base_app, "help") + run_cmd(base_app, "help history") + run_cmd(base_app, "sc") + out, _err = run_cmd(base_app, "history -v /sh.*cuts/") expected = normalize( """ 1 alias create sc shortcuts @@ -587,9 +587,9 @@ def test_history_expanded_with_regex_argument(base_app) -> None: def test_history_with_integer_argument(base_app) -> None: - run_cmd(base_app, 'help') - run_cmd(base_app, 'shortcuts') - out, _err = run_cmd(base_app, 'history 1') + run_cmd(base_app, "help") + run_cmd(base_app, "shortcuts") + out, _err = run_cmd(base_app, "history 1") expected = normalize( """ 1 help @@ -600,10 +600,10 @@ def test_history_with_integer_argument(base_app) -> None: def test_history_with_integer_span(base_app) -> None: - run_cmd(base_app, 'help') - run_cmd(base_app, 'shortcuts') - run_cmd(base_app, 'help history') - out, _err = run_cmd(base_app, 'history 1..2') + run_cmd(base_app, "help") + run_cmd(base_app, "shortcuts") + run_cmd(base_app, "help history") + out, _err = run_cmd(base_app, "history 1..2") expected = normalize( """ 1 help @@ -615,10 +615,10 @@ def test_history_with_integer_span(base_app) -> None: def test_history_with_span_start(base_app) -> None: - run_cmd(base_app, 'help') - run_cmd(base_app, 'shortcuts') - run_cmd(base_app, 'help history') - out, _err = run_cmd(base_app, 'history 2:') + run_cmd(base_app, "help") + run_cmd(base_app, "shortcuts") + run_cmd(base_app, "help history") + out, _err = run_cmd(base_app, "history 2:") expected = normalize( """ 2 shortcuts @@ -630,10 +630,10 @@ def test_history_with_span_start(base_app) -> None: def test_history_with_span_end(base_app) -> None: - run_cmd(base_app, 'help') - run_cmd(base_app, 'shortcuts') - run_cmd(base_app, 'help history') - out, _err = run_cmd(base_app, 'history :2') + run_cmd(base_app, "help") + run_cmd(base_app, "shortcuts") + run_cmd(base_app, "help history") + out, _err = run_cmd(base_app, "history :2") expected = normalize( """ 1 help @@ -645,36 +645,36 @@ def test_history_with_span_end(base_app) -> None: def test_history_with_span_index_error(base_app) -> None: - run_cmd(base_app, 'help') - run_cmd(base_app, 'help history') - run_cmd(base_app, '!ls -hal :') + run_cmd(base_app, "help") + run_cmd(base_app, "help history") + run_cmd(base_app, "!ls -hal :") expected_err = "History indices must be positive or negative integers, and may not be zero." with pytest.raises(ValueError, match=expected_err): base_app.onecmd('history "hal :"') def test_history_output_file() -> None: - app = cmd2.Cmd(multiline_commands=['alias']) - run_cmd(app, 'help') - run_cmd(app, 'shortcuts') - run_cmd(app, 'help history') - run_cmd(app, 'alias create my_alias history;') + app = cmd2.Cmd(multiline_commands=["alias"]) + run_cmd(app, "help") + run_cmd(app, "shortcuts") + run_cmd(app, "help history") + run_cmd(app, "alias create my_alias history;") - fd, fname = tempfile.mkstemp(prefix='', suffix='.txt') + fd, fname = tempfile.mkstemp(prefix="", suffix=".txt") os.close(fd) run_cmd(app, f'history -o "{fname}"') assert app.last_result is True - expected = normalize('help\nshortcuts\nhelp history\nalias create my_alias history;') + expected = normalize("help\nshortcuts\nhelp history\nalias create my_alias history;") with open(fname) as f: content = normalize(f.read()) assert content == expected def test_history_bad_output_file(base_app) -> None: - run_cmd(base_app, 'help') - run_cmd(base_app, 'shortcuts') - run_cmd(base_app, 'help history') + run_cmd(base_app, "help") + run_cmd(base_app, "shortcuts") + run_cmd(base_app, "help history") fname = os.path.join(os.path.sep, "fake", "fake", "fake") out, err = run_cmd(base_app, f'history -o "{fname}"') @@ -685,25 +685,25 @@ def test_history_bad_output_file(base_app) -> None: def test_history_edit(monkeypatch) -> None: - app = cmd2.Cmd(multiline_commands=['alias']) + app = cmd2.Cmd(multiline_commands=["alias"]) # Set a fake editor just to make sure we have one. We aren't really # going to call it due to the mock - app.editor = 'fooedit' + app.editor = "fooedit" # Mock out the run_editor call so we don't actually open an editor - edit_mock = mock.MagicMock(name='run_editor') + edit_mock = mock.MagicMock(name="run_editor") monkeypatch.setattr("cmd2.Cmd.run_editor", edit_mock) # Mock out the run_script call since the mocked edit won't produce a file - run_script_mock = mock.MagicMock(name='do_run_script') + run_script_mock = mock.MagicMock(name="do_run_script") monkeypatch.setattr("cmd2.Cmd.do_run_script", run_script_mock) # Put commands in history - run_cmd(app, 'help') - run_cmd(app, 'alias create my_alias history;') + run_cmd(app, "help") + run_cmd(app, "alias create my_alias history;") - run_cmd(app, 'history -e 1:2') + run_cmd(app, "history -e 1:2") # Make sure both functions were called edit_mock.assert_called_once() @@ -712,8 +712,8 @@ def test_history_edit(monkeypatch) -> None: def test_history_run_all_commands(base_app) -> None: # make sure we refuse to run all commands as a default - run_cmd(base_app, 'shortcuts') - out, err = run_cmd(base_app, 'history -r') + run_cmd(base_app, "shortcuts") + out, err = run_cmd(base_app, "history -r") assert not out assert err[0].startswith("Cowardly refusing to run all") @@ -721,8 +721,8 @@ def test_history_run_all_commands(base_app) -> None: def test_history_run_one_command(base_app) -> None: - out1, _err1 = run_cmd(base_app, 'help') - out2, _err2 = run_cmd(base_app, 'history -r 1') + out1, _err1 = run_cmd(base_app, "help") + out2, _err2 = run_cmd(base_app, "history -r 1") assert out1 == out2 assert base_app.last_result is True @@ -730,52 +730,52 @@ def test_history_run_one_command(base_app) -> None: def test_history_clear(mocker, hist_file) -> None: # Add commands to history app = cmd2.Cmd(persistent_history_file=hist_file) - run_cmd(app, 'help') - run_cmd(app, 'alias') + run_cmd(app, "help") + run_cmd(app, "alias") # Make sure history has items - out, err = run_cmd(app, 'history') + out, err = run_cmd(app, "history") assert out verify_hi_last_result(app, 2) # Clear the history - run_cmd(app, 'history --clear') + run_cmd(app, "history --clear") assert app.last_result is True # Make sure history is empty and its file is gone - out, err = run_cmd(app, 'history') + out, err = run_cmd(app, "history") assert out == [] assert not os.path.exists(hist_file) verify_hi_last_result(app, 0) # Clear the history again and make sure the FileNotFoundError from trying to delete missing history file is silent - run_cmd(app, 'history --clear') + run_cmd(app, "history --clear") assert app.last_result is True # Cause os.remove to fail and make sure error gets printed - mock_remove = mocker.patch('os.remove') + mock_remove = mocker.patch("os.remove") mock_remove.side_effect = OSError - out, err = run_cmd(app, 'history --clear') + out, err = run_cmd(app, "history --clear") assert out == [] - assert 'Error removing history file' in err[0] + assert "Error removing history file" in err[0] assert app.last_result is False def test_history_verbose_with_other_options(base_app) -> None: # make sure -v shows a usage error if any other options are present - options_to_test = ['-r', '-e', '-o file', '-c', '-x'] + options_to_test = ["-r", "-e", "-o file", "-c", "-x"] for opt in options_to_test: - out, _err = run_cmd(base_app, 'history -v ' + opt) - assert '-v cannot be used with any other options' in out + out, _err = run_cmd(base_app, "history -v " + opt) + assert "-v cannot be used with any other options" in out assert base_app.last_result is False def test_history_verbose(base_app) -> None: # validate function of -v option - run_cmd(base_app, 'alias create s shortcuts') - run_cmd(base_app, 's') - out, _err = run_cmd(base_app, 'history -v') + run_cmd(base_app, "alias create s shortcuts") + run_cmd(base_app, "s") + out, _err = run_cmd(base_app, "history -v") expected = normalize( """ @@ -790,68 +790,68 @@ def test_history_verbose(base_app) -> None: def test_history_script_with_invalid_options(base_app) -> None: # make sure -s shows a usage error if -c, -r, -e, or -o are present - options_to_test = ['-r', '-e', '-o file', '-c'] + options_to_test = ["-r", "-e", "-o file", "-c"] for opt in options_to_test: - out, _err = run_cmd(base_app, 'history -s ' + opt) - assert '-s and -x cannot be used with -c, -r, -e, or -o' in out + out, _err = run_cmd(base_app, "history -s " + opt) + assert "-s and -x cannot be used with -c, -r, -e, or -o" in out assert base_app.last_result is False def test_history_script(base_app) -> None: - cmds = ['alias create s shortcuts', 's'] + cmds = ["alias create s shortcuts", "s"] for cmd in cmds: run_cmd(base_app, cmd) - out, _err = run_cmd(base_app, 'history -s') + out, _err = run_cmd(base_app, "history -s") assert out == cmds verify_hi_last_result(base_app, 2) def test_history_expanded_with_invalid_options(base_app) -> None: # make sure -x shows a usage error if -c, -r, -e, or -o are present - options_to_test = ['-r', '-e', '-o file', '-c'] + options_to_test = ["-r", "-e", "-o file", "-c"] for opt in options_to_test: - out, _err = run_cmd(base_app, 'history -x ' + opt) - assert '-s and -x cannot be used with -c, -r, -e, or -o' in out + out, _err = run_cmd(base_app, "history -x " + opt) + assert "-s and -x cannot be used with -c, -r, -e, or -o" in out assert base_app.last_result is False def test_history_expanded(base_app) -> None: # validate function of -x option - cmds = ['alias create s shortcuts', 's'] + cmds = ["alias create s shortcuts", "s"] for cmd in cmds: run_cmd(base_app, cmd) - out, _err = run_cmd(base_app, 'history -x') - expected = [' 1 alias create s shortcuts', ' 2 shortcuts'] + out, _err = run_cmd(base_app, "history -x") + expected = [" 1 alias create s shortcuts", " 2 shortcuts"] assert out == expected verify_hi_last_result(base_app, 2) def test_history_script_expanded(base_app) -> None: # validate function of -s -x options together - cmds = ['alias create s shortcuts', 's'] + cmds = ["alias create s shortcuts", "s"] for cmd in cmds: run_cmd(base_app, cmd) - out, _err = run_cmd(base_app, 'history -sx') - expected = ['alias create s shortcuts', 'shortcuts'] + out, _err = run_cmd(base_app, "history -sx") + expected = ["alias create s shortcuts", "shortcuts"] assert out == expected verify_hi_last_result(base_app, 2) def test_exclude_from_history(base_app) -> None: # Run history command - run_cmd(base_app, 'history') + run_cmd(base_app, "history") verify_hi_last_result(base_app, 0) # Verify that the history is empty - out, _err = run_cmd(base_app, 'history') + out, _err = run_cmd(base_app, "history") assert out == [] verify_hi_last_result(base_app, 0) # Now run a command which isn't excluded from the history - run_cmd(base_app, 'help') + run_cmd(base_app, "help") # And verify we have a history now ... - out, _err = run_cmd(base_app, 'history') + out, _err = run_cmd(base_app, "history") expected = normalize(""" 1 help""") assert out == expected verify_hi_last_result(base_app, 1) @@ -862,7 +862,7 @@ def test_exclude_from_history(base_app) -> None: # @pytest.fixture(scope="session") def hist_file(): - fd, filename = tempfile.mkstemp(prefix='hist_file', suffix='.dat') + fd, filename = tempfile.mkstemp(prefix="hist_file", suffix=".dat") os.close(fd) yield filename # teardown code @@ -875,13 +875,13 @@ def test_history_file_is_directory(capsys) -> None: # Create a new cmd2 app cmd2.Cmd(persistent_history_file=test_dir) _, err = capsys.readouterr() - assert 'is a directory' in err + assert "is a directory" in err def test_history_can_create_directory(mocker) -> None: # Mock out atexit.register so the persistent file doesn't written when this function # exists because we will be deleting the directory it needs to go to. - mocker.patch('atexit.register') + mocker.patch("atexit.register") # Create a temp path for us to use and let it get deleted with tempfile.TemporaryDirectory() as test_dir: @@ -889,8 +889,8 @@ def test_history_can_create_directory(mocker) -> None: assert not os.path.isdir(test_dir) # Add some subdirectories for the complete history file directory - hist_file_dir = os.path.join(test_dir, 'subdir1', 'subdir2') - hist_file = os.path.join(hist_file_dir, 'hist_file') + hist_file_dir = os.path.join(test_dir, "subdir1", "subdir2") + hist_file = os.path.join(hist_file_dir, "hist_file") # Make sure cmd2 creates the history file directory cmd2.Cmd(persistent_history_file=hist_file) @@ -901,34 +901,34 @@ def test_history_can_create_directory(mocker) -> None: def test_history_cannot_create_directory(mocker, capsys) -> None: - mock_open = mocker.patch('os.makedirs') + mock_open = mocker.patch("os.makedirs") mock_open.side_effect = OSError - hist_file_path = os.path.join('fake_dir', 'file') + hist_file_path = os.path.join("fake_dir", "file") cmd2.Cmd(persistent_history_file=hist_file_path) _, err = capsys.readouterr() - assert 'Error creating persistent history file directory' in err + assert "Error creating persistent history file directory" in err def test_history_file_permission_error(mocker, capsys) -> None: - mock_open = mocker.patch('builtins.open') + mock_open = mocker.patch("builtins.open") mock_open.side_effect = PermissionError - cmd2.Cmd(persistent_history_file='/tmp/doesntmatter') + cmd2.Cmd(persistent_history_file="/tmp/doesntmatter") out, err = capsys.readouterr() assert not out - assert 'Cannot read persistent history file' in err + assert "Cannot read persistent history file" in err def test_history_file_bad_compression(mocker, capsys) -> None: - history_file = '/tmp/doesntmatter' + history_file = "/tmp/doesntmatter" with open(history_file, "wb") as f: f.write(b"THIS IS NOT COMPRESSED DATA") cmd2.Cmd(persistent_history_file=history_file) out, err = capsys.readouterr() assert not out - assert 'Error decompressing persistent history data' in err + assert "Error decompressing persistent history data" in err def test_history_file_bad_json(mocker, capsys) -> None: @@ -937,42 +937,42 @@ def test_history_file_bad_json(mocker, capsys) -> None: data = b"THIS IS NOT JSON" compressed_data = lzma.compress(data) - history_file = '/tmp/doesntmatter' + history_file = "/tmp/doesntmatter" with open(history_file, "wb") as f: f.write(compressed_data) cmd2.Cmd(persistent_history_file=history_file) out, err = capsys.readouterr() assert not out - assert 'Error processing persistent history data' in err + assert "Error processing persistent history data" in err def test_history_populates_pt(hist_file) -> None: # - create a cmd2 with persistent history app = cmd2.Cmd(persistent_history_file=hist_file) - run_cmd(app, 'help') - run_cmd(app, 'shortcuts') - run_cmd(app, 'shortcuts') - run_cmd(app, 'alias') + run_cmd(app, "help") + run_cmd(app, "shortcuts") + run_cmd(app, "shortcuts") + run_cmd(app, "alias") # call the private method which is registered to write history at exit app._persist_history() # see if history came back app = cmd2.Cmd(persistent_history_file=hist_file) assert len(app.history) == 4 - assert app.history.get(1).statement.raw == 'help' - assert app.history.get(2).statement.raw == 'shortcuts' - assert app.history.get(3).statement.raw == 'shortcuts' - assert app.history.get(4).statement.raw == 'alias' + assert app.history.get(1).statement.raw == "help" + assert app.history.get(2).statement.raw == "shortcuts" + assert app.history.get(3).statement.raw == "shortcuts" + assert app.history.get(4).statement.raw == "alias" # prompt-toolkit only adds a single entry for multiple sequential identical commands # so we check to make sure that cmd2 populated the prompt-toolkit history # using the same rules pt_history = app.main_session.history.get_strings() assert len(pt_history) == 3 - assert pt_history[0] == 'help' - assert pt_history[1] == 'shortcuts' - assert pt_history[2] == 'alias' + assert pt_history[0] == "help" + assert pt_history[1] == "shortcuts" + assert pt_history[2] == "alias" # @@ -992,10 +992,10 @@ def test_persist_history_ensure_no_error_if_no_histfile(base_app, capsys) -> Non def test_persist_history_permission_error(hist_file, mocker, capsys) -> None: app = cmd2.Cmd(persistent_history_file=hist_file) - run_cmd(app, 'help') - mock_open = mocker.patch('builtins.open') + run_cmd(app, "help") + mock_open = mocker.patch("builtins.open") mock_open.side_effect = PermissionError app._persist_history() out, err = capsys.readouterr() assert not out - assert 'Cannot write persistent history file' in err + assert "Cannot write persistent history file" in err diff --git a/tests/test_parsing.py b/tests/test_parsing.py index 3c9e388bd..2bb21ba82 100644 --- a/tests/test_parsing.py +++ b/tests/test_parsing.py @@ -20,16 +20,16 @@ @pytest.fixture def parser(): return StatementParser( - terminators=[';', '&'], - multiline_commands=['multiline'], + terminators=[";", "&"], + multiline_commands=["multiline"], aliases={ - 'helpalias': 'help', - '42': 'theanswer', - 'l': '!ls -al', - 'anothermultiline': 'multiline', - 'fake': 'run_pyscript', + "helpalias": "help", + "42": "theanswer", + "l": "!ls -al", + "anothermultiline": "multiline", + "fake": "run_pyscript", }, - shortcuts={'?': 'help', '!': 'shell'}, + shortcuts={"?": "help", "!": "shell"}, ) @@ -39,50 +39,50 @@ def default_parser(): def test_parse_empty_string(parser) -> None: - line = '' + line = "" statement = parser.parse(line) - assert statement == '' + assert statement == "" assert statement.args == statement assert statement.raw == line - assert statement.command == '' + assert statement.command == "" assert statement.arg_list == [] assert not statement.multiline_command - assert statement.terminator == '' - assert statement.suffix == '' - assert statement.redirector == '' - assert statement.redirect_to == '' + assert statement.terminator == "" + assert statement.suffix == "" + assert statement.redirector == "" + assert statement.redirect_to == "" assert statement.command_and_args == line assert statement.argv == statement.arg_list def test_parse_empty_string_default(default_parser) -> None: - line = '' + line = "" statement = default_parser.parse(line) - assert statement == '' + assert statement == "" assert statement.args == statement assert statement.raw == line - assert statement.command == '' + assert statement.command == "" assert statement.arg_list == [] assert not statement.multiline_command - assert statement.terminator == '' - assert statement.suffix == '' - assert statement.redirector == '' - assert statement.redirect_to == '' + assert statement.terminator == "" + assert statement.suffix == "" + assert statement.redirector == "" + assert statement.redirect_to == "" assert statement.command_and_args == line assert statement.argv == statement.arg_list @pytest.mark.parametrize( - ('line', 'tokens'), + ("line", "tokens"), [ - ('command', ['command']), - (constants.COMMENT_CHAR + 'comment', []), - ('not ' + constants.COMMENT_CHAR + ' a comment', ['not', constants.COMMENT_CHAR, 'a', 'comment']), - ('termbare ; > /tmp/output', ['termbare', ';', '>', '/tmp/output']), - ('termbare; > /tmp/output', ['termbare', ';', '>', '/tmp/output']), - ('termbare & > /tmp/output', ['termbare', '&', '>', '/tmp/output']), - ('termbare& > /tmp/output', ['termbare&', '>', '/tmp/output']), - ('help|less', ['help', '|', 'less']), + ("command", ["command"]), + (constants.COMMENT_CHAR + "comment", []), + ("not " + constants.COMMENT_CHAR + " a comment", ["not", constants.COMMENT_CHAR, "a", "comment"]), + ("termbare ; > /tmp/output", ["termbare", ";", ">", "/tmp/output"]), + ("termbare; > /tmp/output", ["termbare", ";", ">", "/tmp/output"]), + ("termbare & > /tmp/output", ["termbare", "&", ">", "/tmp/output"]), + ("termbare& > /tmp/output", ["termbare&", ">", "/tmp/output"]), + ("help|less", ["help", "|", "less"]), ], ) def test_tokenize_default(default_parser, line, tokens) -> None: @@ -91,19 +91,19 @@ def test_tokenize_default(default_parser, line, tokens) -> None: @pytest.mark.parametrize( - ('line', 'tokens'), + ("line", "tokens"), [ - ('command', ['command']), - ('# comment', []), - ('not ' + constants.COMMENT_CHAR + ' a comment', ['not', constants.COMMENT_CHAR, 'a', 'comment']), - ('42 arg1 arg2', ['theanswer', 'arg1', 'arg2']), - ('l', ['shell', 'ls', '-al']), - ('termbare ; > /tmp/output', ['termbare', ';', '>', '/tmp/output']), - ('termbare; > /tmp/output', ['termbare', ';', '>', '/tmp/output']), - ('termbare & > /tmp/output', ['termbare', '&', '>', '/tmp/output']), - ('termbare& > /tmp/output', ['termbare', '&', '>', '/tmp/output']), - ('help|less', ['help', '|', 'less']), - ('l|less', ['shell', 'ls', '-al', '|', 'less']), + ("command", ["command"]), + ("# comment", []), + ("not " + constants.COMMENT_CHAR + " a comment", ["not", constants.COMMENT_CHAR, "a", "comment"]), + ("42 arg1 arg2", ["theanswer", "arg1", "arg2"]), + ("l", ["shell", "ls", "-al"]), + ("termbare ; > /tmp/output", ["termbare", ";", ">", "/tmp/output"]), + ("termbare; > /tmp/output", ["termbare", ";", ">", "/tmp/output"]), + ("termbare & > /tmp/output", ["termbare", "&", ">", "/tmp/output"]), + ("termbare& > /tmp/output", ["termbare", "&", ">", "/tmp/output"]), + ("help|less", ["help", "|", "less"]), + ("l|less", ["shell", "ls", "-al", "|", "less"]), ], ) def test_tokenize(parser, line, tokens) -> None: @@ -117,8 +117,8 @@ def test_tokenize_unclosed_quotes(parser) -> None: @pytest.mark.parametrize( - ('tokens', 'command', 'args'), - [([], '', ''), (['command'], 'command', ''), (['command', 'arg1', 'arg2'], 'command', 'arg1 arg2')], + ("tokens", "command", "args"), + [([], "", ""), (["command"], "command", ""), (["command", "arg1", "arg2"], "command", "arg1 arg2")], ) def test_command_and_args(parser, tokens, command, args) -> None: (parsed_command, parsed_args) = parser._command_and_args(tokens) @@ -127,9 +127,9 @@ def test_command_and_args(parser, tokens, command, args) -> None: @pytest.mark.parametrize( - 'line', + "line", [ - 'plainword', + "plainword", '"one word"', "'one word'", ], @@ -137,354 +137,354 @@ def test_command_and_args(parser, tokens, command, args) -> None: def test_parse_single_word(parser, line) -> None: statement = parser.parse(line) assert statement.command == line - assert statement == '' + assert statement == "" assert statement.args == statement assert statement.argv == [su.strip_quotes(line)] assert not statement.arg_list assert statement.raw == line assert not statement.multiline_command - assert statement.terminator == '' - assert statement.suffix == '' - assert statement.redirector == '' - assert statement.redirect_to == '' + assert statement.terminator == "" + assert statement.suffix == "" + assert statement.redirector == "" + assert statement.redirect_to == "" assert statement.command_and_args == line @pytest.mark.parametrize( - ('line', 'terminator'), + ("line", "terminator"), [ - ('termbare;', ';'), - ('termbare ;', ';'), - ('termbare&', '&'), - ('termbare &', '&'), + ("termbare;", ";"), + ("termbare ;", ";"), + ("termbare&", "&"), + ("termbare &", "&"), ], ) def test_parse_word_plus_terminator(parser, line, terminator) -> None: statement = parser.parse(line) - assert statement.command == 'termbare' - assert statement == '' - assert statement.argv == ['termbare'] + assert statement.command == "termbare" + assert statement == "" + assert statement.argv == ["termbare"] assert not statement.arg_list assert statement.terminator == terminator assert statement.expanded_command_line == statement.command + statement.terminator @pytest.mark.parametrize( - ('line', 'terminator'), + ("line", "terminator"), [ - ('termbare; suffx', ';'), - ('termbare ;suffx', ';'), - ('termbare& suffx', '&'), - ('termbare &suffx', '&'), + ("termbare; suffx", ";"), + ("termbare ;suffx", ";"), + ("termbare& suffx", "&"), + ("termbare &suffx", "&"), ], ) def test_parse_suffix_after_terminator(parser, line, terminator) -> None: statement = parser.parse(line) - assert statement.command == 'termbare' - assert statement == '' + assert statement.command == "termbare" + assert statement == "" assert statement.args == statement - assert statement.argv == ['termbare'] + assert statement.argv == ["termbare"] assert not statement.arg_list assert statement.terminator == terminator - assert statement.suffix == 'suffx' - assert statement.expanded_command_line == statement.command + statement.terminator + ' ' + statement.suffix + assert statement.suffix == "suffx" + assert statement.expanded_command_line == statement.command + statement.terminator + " " + statement.suffix def test_parse_command_with_args(parser) -> None: - line = 'command with args' + line = "command with args" statement = parser.parse(line) - assert statement.command == 'command' - assert statement == 'with args' + assert statement.command == "command" + assert statement == "with args" assert statement.args == statement - assert statement.argv == ['command', 'with', 'args'] + assert statement.argv == ["command", "with", "args"] assert statement.arg_list == statement.argv[1:] def test_parse_command_with_quoted_args(parser) -> None: line = 'command with "quoted args" and "some not"' statement = parser.parse(line) - assert statement.command == 'command' + assert statement.command == "command" assert statement == 'with "quoted args" and "some not"' assert statement.args == statement - assert statement.argv == ['command', 'with', 'quoted args', 'and', 'some not'] - assert statement.arg_list == ['with', '"quoted args"', 'and', '"some not"'] + assert statement.argv == ["command", "with", "quoted args", "and", "some not"] + assert statement.arg_list == ["with", '"quoted args"', "and", '"some not"'] def test_parse_command_with_args_terminator_and_suffix(parser) -> None: - line = 'command with args and terminator; and suffix' + line = "command with args and terminator; and suffix" statement = parser.parse(line) - assert statement.command == 'command' + assert statement.command == "command" assert statement == "with args and terminator" assert statement.args == statement - assert statement.argv == ['command', 'with', 'args', 'and', 'terminator'] + assert statement.argv == ["command", "with", "args", "and", "terminator"] assert statement.arg_list == statement.argv[1:] - assert statement.terminator == ';' - assert statement.suffix == 'and suffix' + assert statement.terminator == ";" + assert statement.suffix == "and suffix" def test_parse_comment(parser) -> None: - statement = parser.parse(constants.COMMENT_CHAR + ' this is all a comment') - assert statement.command == '' - assert statement == '' + statement = parser.parse(constants.COMMENT_CHAR + " this is all a comment") + assert statement.command == "" + assert statement == "" assert statement.args == statement assert not statement.argv assert not statement.arg_list def test_parse_embedded_comment_char(parser) -> None: - command_str = 'hi ' + constants.COMMENT_CHAR + ' not a comment' + command_str = "hi " + constants.COMMENT_CHAR + " not a comment" statement = parser.parse(command_str) - assert statement == constants.COMMENT_CHAR + ' not a comment' + assert statement == constants.COMMENT_CHAR + " not a comment" assert statement.args == statement - assert statement.command == 'hi' + assert statement.command == "hi" assert statement.argv == shlex_split(command_str) assert statement.arg_list == statement.argv[1:] @pytest.mark.parametrize( - 'line', + "line", [ - 'simple | piped', - 'simple|piped', + "simple | piped", + "simple|piped", ], ) def test_parse_simple_pipe(parser, line) -> None: statement = parser.parse(line) - assert statement.command == 'simple' - assert statement == '' + assert statement.command == "simple" + assert statement == "" assert statement.args == statement - assert statement.argv == ['simple'] + assert statement.argv == ["simple"] assert not statement.arg_list assert statement.redirector == constants.REDIRECTION_PIPE - assert statement.redirect_to == 'piped' - assert statement.expanded_command_line == statement.command + ' | ' + statement.redirect_to + assert statement.redirect_to == "piped" + assert statement.expanded_command_line == statement.command + " | " + statement.redirect_to def test_parse_double_pipe_is_not_a_pipe(parser) -> None: - line = 'double-pipe || is not a pipe' + line = "double-pipe || is not a pipe" statement = parser.parse(line) - assert statement.command == 'double-pipe' - assert statement == '|| is not a pipe' + assert statement.command == "double-pipe" + assert statement == "|| is not a pipe" assert statement.args == statement - assert statement.argv == ['double-pipe', '||', 'is', 'not', 'a', 'pipe'] + assert statement.argv == ["double-pipe", "||", "is", "not", "a", "pipe"] assert statement.arg_list == statement.argv[1:] assert not statement.redirector assert not statement.redirect_to def test_parse_complex_pipe(parser) -> None: - line = 'command with args, terminator&sufx | piped' + line = "command with args, terminator&sufx | piped" statement = parser.parse(line) - assert statement.command == 'command' + assert statement.command == "command" assert statement == "with args, terminator" assert statement.args == statement - assert statement.argv == ['command', 'with', 'args,', 'terminator'] + assert statement.argv == ["command", "with", "args,", "terminator"] assert statement.arg_list == statement.argv[1:] - assert statement.terminator == '&' - assert statement.suffix == 'sufx' + assert statement.terminator == "&" + assert statement.suffix == "sufx" assert statement.redirector == constants.REDIRECTION_PIPE - assert statement.redirect_to == 'piped' + assert statement.redirect_to == "piped" @pytest.mark.parametrize( - ('line', 'redirector'), + ("line", "redirector"), [ - ('help > out.txt', '>'), - ('help>out.txt', '>'), - ('help >> out.txt', '>>'), - ('help>>out.txt', '>>'), + ("help > out.txt", ">"), + ("help>out.txt", ">"), + ("help >> out.txt", ">>"), + ("help>>out.txt", ">>"), ], ) def test_parse_redirect(parser, line, redirector) -> None: statement = parser.parse(line) - assert statement.command == 'help' - assert statement == '' + assert statement.command == "help" + assert statement == "" assert statement.args == statement assert statement.redirector == redirector - assert statement.redirect_to == 'out.txt' - assert statement.expanded_command_line == statement.command + ' ' + statement.redirector + ' ' + statement.redirect_to + assert statement.redirect_to == "out.txt" + assert statement.expanded_command_line == statement.command + " " + statement.redirector + " " + statement.redirect_to @pytest.mark.parametrize( - 'dest', + "dest", [ - 'afile.txt', - 'python-cmd2/afile.txt', + "afile.txt", + "python-cmd2/afile.txt", ], ) # without dashes # with dashes in path def test_parse_redirect_with_args(parser, dest) -> None: - line = f'output into > {dest}' + line = f"output into > {dest}" statement = parser.parse(line) - assert statement.command == 'output' - assert statement == 'into' + assert statement.command == "output" + assert statement == "into" assert statement.args == statement - assert statement.argv == ['output', 'into'] + assert statement.argv == ["output", "into"] assert statement.arg_list == statement.argv[1:] - assert statement.redirector == '>' + assert statement.redirector == ">" assert statement.redirect_to == dest def test_parse_redirect_append(parser) -> None: - line = 'output appended to >> /tmp/afile.txt' + line = "output appended to >> /tmp/afile.txt" statement = parser.parse(line) - assert statement.command == 'output' - assert statement == 'appended to' + assert statement.command == "output" + assert statement == "appended to" assert statement.args == statement - assert statement.argv == ['output', 'appended', 'to'] + assert statement.argv == ["output", "appended", "to"] assert statement.arg_list == statement.argv[1:] - assert statement.redirector == '>>' - assert statement.redirect_to == '/tmp/afile.txt' + assert statement.redirector == ">>" + assert statement.redirect_to == "/tmp/afile.txt" def test_parse_pipe_then_redirect(parser) -> None: - line = 'output into;sufx | pipethrume plz > afile.txt' + line = "output into;sufx | pipethrume plz > afile.txt" statement = parser.parse(line) - assert statement.command == 'output' - assert statement == 'into' + assert statement.command == "output" + assert statement == "into" assert statement.args == statement - assert statement.argv == ['output', 'into'] + assert statement.argv == ["output", "into"] assert statement.arg_list == statement.argv[1:] - assert statement.terminator == ';' - assert statement.suffix == 'sufx' + assert statement.terminator == ";" + assert statement.suffix == "sufx" assert statement.redirector == constants.REDIRECTION_PIPE - assert statement.redirect_to == 'pipethrume plz > afile.txt' + assert statement.redirect_to == "pipethrume plz > afile.txt" def test_parse_multiple_pipes(parser) -> None: - line = 'output into;sufx | pipethrume plz | grep blah' + line = "output into;sufx | pipethrume plz | grep blah" statement = parser.parse(line) - assert statement.command == 'output' - assert statement == 'into' + assert statement.command == "output" + assert statement == "into" assert statement.args == statement - assert statement.argv == ['output', 'into'] + assert statement.argv == ["output", "into"] assert statement.arg_list == statement.argv[1:] - assert statement.terminator == ';' - assert statement.suffix == 'sufx' + assert statement.terminator == ";" + assert statement.suffix == "sufx" assert statement.redirector == constants.REDIRECTION_PIPE - assert statement.redirect_to == 'pipethrume plz | grep blah' + assert statement.redirect_to == "pipethrume plz | grep blah" def test_redirect_then_pipe(parser) -> None: - line = 'help alias > file.txt | grep blah' + line = "help alias > file.txt | grep blah" statement = parser.parse(line) - assert statement.command == 'help' - assert statement == 'alias' + assert statement.command == "help" + assert statement == "alias" assert statement.args == statement - assert statement.argv == ['help', 'alias'] + assert statement.argv == ["help", "alias"] assert statement.arg_list == statement.argv[1:] - assert statement.terminator == '' - assert statement.suffix == '' - assert statement.redirector == '>' - assert statement.redirect_to == 'file.txt' + assert statement.terminator == "" + assert statement.suffix == "" + assert statement.redirector == ">" + assert statement.redirect_to == "file.txt" def test_append_then_pipe(parser) -> None: - line = 'help alias >> file.txt | grep blah' + line = "help alias >> file.txt | grep blah" statement = parser.parse(line) - assert statement.command == 'help' - assert statement == 'alias' + assert statement.command == "help" + assert statement == "alias" assert statement.args == statement - assert statement.argv == ['help', 'alias'] + assert statement.argv == ["help", "alias"] assert statement.arg_list == statement.argv[1:] - assert statement.terminator == '' - assert statement.suffix == '' - assert statement.redirector == '>>' - assert statement.redirect_to == 'file.txt' + assert statement.terminator == "" + assert statement.suffix == "" + assert statement.redirector == ">>" + assert statement.redirect_to == "file.txt" def test_append_then_redirect(parser) -> None: - line = 'help alias >> file.txt > file2.txt' + line = "help alias >> file.txt > file2.txt" statement = parser.parse(line) - assert statement.command == 'help' - assert statement == 'alias' + assert statement.command == "help" + assert statement == "alias" assert statement.args == statement - assert statement.argv == ['help', 'alias'] + assert statement.argv == ["help", "alias"] assert statement.arg_list == statement.argv[1:] - assert statement.terminator == '' - assert statement.suffix == '' - assert statement.redirector == '>>' - assert statement.redirect_to == 'file.txt' + assert statement.terminator == "" + assert statement.suffix == "" + assert statement.redirector == ">>" + assert statement.redirect_to == "file.txt" def test_redirect_then_append(parser) -> None: - line = 'help alias > file.txt >> file2.txt' + line = "help alias > file.txt >> file2.txt" statement = parser.parse(line) - assert statement.command == 'help' - assert statement == 'alias' + assert statement.command == "help" + assert statement == "alias" assert statement.args == statement - assert statement.argv == ['help', 'alias'] + assert statement.argv == ["help", "alias"] assert statement.arg_list == statement.argv[1:] - assert statement.terminator == '' - assert statement.suffix == '' - assert statement.redirector == '>' - assert statement.redirect_to == 'file.txt' + assert statement.terminator == "" + assert statement.suffix == "" + assert statement.redirector == ">" + assert statement.redirect_to == "file.txt" def test_redirect_to_quoted_string(parser) -> None: line = 'help alias > "file.txt"' statement = parser.parse(line) - assert statement.command == 'help' - assert statement == 'alias' + assert statement.command == "help" + assert statement == "alias" assert statement.args == statement - assert statement.argv == ['help', 'alias'] + assert statement.argv == ["help", "alias"] assert statement.arg_list == statement.argv[1:] - assert statement.terminator == '' - assert statement.suffix == '' - assert statement.redirector == '>' + assert statement.terminator == "" + assert statement.suffix == "" + assert statement.redirector == ">" assert statement.redirect_to == '"file.txt"' def test_redirect_to_single_quoted_string(parser) -> None: line = "help alias > 'file.txt'" statement = parser.parse(line) - assert statement.command == 'help' - assert statement == 'alias' + assert statement.command == "help" + assert statement == "alias" assert statement.args == statement - assert statement.argv == ['help', 'alias'] + assert statement.argv == ["help", "alias"] assert statement.arg_list == statement.argv[1:] - assert statement.terminator == '' - assert statement.suffix == '' - assert statement.redirector == '>' + assert statement.terminator == "" + assert statement.suffix == "" + assert statement.redirector == ">" assert statement.redirect_to == "'file.txt'" def test_redirect_to_empty_quoted_string(parser) -> None: line = 'help alias > ""' statement = parser.parse(line) - assert statement.command == 'help' - assert statement == 'alias' + assert statement.command == "help" + assert statement == "alias" assert statement.args == statement - assert statement.argv == ['help', 'alias'] + assert statement.argv == ["help", "alias"] assert statement.arg_list == statement.argv[1:] - assert statement.terminator == '' - assert statement.suffix == '' - assert statement.redirector == '>' - assert statement.redirect_to == '' + assert statement.terminator == "" + assert statement.suffix == "" + assert statement.redirector == ">" + assert statement.redirect_to == "" def test_redirect_to_empty_single_quoted_string(parser) -> None: line = "help alias > ''" statement = parser.parse(line) - assert statement.command == 'help' - assert statement == 'alias' + assert statement.command == "help" + assert statement == "alias" assert statement.args == statement - assert statement.argv == ['help', 'alias'] + assert statement.argv == ["help", "alias"] assert statement.arg_list == statement.argv[1:] - assert statement.terminator == '' - assert statement.suffix == '' - assert statement.redirector == '>' - assert statement.redirect_to == '' + assert statement.terminator == "" + assert statement.suffix == "" + assert statement.redirector == ">" + assert statement.redirect_to == "" def test_parse_redirect_to_paste_buffer(parser) -> None: - line = 'redirect to paste buffer >> ' + line = "redirect to paste buffer >> " statement = parser.parse(line) - assert statement.command == 'redirect' - assert statement == 'to paste buffer' + assert statement.command == "redirect" + assert statement == "to paste buffer" assert statement.args == statement - assert statement.argv == ['redirect', 'to', 'paste', 'buffer'] + assert statement.argv == ["redirect", "to", "paste", "buffer"] assert statement.arg_list == statement.argv[1:] - assert statement.redirector == '>>' + assert statement.redirector == ">>" def test_parse_redirect_inside_terminator(parser) -> None: @@ -492,115 +492,115 @@ def test_parse_redirect_inside_terminator(parser) -> None: If a redirector occurs before a terminator, then it will be treated as part of the arguments and not as a redirector. """ - line = 'has > inside;' + line = "has > inside;" statement = parser.parse(line) - assert statement.command == 'has' - assert statement == '> inside' + assert statement.command == "has" + assert statement == "> inside" assert statement.args == statement - assert statement.argv == ['has', '>', 'inside'] + assert statement.argv == ["has", ">", "inside"] assert statement.arg_list == statement.argv[1:] - assert statement.terminator == ';' + assert statement.terminator == ";" @pytest.mark.parametrize( - ('line', 'terminator'), + ("line", "terminator"), [ - ('multiline with | inside;', ';'), - ('multiline with | inside ;', ';'), - ('multiline with | inside;;;', ';'), - ('multiline with | inside;; ;;', ';'), - ('multiline with | inside&', '&'), - ('multiline with | inside &;', '&'), - ('multiline with | inside&&;', '&'), - ('multiline with | inside &; &;', '&'), + ("multiline with | inside;", ";"), + ("multiline with | inside ;", ";"), + ("multiline with | inside;;;", ";"), + ("multiline with | inside;; ;;", ";"), + ("multiline with | inside&", "&"), + ("multiline with | inside &;", "&"), + ("multiline with | inside&&;", "&"), + ("multiline with | inside &; &;", "&"), ], ) def test_parse_multiple_terminators(parser, line, terminator) -> None: statement = parser.parse(line) assert statement.multiline_command - assert statement == 'with | inside' + assert statement == "with | inside" assert statement.args == statement - assert statement.argv == ['multiline', 'with', '|', 'inside'] + assert statement.argv == ["multiline", "with", "|", "inside"] assert statement.arg_list == statement.argv[1:] assert statement.terminator == terminator def test_parse_unfinished_multiliine_command(parser) -> None: - line = 'multiline has > inside an unfinished command' + line = "multiline has > inside an unfinished command" statement = parser.parse(line) assert statement.multiline_command - assert statement.command == 'multiline' - assert statement == 'has > inside an unfinished command' + assert statement.command == "multiline" + assert statement == "has > inside an unfinished command" assert statement.args == statement - assert statement.argv == ['multiline', 'has', '>', 'inside', 'an', 'unfinished', 'command'] + assert statement.argv == ["multiline", "has", ">", "inside", "an", "unfinished", "command"] assert statement.arg_list == statement.argv[1:] - assert statement.terminator == '' + assert statement.terminator == "" def test_parse_basic_multiline_command(parser) -> None: - line = 'multiline foo\nbar\n\n' + line = "multiline foo\nbar\n\n" statement = parser.parse(line) assert statement.multiline_command - assert statement.command == 'multiline' - assert statement == 'foo bar' + assert statement.command == "multiline" + assert statement == "foo bar" assert statement.args == statement - assert statement.argv == ['multiline', 'foo', 'bar'] - assert statement.arg_list == ['foo', 'bar'] + assert statement.argv == ["multiline", "foo", "bar"] + assert statement.arg_list == ["foo", "bar"] assert statement.raw == line - assert statement.terminator == '\n' + assert statement.terminator == "\n" @pytest.mark.parametrize( - ('line', 'terminator'), + ("line", "terminator"), [ - ('multiline has > inside;', ';'), - ('multiline has > inside;;;', ';'), - ('multiline has > inside;; ;;', ';'), - ('multiline has > inside &', '&'), - ('multiline has > inside & &', '&'), + ("multiline has > inside;", ";"), + ("multiline has > inside;;;", ";"), + ("multiline has > inside;; ;;", ";"), + ("multiline has > inside &", "&"), + ("multiline has > inside & &", "&"), ], ) def test_parse_multiline_command_ignores_redirectors_within_it(parser, line, terminator) -> None: statement = parser.parse(line) assert statement.multiline_command - assert statement == 'has > inside' + assert statement == "has > inside" assert statement.args == statement - assert statement.argv == ['multiline', 'has', '>', 'inside'] + assert statement.argv == ["multiline", "has", ">", "inside"] assert statement.arg_list == statement.argv[1:] assert statement.terminator == terminator def test_parse_multiline_terminated_by_empty_line(parser) -> None: - line = 'multiline command ends\n\n' + line = "multiline command ends\n\n" statement = parser.parse(line) assert statement.multiline_command - assert statement.command == 'multiline' - assert statement == 'command ends' + assert statement.command == "multiline" + assert statement == "command ends" assert statement.args == statement - assert statement.argv == ['multiline', 'command', 'ends'] + assert statement.argv == ["multiline", "command", "ends"] assert statement.arg_list == statement.argv[1:] - assert statement.terminator == '\n' + assert statement.terminator == "\n" @pytest.mark.parametrize( - ('line', 'terminator'), + ("line", "terminator"), [ - ('multiline command "with\nembedded newline";', ';'), - ('multiline command "with\nembedded newline";;;', ';'), - ('multiline command "with\nembedded newline";; ;;', ';'), - ('multiline command "with\nembedded newline" &', '&'), - ('multiline command "with\nembedded newline" & &', '&'), - ('multiline command "with\nembedded newline"\n\n', '\n'), + ('multiline command "with\nembedded newline";', ";"), + ('multiline command "with\nembedded newline";;;', ";"), + ('multiline command "with\nembedded newline";; ;;', ";"), + ('multiline command "with\nembedded newline" &', "&"), + ('multiline command "with\nembedded newline" & &', "&"), + ('multiline command "with\nembedded newline"\n\n', "\n"), ], ) def test_parse_multiline_with_embedded_newline(parser, line, terminator) -> None: statement = parser.parse(line) assert statement.multiline_command - assert statement.command == 'multiline' + assert statement.command == "multiline" assert statement == 'command "with\nembedded newline"' assert statement.args == statement - assert statement.argv == ['multiline', 'command', 'with\nembedded newline'] - assert statement.arg_list == ['command', '"with\nembedded newline"'] + assert statement.argv == ["multiline", "command", "with\nembedded newline"] + assert statement.arg_list == ["command", '"with\nembedded newline"'] assert statement.terminator == terminator @@ -608,44 +608,44 @@ def test_parse_multiline_ignores_terminators_in_quotes(parser) -> None: line = 'multiline command "with term; ends" now\n\n' statement = parser.parse(line) assert statement.multiline_command - assert statement.command == 'multiline' + assert statement.command == "multiline" assert statement == 'command "with term; ends" now' assert statement.args == statement - assert statement.argv == ['multiline', 'command', 'with term; ends', 'now'] - assert statement.arg_list == ['command', '"with term; ends"', 'now'] - assert statement.terminator == '\n' + assert statement.argv == ["multiline", "command", "with term; ends", "now"] + assert statement.arg_list == ["command", '"with term; ends"', "now"] + assert statement.terminator == "\n" def test_parse_command_with_unicode_args(parser) -> None: - line = 'drink café' + line = "drink café" statement = parser.parse(line) - assert statement.command == 'drink' - assert statement == 'café' + assert statement.command == "drink" + assert statement == "café" assert statement.args == statement - assert statement.argv == ['drink', 'café'] + assert statement.argv == ["drink", "café"] assert statement.arg_list == statement.argv[1:] def test_parse_unicode_command(parser) -> None: - line = 'café au lait' + line = "café au lait" statement = parser.parse(line) - assert statement.command == 'café' - assert statement == 'au lait' + assert statement.command == "café" + assert statement == "au lait" assert statement.args == statement - assert statement.argv == ['café', 'au', 'lait'] + assert statement.argv == ["café", "au", "lait"] assert statement.arg_list == statement.argv[1:] def test_parse_redirect_to_unicode_filename(parser) -> None: - line = 'dir home > café' + line = "dir home > café" statement = parser.parse(line) - assert statement.command == 'dir' - assert statement == 'home' + assert statement.command == "dir" + assert statement == "home" assert statement.args == statement - assert statement.argv == ['dir', 'home'] + assert statement.argv == ["dir", "home"] assert statement.arg_list == statement.argv[1:] - assert statement.redirector == '>' - assert statement.redirect_to == 'café' + assert statement.redirector == ">" + assert statement.redirect_to == "café" def test_parse_unclosed_quotes(parser) -> None: @@ -656,22 +656,22 @@ def test_parse_unclosed_quotes(parser) -> None: def test_empty_statement_raises_exception() -> None: app = cmd2.Cmd() with pytest.raises(exceptions.EmptyStatement): - app._complete_statement('') + app._complete_statement("") with pytest.raises(exceptions.EmptyStatement): - app._complete_statement(' ') + app._complete_statement(" ") @pytest.mark.parametrize( - ('line', 'command', 'args'), + ("line", "command", "args"), [ - ('helpalias', 'help', ''), - ('helpalias mycommand', 'help', 'mycommand'), - ('42', 'theanswer', ''), - ('42 arg1 arg2', 'theanswer', 'arg1 arg2'), - ('!ls', 'shell', 'ls'), - ('!ls -al /tmp', 'shell', 'ls -al /tmp'), - ('l', 'shell', 'ls -al'), + ("helpalias", "help", ""), + ("helpalias mycommand", "help", "mycommand"), + ("42", "theanswer", ""), + ("42 arg1 arg2", "theanswer", "arg1 arg2"), + ("!ls", "shell", "ls"), + ("!ls -al /tmp", "shell", "ls -al /tmp"), + ("l", "shell", "ls -al"), ], ) def test_parse_alias_and_shortcut_expansion(parser, line, command, args) -> None: @@ -682,83 +682,83 @@ def test_parse_alias_and_shortcut_expansion(parser, line, command, args) -> None def test_parse_alias_on_multiline_command(parser) -> None: - line = 'anothermultiline has > inside an unfinished command' + line = "anothermultiline has > inside an unfinished command" statement = parser.parse(line) - assert statement == 'has > inside an unfinished command' + assert statement == "has > inside an unfinished command" assert statement.args == statement assert statement.multiline_command - assert statement.command == 'multiline' - assert statement.terminator == '' + assert statement.command == "multiline" + assert statement.terminator == "" @pytest.mark.parametrize( - ('line', 'redirector'), + ("line", "redirector"), [ - ('helpalias > out.txt', '>'), - ('helpalias>out.txt', '>'), - ('helpalias >> out.txt', '>>'), - ('helpalias>>out.txt', '>>'), + ("helpalias > out.txt", ">"), + ("helpalias>out.txt", ">"), + ("helpalias >> out.txt", ">>"), + ("helpalias>>out.txt", ">>"), ], ) def test_parse_alias_redirection(parser, line, redirector) -> None: statement = parser.parse(line) - assert statement.command == 'help' - assert statement == '' + assert statement.command == "help" + assert statement == "" assert statement.args == statement assert statement.redirector == redirector - assert statement.redirect_to == 'out.txt' + assert statement.redirect_to == "out.txt" @pytest.mark.parametrize( - 'line', + "line", [ - 'helpalias | less', - 'helpalias|less', + "helpalias | less", + "helpalias|less", ], ) def test_parse_alias_pipe(parser, line) -> None: statement = parser.parse(line) - assert statement.command == 'help' - assert statement == '' + assert statement.command == "help" + assert statement == "" assert statement.args == statement assert statement.redirector == constants.REDIRECTION_PIPE - assert statement.redirect_to == 'less' + assert statement.redirect_to == "less" @pytest.mark.parametrize( - 'line', + "line", [ - 'helpalias;', - 'helpalias;;', - 'helpalias;; ;', - 'helpalias ;', - 'helpalias ; ;', - 'helpalias ;; ;', + "helpalias;", + "helpalias;;", + "helpalias;; ;", + "helpalias ;", + "helpalias ; ;", + "helpalias ;; ;", ], ) def test_parse_alias_terminator_no_whitespace(parser, line) -> None: statement = parser.parse(line) - assert statement.command == 'help' - assert statement == '' + assert statement.command == "help" + assert statement == "" assert statement.args == statement - assert statement.terminator == ';' + assert statement.terminator == ";" def test_parse_command_only_command_and_args(parser) -> None: - line = 'help history' + line = "help history" partial_statement = parser.parse_command_only(line) - assert partial_statement.command == 'help' - assert partial_statement.args == 'history' + assert partial_statement.command == "help" + assert partial_statement.args == "history" assert partial_statement.raw == line assert not partial_statement.multiline_command assert partial_statement.command_and_args == line def test_parse_command_only_strips_line(parser) -> None: - line = ' help history ' + line = " help history " partial_statement = parser.parse_command_only(line) - assert partial_statement.command == 'help' - assert partial_statement.args == 'history' + assert partial_statement.command == "help" + assert partial_statement.args == "history" assert partial_statement.raw == line assert not partial_statement.multiline_command assert partial_statement.command_and_args == line.strip() @@ -767,7 +767,7 @@ def test_parse_command_only_strips_line(parser) -> None: def test_parse_command_only_expands_alias(parser) -> None: line = 'fake foobar.py "somebody.py' partial_statement = parser.parse_command_only(line) - assert partial_statement.command == 'run_pyscript' + assert partial_statement.command == "run_pyscript" assert partial_statement.args == 'foobar.py "somebody.py' assert partial_statement.raw == line assert not partial_statement.multiline_command @@ -775,30 +775,30 @@ def test_parse_command_only_expands_alias(parser) -> None: def test_parse_command_only_expands_shortcuts(parser) -> None: - line = '!cat foobar.txt' + line = "!cat foobar.txt" partial_statement = parser.parse_command_only(line) - assert partial_statement.command == 'shell' - assert partial_statement.args == 'cat foobar.txt' + assert partial_statement.command == "shell" + assert partial_statement.args == "cat foobar.txt" assert partial_statement.raw == line assert not partial_statement.multiline_command - assert partial_statement.command_and_args == 'shell cat foobar.txt' + assert partial_statement.command_and_args == "shell cat foobar.txt" def test_parse_command_only_quoted_args(parser) -> None: line = 'l "/tmp/directory with spaces/doit.sh"' partial_statement = parser.parse_command_only(line) - assert partial_statement.command == 'shell' + assert partial_statement.command == "shell" assert partial_statement.args == 'ls -al "/tmp/directory with spaces/doit.sh"' assert partial_statement.raw == line assert not partial_statement.multiline_command - assert partial_statement.command_and_args == line.replace('l', 'shell ls -al') + assert partial_statement.command_and_args == line.replace("l", "shell ls -al") def test_parse_command_only_unclosed_quote(parser) -> None: # Quoted trailing spaces will be preserved line = 'command with unclosed "quote ' partial_statement = parser.parse_command_only(line) - assert partial_statement.command == 'command' + assert partial_statement.command == "command" assert partial_statement.args == 'with unclosed "quote ' assert partial_statement.raw == line assert not partial_statement.multiline_command @@ -806,56 +806,56 @@ def test_parse_command_only_unclosed_quote(parser) -> None: @pytest.mark.parametrize( - ('line', 'args'), + ("line", "args"), [ - ('helpalias > out.txt', '> out.txt'), - ('helpalias>out.txt', '>out.txt'), - ('helpalias >> out.txt', '>> out.txt'), - ('helpalias>>out.txt', '>>out.txt'), - ('help|less', '|less'), - ('helpalias;', ';'), - ('help ;;', ';;'), - ('help; ;;', '; ;;'), + ("helpalias > out.txt", "> out.txt"), + ("helpalias>out.txt", ">out.txt"), + ("helpalias >> out.txt", ">> out.txt"), + ("helpalias>>out.txt", ">>out.txt"), + ("help|less", "|less"), + ("helpalias;", ";"), + ("help ;;", ";;"), + ("help; ;;", "; ;;"), ], ) def test_parse_command_only_specialchars(parser, line, args) -> None: partial_statement = parser.parse_command_only(line) - assert partial_statement.command == 'help' + assert partial_statement.command == "help" assert partial_statement.args == args assert partial_statement.raw == line assert not partial_statement.multiline_command - assert partial_statement.command_and_args == 'help' + ' ' + args + assert partial_statement.command_and_args == "help" + " " + args @pytest.mark.parametrize( - 'line', + "line", [ - '', - ';', - ';;', - ';; ;', - '&', - '& &', - ' && &', - '>', + "", + ";", + ";;", + ";; ;", + "&", + "& &", + " && &", + ">", "'", '"', - '|', + "|", ], ) def test_parse_command_only_empty(parser, line) -> None: partial_statement = parser.parse_command_only(line) - assert partial_statement.command == '' - assert partial_statement.args == '' + assert partial_statement.command == "" + assert partial_statement.args == "" assert partial_statement.raw == line assert not partial_statement.multiline_command - assert partial_statement.command_and_args == '' + assert partial_statement.command_and_args == "" def test_parse_command_only_multiline(parser) -> None: line = 'multiline with partially "open quotes and no terminator' partial_statement = parser.parse_command_only(line) - assert partial_statement.command == 'multiline' + assert partial_statement.command == "multiline" assert partial_statement.args == 'with partially "open quotes and no terminator' assert partial_statement.raw == line assert partial_statement.multiline_command @@ -863,33 +863,33 @@ def test_parse_command_only_multiline(parser) -> None: def test_statement_initialization() -> None: - string = 'alias' + string = "alias" statement = cmd2.Statement(string) assert statement == string assert statement.args == statement - assert statement.raw == '' - assert statement.command == '' + assert statement.raw == "" + assert statement.command == "" assert isinstance(statement.arg_list, list) - assert statement.arg_list == ['alias'] + assert statement.arg_list == ["alias"] assert isinstance(statement.argv, list) assert not statement.argv assert not statement.multiline_command - assert statement.terminator == '' - assert statement.suffix == '' - assert statement.redirector == '' - assert statement.redirect_to == '' + assert statement.terminator == "" + assert statement.suffix == "" + assert statement.redirector == "" + assert statement.redirect_to == "" def test_statement_is_immutable() -> None: - string = 'foo' + string = "foo" statement = cmd2.Statement(string) assert statement == string assert statement.args == statement - assert statement.raw == '' + assert statement.raw == "" with pytest.raises(dataclasses.FrozenInstanceError): - statement.args = 'bar' + statement.args = "bar" with pytest.raises(dataclasses.FrozenInstanceError): - statement.raw = 'baz' + statement.raw = "baz" def test_statement_as_dict(parser) -> None: @@ -916,57 +916,57 @@ def test_is_valid_command_invalid(mocker, parser) -> None: # Non-string command valid, errmsg = parser.is_valid_command(5) assert not valid - assert 'must be a string' in errmsg + assert "must be a string" in errmsg mock = mocker.MagicMock() valid, errmsg = parser.is_valid_command(mock) assert not valid - assert 'must be a string' in errmsg + assert "must be a string" in errmsg # Empty command - valid, errmsg = parser.is_valid_command('') + valid, errmsg = parser.is_valid_command("") assert not valid - assert 'cannot be an empty string' in errmsg + assert "cannot be an empty string" in errmsg # Start with the comment character valid, errmsg = parser.is_valid_command(constants.COMMENT_CHAR) assert not valid - assert 'cannot start with the comment character' in errmsg + assert "cannot start with the comment character" in errmsg # Starts with shortcut - valid, errmsg = parser.is_valid_command('!ls') + valid, errmsg = parser.is_valid_command("!ls") assert not valid - assert 'cannot start with a shortcut' in errmsg + assert "cannot start with a shortcut" in errmsg # Contains whitespace - valid, errmsg = parser.is_valid_command('shell ls') + valid, errmsg = parser.is_valid_command("shell ls") assert not valid - assert 'cannot contain: whitespace, quotes,' in errmsg + assert "cannot contain: whitespace, quotes," in errmsg # Contains a quote valid, errmsg = parser.is_valid_command('"shell"') assert not valid - assert 'cannot contain: whitespace, quotes,' in errmsg + assert "cannot contain: whitespace, quotes," in errmsg # Contains a redirector - valid, errmsg = parser.is_valid_command('>shell') + valid, errmsg = parser.is_valid_command(">shell") assert not valid - assert 'cannot contain: whitespace, quotes,' in errmsg + assert "cannot contain: whitespace, quotes," in errmsg # Contains a terminator - valid, errmsg = parser.is_valid_command(';shell') + valid, errmsg = parser.is_valid_command(";shell") assert not valid - assert 'cannot contain: whitespace, quotes,' in errmsg + assert "cannot contain: whitespace, quotes," in errmsg def test_is_valid_command_valid(parser) -> None: # Valid command - valid, errmsg = parser.is_valid_command('shell') + valid, errmsg = parser.is_valid_command("shell") assert valid assert not errmsg # Subcommands can start with shortcut - valid, errmsg = parser.is_valid_command('!subcmd', is_subcommand=True) + valid, errmsg = parser.is_valid_command("!subcmd", is_subcommand=True) assert valid assert not errmsg @@ -980,48 +980,48 @@ def test_macro_normal_arg_pattern() -> None: pattern = MacroArg.macro_normal_arg_pattern # Valid strings - matches = pattern.findall('{5}') - assert matches == ['{5}'] + matches = pattern.findall("{5}") + assert matches == ["{5}"] - matches = pattern.findall('{233}') - assert matches == ['{233}'] + matches = pattern.findall("{233}") + assert matches == ["{233}"] - matches = pattern.findall('{{{{{4}') - assert matches == ['{4}'] + matches = pattern.findall("{{{{{4}") + assert matches == ["{4}"] - matches = pattern.findall('{2}}}}}') - assert matches == ['{2}'] + matches = pattern.findall("{2}}}}}") + assert matches == ["{2}"] - matches = pattern.findall('{3}{4}{5}') - assert matches == ['{3}', '{4}', '{5}'] + matches = pattern.findall("{3}{4}{5}") + assert matches == ["{3}", "{4}", "{5}"] - matches = pattern.findall('{3} {4} {5}') - assert matches == ['{3}', '{4}', '{5}'] + matches = pattern.findall("{3} {4} {5}") + assert matches == ["{3}", "{4}", "{5}"] - matches = pattern.findall('{3} {{{4} {5}}}}') - assert matches == ['{3}', '{4}', '{5}'] + matches = pattern.findall("{3} {{{4} {5}}}}") + assert matches == ["{3}", "{4}", "{5}"] - matches = pattern.findall('{3} text {4} stuff {5}}}}') - assert matches == ['{3}', '{4}', '{5}'] + matches = pattern.findall("{3} text {4} stuff {5}}}}") + assert matches == ["{3}", "{4}", "{5}"] # Unicode digit - matches = pattern.findall('{\N{ARABIC-INDIC DIGIT ONE}}') - assert matches == ['{\N{ARABIC-INDIC DIGIT ONE}}'] + matches = pattern.findall("{\N{ARABIC-INDIC DIGIT ONE}}") + assert matches == ["{\N{ARABIC-INDIC DIGIT ONE}}"] # Invalid strings - matches = pattern.findall('5') + matches = pattern.findall("5") assert not matches - matches = pattern.findall('{5') + matches = pattern.findall("{5") assert not matches - matches = pattern.findall('5}') + matches = pattern.findall("5}") assert not matches - matches = pattern.findall('{{5}}') + matches = pattern.findall("{{5}}") assert not matches - matches = pattern.findall('{5text}') + matches = pattern.findall("{5text}") assert not matches @@ -1034,46 +1034,46 @@ def test_macro_escaped_arg_pattern() -> None: pattern = MacroArg.macro_escaped_arg_pattern # Valid strings - matches = pattern.findall('{{5}}') - assert matches == ['{{5}}'] + matches = pattern.findall("{{5}}") + assert matches == ["{{5}}"] - matches = pattern.findall('{{233}}') - assert matches == ['{{233}}'] + matches = pattern.findall("{{233}}") + assert matches == ["{{233}}"] - matches = pattern.findall('{{{{{4}}') - assert matches == ['{{4}}'] + matches = pattern.findall("{{{{{4}}") + assert matches == ["{{4}}"] - matches = pattern.findall('{{2}}}}}') - assert matches == ['{{2}}'] + matches = pattern.findall("{{2}}}}}") + assert matches == ["{{2}}"] - matches = pattern.findall('{{3}}{{4}}{{5}}') - assert matches == ['{{3}}', '{{4}}', '{{5}}'] + matches = pattern.findall("{{3}}{{4}}{{5}}") + assert matches == ["{{3}}", "{{4}}", "{{5}}"] - matches = pattern.findall('{{3}} {{4}} {{5}}') - assert matches == ['{{3}}', '{{4}}', '{{5}}'] + matches = pattern.findall("{{3}} {{4}} {{5}}") + assert matches == ["{{3}}", "{{4}}", "{{5}}"] - matches = pattern.findall('{{3}} {{{4}} {{5}}}}') - assert matches == ['{{3}}', '{{4}}', '{{5}}'] + matches = pattern.findall("{{3}} {{{4}} {{5}}}}") + assert matches == ["{{3}}", "{{4}}", "{{5}}"] - matches = pattern.findall('{{3}} text {{4}} stuff {{5}}}}') - assert matches == ['{{3}}', '{{4}}', '{{5}}'] + matches = pattern.findall("{{3}} text {{4}} stuff {{5}}}}") + assert matches == ["{{3}}", "{{4}}", "{{5}}"] # Unicode digit - matches = pattern.findall('{{\N{ARABIC-INDIC DIGIT ONE}}}') - assert matches == ['{{\N{ARABIC-INDIC DIGIT ONE}}}'] + matches = pattern.findall("{{\N{ARABIC-INDIC DIGIT ONE}}}") + assert matches == ["{{\N{ARABIC-INDIC DIGIT ONE}}}"] # Invalid strings - matches = pattern.findall('5') + matches = pattern.findall("5") assert not matches - matches = pattern.findall('{{5') + matches = pattern.findall("{{5") assert not matches - matches = pattern.findall('5}}') + matches = pattern.findall("5}}") assert not matches - matches = pattern.findall('{5}') + matches = pattern.findall("{5}") assert not matches - matches = pattern.findall('{{5text}}') + matches = pattern.findall("{{5text}}") assert not matches diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 346292a7f..c47c30ad2 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -148,7 +148,7 @@ def precmd_hook_no_return_annotation(self, data: plugin.PrecommandData): return data def precmd_hook_wrong_return_annotation(self, data: plugin.PrecommandData) -> cmd2.Statement: - return self.statement_parser.parse('hi there') + return self.statement_parser.parse("hi there") ### # @@ -190,7 +190,7 @@ def postcmd_hook_no_return_annotation(self, data: plugin.PostcommandData): return data def postcmd_hook_wrong_return_annotation(self, data: plugin.PostcommandData) -> cmd2.Statement: - return self.statement_parser.parse('hi there') + return self.statement_parser.parse("hi there") ### # @@ -258,7 +258,7 @@ def cmdfinalization_hook_no_return_annotation(self, data: plugin.CommandFinaliza def cmdfinalization_hook_wrong_return_annotation(self, data: plugin.CommandFinalizationData) -> cmd2.Statement: """A command finalization hook with the wrong return type annotation.""" - return self.statement_parser.parse('hi there') + return self.statement_parser.parse("hi there") class PluggedApp(Plugin, cmd2.Cmd): @@ -303,30 +303,30 @@ def test_register_preloop_hook_with_return_annotation() -> None: def test_preloop_hook(capsys) -> None: # Need to patch sys.argv so cmd2 doesn't think it was called with arguments equal to the py.test args - testargs = ["prog", "say hello", 'quit'] + testargs = ["prog", "say hello", "quit"] - with mock.patch.object(sys, 'argv', testargs): + with mock.patch.object(sys, "argv", testargs): app = PluggedApp() app.register_preloop_hook(app.prepost_hook_one) app.cmdloop() out, err = capsys.readouterr() - assert out == 'one\nhello\n' + assert out == "one\nhello\n" assert not err def test_preloop_hooks(capsys) -> None: # Need to patch sys.argv so cmd2 doesn't think it was called with arguments equal to the py.test args - testargs = ["prog", "say hello", 'quit'] + testargs = ["prog", "say hello", "quit"] - with mock.patch.object(sys, 'argv', testargs): + with mock.patch.object(sys, "argv", testargs): app = PluggedApp() app.register_preloop_hook(app.prepost_hook_one) app.register_preloop_hook(app.prepost_hook_two) app.cmdloop() out, err = capsys.readouterr() - assert out == 'one\ntwo\nhello\n' + assert out == "one\ntwo\nhello\n" assert not err @@ -344,30 +344,30 @@ def test_register_postloop_hook_with_wrong_return_annotation() -> None: def test_postloop_hook(capsys) -> None: # Need to patch sys.argv so cmd2 doesn't think it was called with arguments equal to the py.test args - testargs = ["prog", "say hello", 'quit'] + testargs = ["prog", "say hello", "quit"] - with mock.patch.object(sys, 'argv', testargs): + with mock.patch.object(sys, "argv", testargs): app = PluggedApp() app.register_postloop_hook(app.prepost_hook_one) app.cmdloop() out, err = capsys.readouterr() - assert out == 'hello\none\n' + assert out == "hello\none\n" assert not err def test_postloop_hooks(capsys) -> None: # Need to patch sys.argv so cmd2 doesn't think it was called with arguments equal to the py.test args - testargs = ["prog", "say hello", 'quit'] + testargs = ["prog", "say hello", "quit"] - with mock.patch.object(sys, 'argv', testargs): + with mock.patch.object(sys, "argv", testargs): app = PluggedApp() app.register_postloop_hook(app.prepost_hook_one) app.register_postloop_hook(app.prepost_hook_two) app.cmdloop() out, err = capsys.readouterr() - assert out == 'hello\none\ntwo\n' + assert out == "hello\none\ntwo\n" assert not err @@ -379,9 +379,9 @@ def test_postloop_hooks(capsys) -> None: def test_preparse(capsys) -> None: app = PluggedApp() app.register_postparsing_hook(app.preparse) - app.onecmd_plus_hooks('say hello') + app.onecmd_plus_hooks("say hello") out, err = capsys.readouterr() - assert out == 'hello\n' + assert out == "hello\n" assert not err assert app.called_preparse == 1 @@ -423,26 +423,26 @@ def test_postparsing_hook_wrong_return_annotation() -> None: def test_postparsing_hook(capsys) -> None: app = PluggedApp() - app.onecmd_plus_hooks('say hello') + app.onecmd_plus_hooks("say hello") out, err = capsys.readouterr() - assert out == 'hello\n' + assert out == "hello\n" assert not err assert not app.called_postparsing app.reset_counters() app.register_postparsing_hook(app.postparse_hook) - app.onecmd_plus_hooks('say hello') + app.onecmd_plus_hooks("say hello") out, err = capsys.readouterr() - assert out == 'hello\n' + assert out == "hello\n" assert not err assert app.called_postparsing == 1 # register the function again, so it should be called twice app.reset_counters() app.register_postparsing_hook(app.postparse_hook) - app.onecmd_plus_hooks('say hello') + app.onecmd_plus_hooks("say hello") out, err = capsys.readouterr() - assert out == 'hello\n' + assert out == "hello\n" assert not err assert app.called_postparsing == 2 @@ -450,14 +450,14 @@ def test_postparsing_hook(capsys) -> None: def test_postparsing_hook_stop_first(capsys) -> None: app = PluggedApp() app.register_postparsing_hook(app.postparse_hook_stop) - stop = app.onecmd_plus_hooks('say hello') + stop = app.onecmd_plus_hooks("say hello") assert app.called_postparsing == 1 assert stop # register another function but it shouldn't be called app.reset_counters() app.register_postparsing_hook(app.postparse_hook) - stop = app.onecmd_plus_hooks('say hello') + stop = app.onecmd_plus_hooks("say hello") assert app.called_postparsing == 1 assert stop @@ -465,21 +465,21 @@ def test_postparsing_hook_stop_first(capsys) -> None: def test_postparsing_hook_stop_second(capsys) -> None: app = PluggedApp() app.register_postparsing_hook(app.postparse_hook) - stop = app.onecmd_plus_hooks('say hello') + stop = app.onecmd_plus_hooks("say hello") assert app.called_postparsing == 1 assert not stop # register another function and make sure it gets called app.reset_counters() app.register_postparsing_hook(app.postparse_hook_stop) - stop = app.onecmd_plus_hooks('say hello') + stop = app.onecmd_plus_hooks("say hello") assert app.called_postparsing == 2 assert stop # register a third function which shouldn't be called app.reset_counters() app.register_postparsing_hook(app.postparse_hook) - stop = app.onecmd_plus_hooks('say hello') + stop = app.onecmd_plus_hooks("say hello") assert app.called_postparsing == 2 assert stop @@ -487,7 +487,7 @@ def test_postparsing_hook_stop_second(capsys) -> None: def test_postparsing_hook_emptystatement_first(capsys) -> None: app = PluggedApp() app.register_postparsing_hook(app.postparse_hook_emptystatement) - stop = app.onecmd_plus_hooks('say hello') + stop = app.onecmd_plus_hooks("say hello") out, err = capsys.readouterr() assert not stop assert not out @@ -497,7 +497,7 @@ def test_postparsing_hook_emptystatement_first(capsys) -> None: # register another function but it shouldn't be called app.reset_counters() stop = app.register_postparsing_hook(app.postparse_hook) - app.onecmd_plus_hooks('say hello') + app.onecmd_plus_hooks("say hello") out, err = capsys.readouterr() assert not stop assert not out @@ -508,17 +508,17 @@ def test_postparsing_hook_emptystatement_first(capsys) -> None: def test_postparsing_hook_emptystatement_second(capsys) -> None: app = PluggedApp() app.register_postparsing_hook(app.postparse_hook) - stop = app.onecmd_plus_hooks('say hello') + stop = app.onecmd_plus_hooks("say hello") out, err = capsys.readouterr() assert not stop - assert out == 'hello\n' + assert out == "hello\n" assert not err assert app.called_postparsing == 1 # register another function and make sure it gets called app.reset_counters() app.register_postparsing_hook(app.postparse_hook_emptystatement) - stop = app.onecmd_plus_hooks('say hello') + stop = app.onecmd_plus_hooks("say hello") out, err = capsys.readouterr() assert not stop assert not out @@ -528,7 +528,7 @@ def test_postparsing_hook_emptystatement_second(capsys) -> None: # register a third function which shouldn't be called app.reset_counters() app.register_postparsing_hook(app.postparse_hook) - stop = app.onecmd_plus_hooks('say hello') + stop = app.onecmd_plus_hooks("say hello") out, err = capsys.readouterr() assert not stop assert not out @@ -539,7 +539,7 @@ def test_postparsing_hook_emptystatement_second(capsys) -> None: def test_postparsing_hook_exception(capsys) -> None: app = PluggedApp() app.register_postparsing_hook(app.postparse_hook_exception) - stop = app.onecmd_plus_hooks('say hello') + stop = app.onecmd_plus_hooks("say hello") out, err = capsys.readouterr() assert not stop assert not out @@ -549,7 +549,7 @@ def test_postparsing_hook_exception(capsys) -> None: # register another function, but it shouldn't be called app.reset_counters() app.register_postparsing_hook(app.postparse_hook) - stop = app.onecmd_plus_hooks('say hello') + stop = app.onecmd_plus_hooks("say hello") out, err = capsys.readouterr() assert not stop assert not out @@ -596,18 +596,18 @@ def test_register_precmd_hook_wrong_return_annotation() -> None: def test_precmd_hook(capsys) -> None: app = PluggedApp() - app.onecmd_plus_hooks('say hello') + app.onecmd_plus_hooks("say hello") out, err = capsys.readouterr() - assert out == 'hello\n' + assert out == "hello\n" assert not err # without registering any hooks, precmd() should be called assert app.called_precmd == 1 app.reset_counters() app.register_precmd_hook(app.precmd_hook) - app.onecmd_plus_hooks('say hello') + app.onecmd_plus_hooks("say hello") out, err = capsys.readouterr() - assert out == 'hello\n' + assert out == "hello\n" assert not err # with one hook registered, we should get precmd() and the hook assert app.called_precmd == 2 @@ -615,9 +615,9 @@ def test_precmd_hook(capsys) -> None: # register the function again, so it should be called twice app.reset_counters() app.register_precmd_hook(app.precmd_hook) - app.onecmd_plus_hooks('say hello') + app.onecmd_plus_hooks("say hello") out, err = capsys.readouterr() - assert out == 'hello\n' + assert out == "hello\n" assert not err # with two hooks registered, we should get precmd() and both hooks assert app.called_precmd == 3 @@ -626,7 +626,7 @@ def test_precmd_hook(capsys) -> None: def test_precmd_hook_emptystatement_first(capsys) -> None: app = PluggedApp() app.register_precmd_hook(app.precmd_hook_emptystatement) - stop = app.onecmd_plus_hooks('say hello') + stop = app.onecmd_plus_hooks("say hello") out, err = capsys.readouterr() assert not stop assert not out @@ -638,7 +638,7 @@ def test_precmd_hook_emptystatement_first(capsys) -> None: # register another function but it shouldn't be called app.reset_counters() stop = app.register_precmd_hook(app.precmd_hook) - app.onecmd_plus_hooks('say hello') + app.onecmd_plus_hooks("say hello") out, err = capsys.readouterr() assert not stop assert not out @@ -652,10 +652,10 @@ def test_precmd_hook_emptystatement_first(capsys) -> None: def test_precmd_hook_emptystatement_second(capsys) -> None: app = PluggedApp() app.register_precmd_hook(app.precmd_hook) - stop = app.onecmd_plus_hooks('say hello') + stop = app.onecmd_plus_hooks("say hello") out, err = capsys.readouterr() assert not stop - assert out == 'hello\n' + assert out == "hello\n" assert not err # with one hook registered, we should get precmd() and the hook assert app.called_precmd == 2 @@ -663,7 +663,7 @@ def test_precmd_hook_emptystatement_second(capsys) -> None: # register another function and make sure it gets called app.reset_counters() app.register_precmd_hook(app.precmd_hook_emptystatement) - stop = app.onecmd_plus_hooks('say hello') + stop = app.onecmd_plus_hooks("say hello") out, err = capsys.readouterr() assert not stop assert not out @@ -675,7 +675,7 @@ def test_precmd_hook_emptystatement_second(capsys) -> None: # register a third function which shouldn't be called app.reset_counters() app.register_precmd_hook(app.precmd_hook) - stop = app.onecmd_plus_hooks('say hello') + stop = app.onecmd_plus_hooks("say hello") out, err = capsys.readouterr() assert not stop assert not out @@ -725,18 +725,18 @@ def test_register_postcmd_hook_wrong_return_annotation() -> None: def test_postcmd(capsys) -> None: app = PluggedApp() - app.onecmd_plus_hooks('say hello') + app.onecmd_plus_hooks("say hello") out, err = capsys.readouterr() - assert out == 'hello\n' + assert out == "hello\n" assert not err # without registering any hooks, postcmd() should be called assert app.called_postcmd == 1 app.reset_counters() app.register_postcmd_hook(app.postcmd_hook) - app.onecmd_plus_hooks('say hello') + app.onecmd_plus_hooks("say hello") out, err = capsys.readouterr() - assert out == 'hello\n' + assert out == "hello\n" assert not err # with one hook registered, we should get precmd() and the hook assert app.called_postcmd == 2 @@ -744,9 +744,9 @@ def test_postcmd(capsys) -> None: # register the function again, so it should be called twice app.reset_counters() app.register_postcmd_hook(app.postcmd_hook) - app.onecmd_plus_hooks('say hello') + app.onecmd_plus_hooks("say hello") out, err = capsys.readouterr() - assert out == 'hello\n' + assert out == "hello\n" assert not err # with two hooks registered, we should get precmd() and both hooks assert app.called_postcmd == 3 @@ -755,10 +755,10 @@ def test_postcmd(capsys) -> None: def test_postcmd_exception_first(capsys) -> None: app = PluggedApp() app.register_postcmd_hook(app.postcmd_hook_exception) - stop = app.onecmd_plus_hooks('say hello') + stop = app.onecmd_plus_hooks("say hello") out, err = capsys.readouterr() assert not stop - assert out == 'hello\n' + assert out == "hello\n" assert err # since the registered hooks are called before postcmd(), if a registered # hook throws an exception, postcmd() is never called. So we should have @@ -768,10 +768,10 @@ def test_postcmd_exception_first(capsys) -> None: # register another function but it shouldn't be called app.reset_counters() stop = app.register_postcmd_hook(app.postcmd_hook) - app.onecmd_plus_hooks('say hello') + app.onecmd_plus_hooks("say hello") out, err = capsys.readouterr() assert not stop - assert out == 'hello\n' + assert out == "hello\n" assert err # the exception raised by the first hook should prevent the second # hook from being called, and it also prevents postcmd() from being @@ -782,10 +782,10 @@ def test_postcmd_exception_first(capsys) -> None: def test_postcmd_exception_second(capsys) -> None: app = PluggedApp() app.register_postcmd_hook(app.postcmd_hook) - stop = app.onecmd_plus_hooks('say hello') + stop = app.onecmd_plus_hooks("say hello") out, err = capsys.readouterr() assert not stop - assert out == 'hello\n' + assert out == "hello\n" assert not err # with one hook registered, we should get the hook and postcmd() assert app.called_postcmd == 2 @@ -793,10 +793,10 @@ def test_postcmd_exception_second(capsys) -> None: # register another function which should be called app.reset_counters() stop = app.register_postcmd_hook(app.postcmd_hook_exception) - app.onecmd_plus_hooks('say hello') + app.onecmd_plus_hooks("say hello") out, err = capsys.readouterr() assert not stop - assert out == 'hello\n' + assert out == "hello\n" assert err # the exception raised by the first hook should prevent the second # hook from being called, and it also prevents postcmd() from being @@ -844,25 +844,25 @@ def test_register_cmdfinalization_hook_wrong_return_annotation() -> None: def test_cmdfinalization(capsys) -> None: app = PluggedApp() - app.onecmd_plus_hooks('say hello') + app.onecmd_plus_hooks("say hello") out, err = capsys.readouterr() - assert out == 'hello\n' + assert out == "hello\n" assert not err assert app.called_cmdfinalization == 0 app.register_cmdfinalization_hook(app.cmdfinalization_hook) - app.onecmd_plus_hooks('say hello') + app.onecmd_plus_hooks("say hello") out, err = capsys.readouterr() - assert out == 'hello\n' + assert out == "hello\n" assert not err assert app.called_cmdfinalization == 1 # register the function again, so it should be called twice app.reset_counters() app.register_cmdfinalization_hook(app.cmdfinalization_hook) - app.onecmd_plus_hooks('say hello') + app.onecmd_plus_hooks("say hello") out, err = capsys.readouterr() - assert out == 'hello\n' + assert out == "hello\n" assert not err assert app.called_cmdfinalization == 2 @@ -871,9 +871,9 @@ def test_cmdfinalization_stop_first(capsys) -> None: app = PluggedApp() app.register_cmdfinalization_hook(app.cmdfinalization_hook_stop) app.register_cmdfinalization_hook(app.cmdfinalization_hook) - stop = app.onecmd_plus_hooks('say hello') + stop = app.onecmd_plus_hooks("say hello") out, err = capsys.readouterr() - assert out == 'hello\n' + assert out == "hello\n" assert not err assert app.called_cmdfinalization == 2 assert stop @@ -883,9 +883,9 @@ def test_cmdfinalization_stop_second(capsys) -> None: app = PluggedApp() app.register_cmdfinalization_hook(app.cmdfinalization_hook) app.register_cmdfinalization_hook(app.cmdfinalization_hook_stop) - stop = app.onecmd_plus_hooks('say hello') + stop = app.onecmd_plus_hooks("say hello") out, err = capsys.readouterr() - assert out == 'hello\n' + assert out == "hello\n" assert not err assert app.called_cmdfinalization == 2 assert stop @@ -894,20 +894,20 @@ def test_cmdfinalization_stop_second(capsys) -> None: def test_cmdfinalization_hook_exception(capsys) -> None: app = PluggedApp() app.register_cmdfinalization_hook(app.cmdfinalization_hook_exception) - stop = app.onecmd_plus_hooks('say hello') + stop = app.onecmd_plus_hooks("say hello") out, err = capsys.readouterr() assert not stop - assert out == 'hello\n' + assert out == "hello\n" assert err assert app.called_cmdfinalization == 1 # register another function, but it shouldn't be called app.reset_counters() app.register_cmdfinalization_hook(app.cmdfinalization_hook) - stop = app.onecmd_plus_hooks('say hello') + stop = app.onecmd_plus_hooks("say hello") out, err = capsys.readouterr() assert not stop - assert out == 'hello\n' + assert out == "hello\n" assert err assert app.called_cmdfinalization == 1 @@ -915,7 +915,7 @@ def test_cmdfinalization_hook_exception(capsys) -> None: def test_cmdfinalization_hook_system_exit() -> None: app = PluggedApp() app.register_cmdfinalization_hook(app.cmdfinalization_hook_system_exit) - stop = app.onecmd_plus_hooks('say hello') + stop = app.onecmd_plus_hooks("say hello") assert stop assert app.called_cmdfinalization == 1 assert app.exit_code == 5 @@ -926,20 +926,20 @@ def test_cmdfinalization_hook_keyboard_interrupt() -> None: app.register_cmdfinalization_hook(app.cmdfinalization_hook_keyboard_interrupt) # First make sure KeyboardInterrupt isn't raised unless told to - stop = app.onecmd_plus_hooks('say hello', raise_keyboard_interrupt=False) + stop = app.onecmd_plus_hooks("say hello", raise_keyboard_interrupt=False) assert not stop assert app.called_cmdfinalization == 1 # Now enable raising the KeyboardInterrupt app.reset_counters() with pytest.raises(KeyboardInterrupt): - stop = app.onecmd_plus_hooks('say hello', raise_keyboard_interrupt=True) + stop = app.onecmd_plus_hooks("say hello", raise_keyboard_interrupt=True) assert not stop assert app.called_cmdfinalization == 1 # Now make sure KeyboardInterrupt isn't raised if stop is already True app.reset_counters() - stop = app.onecmd_plus_hooks('quit', raise_keyboard_interrupt=True) + stop = app.onecmd_plus_hooks("quit", raise_keyboard_interrupt=True) assert stop assert app.called_cmdfinalization == 1 @@ -950,7 +950,7 @@ def test_cmdfinalization_hook_passthrough_exception() -> None: expected_err = "Pass me up" with pytest.raises(OSError, match=expected_err): - app.onecmd_plus_hooks('say hello') + app.onecmd_plus_hooks("say hello") assert app.called_cmdfinalization == 1 @@ -960,7 +960,7 @@ def test_skip_postcmd_hooks(capsys) -> None: app.register_cmdfinalization_hook(app.cmdfinalization_hook) # Cause a SkipPostcommandHooks exception and verify no postcmd stuff runs but cmdfinalization_hook still does - app.onecmd_plus_hooks('skip_postcmd_hooks') + app.onecmd_plus_hooks("skip_postcmd_hooks") out, _err = capsys.readouterr() assert "In do_skip_postcmd_hooks" in out assert app.called_postcmd == 0 @@ -976,9 +976,9 @@ def test_cmd2_argparse_exception(capsys) -> None: app.register_cmdfinalization_hook(app.cmdfinalization_hook) # First generate no exception and make sure postcmd_hook, postcmd, and cmdfinalization_hook run - app.onecmd_plus_hooks('argparse_cmd arg_val') + app.onecmd_plus_hooks("argparse_cmd arg_val") out, err = capsys.readouterr() - assert out == 'arg_val\n' + assert out == "arg_val\n" assert not err assert app.called_postcmd == 2 assert app.called_cmdfinalization == 1 @@ -986,7 +986,7 @@ def test_cmd2_argparse_exception(capsys) -> None: app.reset_counters() # Next cause an argparse exception and verify no postcmd stuff runs but cmdfinalization_hook still does - app.onecmd_plus_hooks('argparse_cmd') + app.onecmd_plus_hooks("argparse_cmd") out, err = capsys.readouterr() assert not out assert "Error: the following arguments are required: my_arg" in err diff --git a/tests/test_pt_utils.py b/tests/test_pt_utils.py index 3051c9716..146ab81c8 100644 --- a/tests/test_pt_utils.py +++ b/tests/test_pt_utils.py @@ -36,9 +36,9 @@ def __init__(self) -> None: self.stdout = io.StringIO() self.always_show_hint = False self.statement_parser = Mock() - self.statement_parser.terminators = [';'] + self.statement_parser.terminators = [";"] self.statement_parser.shortcuts = [] - self.statement_parser._command_pattern = re.compile(r'\A\s*(\S*?)(\s|\Z)') + self.statement_parser._command_pattern = re.compile(r"\A\s*(\S*?)(\s|\Z)") self.aliases = {} self.macros = {} self.all_commands = [] @@ -105,7 +105,7 @@ def test_lex_document_no_style(self, mock_cmd_app): get_line = lexer.lex_document(document) tokens = get_line(0) - assert tokens == [('', line)] + assert tokens == [("", line)] def test_lex_document_command(self, mock_cmd_app): """Test lexing a command name.""" @@ -117,7 +117,7 @@ def test_lex_document_command(self, mock_cmd_app): get_line = lexer.lex_document(document) tokens = get_line(0) - assert tokens == [('ansigreen', 'help'), ('', ' '), ('ansiyellow', 'something')] + assert tokens == [("ansigreen", "help"), ("", " "), ("ansiyellow", "something")] def test_lex_document_alias(self, mock_cmd_app): """Test lexing an alias.""" @@ -129,7 +129,7 @@ def test_lex_document_alias(self, mock_cmd_app): get_line = lexer.lex_document(document) tokens = get_line(0) - assert tokens == [('ansicyan', 'ls'), ('', ' '), ('ansired', '-l')] + assert tokens == [("ansicyan", "ls"), ("", " "), ("ansired", "-l")] def test_lex_document_macro(self, mock_cmd_app): """Test lexing a macro.""" @@ -141,7 +141,7 @@ def test_lex_document_macro(self, mock_cmd_app): get_line = lexer.lex_document(document) tokens = get_line(0) - assert tokens == [('ansimagenta', 'my_macro'), ('', ' '), ('ansiyellow', 'arg1')] + assert tokens == [("ansimagenta", "my_macro"), ("", " "), ("ansiyellow", "arg1")] def test_lex_document_leading_whitespace(self, mock_cmd_app): """Test lexing with leading whitespace.""" @@ -153,7 +153,7 @@ def test_lex_document_leading_whitespace(self, mock_cmd_app): get_line = lexer.lex_document(document) tokens = get_line(0) - assert tokens == [('', ' '), ('ansigreen', 'help'), ('', ' '), ('ansiyellow', 'something')] + assert tokens == [("", " "), ("ansigreen", "help"), ("", " "), ("ansiyellow", "something")] def test_lex_document_unknown_command(self, mock_cmd_app): """Test lexing an unknown command.""" @@ -164,7 +164,7 @@ def test_lex_document_unknown_command(self, mock_cmd_app): get_line = lexer.lex_document(document) tokens = get_line(0) - assert tokens == [('', 'unknown'), ('', ' '), ('ansiyellow', 'command')] + assert tokens == [("", "unknown"), ("", " "), ("ansiyellow", "command")] def test_lex_document_no_command(self, mock_cmd_app): """Test lexing an empty line or line with only whitespace.""" @@ -175,12 +175,12 @@ def test_lex_document_no_command(self, mock_cmd_app): get_line = lexer.lex_document(document) tokens = get_line(0) - assert tokens == [('', ' ')] + assert tokens == [("", " ")] def test_lex_document_no_match(self, mock_cmd_app): """Test lexing when command pattern fails to match.""" # Force the pattern to not match anything - mock_cmd_app.statement_parser._command_pattern = re.compile(r'something_impossible') + mock_cmd_app.statement_parser._command_pattern = re.compile(r"something_impossible") lexer = pt_utils.Cmd2Lexer(cast(Any, mock_cmd_app)) line = "test command" @@ -188,30 +188,30 @@ def test_lex_document_no_match(self, mock_cmd_app): get_line = lexer.lex_document(document) tokens = get_line(0) - assert tokens == [('', line)] + assert tokens == [("", line)] def test_lex_document_arguments(self, mock_cmd_app): """Test lexing a command with flags and values.""" mock_cmd_app.all_commands = ["help"] lexer = pt_utils.Cmd2Lexer(cast(Any, mock_cmd_app)) - line = "help -v --name \"John Doe\" > out.txt" + line = 'help -v --name "John Doe" > out.txt' document = Document(line) get_line = lexer.lex_document(document) tokens = get_line(0) assert tokens == [ - ('ansigreen', 'help'), - ('', ' '), - ('ansired', '-v'), - ('', ' '), - ('ansired', '--name'), - ('', ' '), - ('ansiyellow', '"John Doe"'), - ('', ' '), - ('', '>'), - ('', ' '), - ('ansiyellow', 'out.txt'), + ("ansigreen", "help"), + ("", " "), + ("ansired", "-v"), + ("", " "), + ("ansired", "--name"), + ("", " "), + ("ansiyellow", '"John Doe"'), + ("", " "), + ("", ">"), + ("", " "), + ("ansiyellow", "out.txt"), ] def test_lex_document_unclosed_quote(self, mock_cmd_app): @@ -219,16 +219,16 @@ def test_lex_document_unclosed_quote(self, mock_cmd_app): mock_cmd_app.all_commands = ["echo"] lexer = pt_utils.Cmd2Lexer(cast(Any, mock_cmd_app)) - line = "echo \"hello" + line = 'echo "hello' document = Document(line) get_line = lexer.lex_document(document) tokens = get_line(0) - assert tokens == [('ansigreen', 'echo'), ('', ' '), ('ansiyellow', '"hello')] + assert tokens == [("ansigreen", "echo"), ("", " "), ("ansiyellow", '"hello')] def test_lex_document_shortcut(self, mock_cmd_app): """Test lexing a shortcut.""" - mock_cmd_app.statement_parser.shortcuts = [('!', 'shell')] + mock_cmd_app.statement_parser.shortcuts = [("!", "shell")] lexer = pt_utils.Cmd2Lexer(cast(Any, mock_cmd_app)) # Case 1: Shortcut glued to argument @@ -236,13 +236,13 @@ def test_lex_document_shortcut(self, mock_cmd_app): document = Document(line) get_line = lexer.lex_document(document) tokens = get_line(0) - assert tokens == [('ansigreen', '!'), ('ansiyellow', 'ls')] + assert tokens == [("ansigreen", "!"), ("ansiyellow", "ls")] line = "! ls" document = Document(line) get_line = lexer.lex_document(document) tokens = get_line(0) - assert tokens == [('ansigreen', '!'), ('', ' '), ('ansiyellow', 'ls')] + assert tokens == [("ansigreen", "!"), ("", " "), ("ansiyellow", "ls")] def test_lex_document_multiline(self, mock_cmd_app): """Test lexing a multiline command.""" @@ -256,11 +256,11 @@ def test_lex_document_multiline(self, mock_cmd_app): # First line should have command tokens0 = get_line(0) - assert tokens0 == [('ansigreen', 'orate')] + assert tokens0 == [("ansigreen", "orate")] # Second line should have argument (not command) tokens1 = get_line(1) - assert tokens1 == [('ansiyellow', 'help')] + assert tokens1 == [("ansiyellow", "help")] class TestCmd2Completer: @@ -382,10 +382,10 @@ def test_get_completions_with_error(self, mock_cmd_app: MockCmd, monkeypatch) -> @pytest.mark.parametrize( # search_text_offset is the starting index of the user-provided search text within a full match. # This accounts for leading shortcuts (e.g., in '@has', the offset is 1). - ('line', 'match', 'search_text_offset'), + ("line", "match", "search_text_offset"), [ - ('has', 'has space', 0), - ('@has', '@has space', 1), + ("has", "has space", 0), + ("@has", "@has space", 1), ], ) def test_get_completions_add_opening_quote_and_abort(self, line, match, search_text_offset, mock_cmd_app) -> None: @@ -417,14 +417,14 @@ def test_get_completions_add_opening_quote_and_abort(self, line, match, search_t @pytest.mark.parametrize( # search_text_offset is the starting index of the user-provided search text within a full match. # This accounts for leading shortcuts (e.g., in '@has', the offset is 1). - ('line', 'matches', 'search_text_offset', 'quote_char', 'expected'), + ("line", "matches", "search_text_offset", "quote_char", "expected"), [ # Single matches need opening quote, closing quote, and trailing space - ('', ['has space'], 0, '"', ['"has space" ']), - ('@', ['@has space'], 1, "'", ["@'has space' "]), + ("", ["has space"], 0, '"', ['"has space" ']), + ("@", ["@has space"], 1, "'", ["@'has space' "]), # Multiple matches only need opening quote - ('', ['has space', 'more space'], 0, '"', ['"has space', '"more space']), - ('@', ['@has space', '@more space'], 1, "'", ["@'has space", "@'more space"]), + ("", ["has space", "more space"], 0, '"', ['"has space', '"more space']), + ("@", ["@has space", "@more space"], 1, "'", ["@'has space", "@'more space"]), ], ) def test_get_completions_add_opening_quote_and_return_results( @@ -458,12 +458,12 @@ def test_get_completions_add_opening_quote_and_return_results( assert completion_texts == expected @pytest.mark.parametrize( - ('line', 'match', 'quote_char', 'end_of_line', 'expected'), + ("line", "match", "quote_char", "end_of_line", "expected"), [ # --- Unquoted search text --- # Append a trailing space when end_of_line is True - ('ma', 'match', '', True, 'match '), - ('ma', 'match', '', False, 'match'), + ("ma", "match", "", True, "match "), + ("ma", "match", "", False, "match"), # --- Quoted search text --- # Ensure closing quotes are added # Append a trailing space when end_of_line is True @@ -491,11 +491,11 @@ def test_get_completions_allow_finalization( assert completions[0].text == expected @pytest.mark.parametrize( - ('line', 'match', 'quote_char', 'end_of_line', 'expected'), + ("line", "match", "quote_char", "end_of_line", "expected"), [ # Do not add a trailing space or closing quote to any of the matches - ('ma', 'match', '', True, 'match'), - ('ma', 'match', '', False, 'match'), + ("ma", "match", "", True, "match"), + ("ma", "match", "", False, "match"), ('"ma', '"match', '"', True, '"match'), ("'ma", "'match", "'", False, "'match"), ], @@ -536,11 +536,11 @@ def test_init_with_custom_settings(self, mock_cmd_app: MockCmd) -> None: list(completer.get_completions(document, None)) mock_cmd_app.complete.assert_called_once() - assert mock_cmd_app.complete.call_args[1]['custom_settings'] == custom_settings + assert mock_cmd_app.complete.call_args[1]["custom_settings"] == custom_settings def test_get_completions_custom_delimiters(self, mock_cmd_app: MockCmd) -> None: """Test that custom delimiters (terminators) are respected.""" - mock_cmd_app.statement_parser.terminators = ['#'] + mock_cmd_app.statement_parser.terminators = ["#"] completer = pt_utils.Cmd2Completer(cast(Any, mock_cmd_app)) # '#' should act as a word boundary diff --git a/tests/test_py_completion.py b/tests/test_py_completion.py index c81243c7b..d463119b7 100644 --- a/tests/test_py_completion.py +++ b/tests/test_py_completion.py @@ -10,10 +10,10 @@ def test_py_completion_setup_readline(base_app): # Mock readline and rlcompleter mock_readline = mock.MagicMock() - mock_readline.__doc__ = 'GNU Readline' + mock_readline.__doc__ = "GNU Readline" mock_rlcompleter = mock.MagicMock() - with mock.patch.dict(sys.modules, {'readline': mock_readline, 'rlcompleter': mock_rlcompleter}): + with mock.patch.dict(sys.modules, {"readline": mock_readline, "rlcompleter": mock_rlcompleter}): interp = InteractiveConsole() base_app._set_up_py_shell_env(interp) @@ -25,10 +25,10 @@ def test_py_completion_setup_readline(base_app): def test_py_completion_setup_libedit(base_app): # Mock readline and rlcompleter mock_readline = mock.MagicMock() - mock_readline.__doc__ = 'libedit' + mock_readline.__doc__ = "libedit" mock_rlcompleter = mock.MagicMock() - with mock.patch.dict(sys.modules, {'readline': mock_readline, 'rlcompleter': mock_rlcompleter}): + with mock.patch.dict(sys.modules, {"readline": mock_readline, "rlcompleter": mock_rlcompleter}): interp = InteractiveConsole() base_app._set_up_py_shell_env(interp) @@ -43,7 +43,7 @@ def test_py_completion_restore(base_app): original_completer = mock.Mock() mock_readline.get_completer.return_value = original_completer - with mock.patch.dict(sys.modules, {'readline': mock_readline, 'rlcompleter': mock.MagicMock()}): + with mock.patch.dict(sys.modules, {"readline": mock_readline, "rlcompleter": mock.MagicMock()}): interp = InteractiveConsole() env = base_app._set_up_py_shell_env(interp) diff --git a/tests/test_rich_utils.py b/tests/test_rich_utils.py index 1a90406ab..1401c860c 100644 --- a/tests/test_rich_utils.py +++ b/tests/test_rich_utils.py @@ -26,19 +26,19 @@ def test_cmd2_base_console() -> None: # Test the keyword arguments which are not allowed. with pytest.raises(TypeError) as excinfo: ru.Cmd2BaseConsole(color_system="auto") - assert 'color_system' in str(excinfo.value) + assert "color_system" in str(excinfo.value) with pytest.raises(TypeError) as excinfo: ru.Cmd2BaseConsole(force_terminal=True) - assert 'force_terminal' in str(excinfo.value) + assert "force_terminal" in str(excinfo.value) with pytest.raises(TypeError) as excinfo: ru.Cmd2BaseConsole(force_interactive=True) - assert 'force_interactive' in str(excinfo.value) + assert "force_interactive" in str(excinfo.value) with pytest.raises(TypeError) as excinfo: ru.Cmd2BaseConsole(theme=None) - assert 'theme' in str(excinfo.value) + assert "theme" in str(excinfo.value) def test_indented_text() -> None: @@ -80,7 +80,7 @@ def test_indented_table() -> None: @pytest.mark.parametrize( - ('rich_text', 'string'), + ("rich_text", "string"), [ (Text("Hello"), "Hello"), (Text("Hello\n"), "Hello\n"), @@ -178,8 +178,8 @@ def test_cmd2_base_console_log(mocker: MockerFixture) -> None: def test_cmd2_base_console_init_always_interactive_true() -> None: """Test Cmd2BaseConsole initialization when ALLOW_STYLE is ALWAYS and is_interactive is True.""" with ( - mock.patch('rich.console.Console.__init__', return_value=None) as mock_base_init, - mock.patch('cmd2.rich_utils.Console', autospec=True) as mock_detect_console_class, + mock.patch("rich.console.Console.__init__", return_value=None) as mock_base_init, + mock.patch("cmd2.rich_utils.Console", autospec=True) as mock_detect_console_class, ): mock_detect_console = mock_detect_console_class.return_value mock_detect_console.is_interactive = True @@ -188,17 +188,17 @@ def test_cmd2_base_console_init_always_interactive_true() -> None: # Verify arguments passed to super().__init__ _, kwargs = mock_base_init.call_args - assert kwargs['color_system'] == "truecolor" - assert kwargs['force_terminal'] is True - assert kwargs['force_interactive'] is True + assert kwargs["color_system"] == "truecolor" + assert kwargs["force_terminal"] is True + assert kwargs["force_interactive"] is True @with_ansi_style(ru.AllowStyle.ALWAYS) def test_cmd2_base_console_init_always_interactive_false() -> None: """Test Cmd2BaseConsole initialization when ALLOW_STYLE is ALWAYS and is_interactive is False.""" with ( - mock.patch('rich.console.Console.__init__', return_value=None) as mock_base_init, - mock.patch('cmd2.rich_utils.Console', autospec=True) as mock_detect_console_class, + mock.patch("rich.console.Console.__init__", return_value=None) as mock_base_init, + mock.patch("cmd2.rich_utils.Console", autospec=True) as mock_detect_console_class, ): mock_detect_console = mock_detect_console_class.return_value mock_detect_console.is_interactive = False @@ -206,17 +206,17 @@ def test_cmd2_base_console_init_always_interactive_false() -> None: ru.Cmd2BaseConsole() _, kwargs = mock_base_init.call_args - assert kwargs['color_system'] == "truecolor" - assert kwargs['force_terminal'] is True - assert kwargs['force_interactive'] is False + assert kwargs["color_system"] == "truecolor" + assert kwargs["force_terminal"] is True + assert kwargs["force_interactive"] is False @with_ansi_style(ru.AllowStyle.TERMINAL) def test_cmd2_base_console_init_terminal_true() -> None: """Test Cmd2BaseConsole initialization when ALLOW_STYLE is TERMINAL and it is a terminal.""" with ( - mock.patch('rich.console.Console.__init__', return_value=None) as mock_base_init, - mock.patch('cmd2.rich_utils.Console', autospec=True) as mock_detect_console_class, + mock.patch("rich.console.Console.__init__", return_value=None) as mock_base_init, + mock.patch("cmd2.rich_utils.Console", autospec=True) as mock_detect_console_class, ): mock_detect_console = mock_detect_console_class.return_value mock_detect_console.is_terminal = True @@ -224,17 +224,17 @@ def test_cmd2_base_console_init_terminal_true() -> None: ru.Cmd2BaseConsole() _, kwargs = mock_base_init.call_args - assert kwargs['color_system'] == "truecolor" - assert kwargs['force_terminal'] is None - assert kwargs['force_interactive'] is None + assert kwargs["color_system"] == "truecolor" + assert kwargs["force_terminal"] is None + assert kwargs["force_interactive"] is None @with_ansi_style(ru.AllowStyle.TERMINAL) def test_cmd2_base_console_init_terminal_false() -> None: """Test Cmd2BaseConsole initialization when ALLOW_STYLE is TERMINAL and it is not a terminal.""" with ( - mock.patch('rich.console.Console.__init__', return_value=None) as mock_base_init, - mock.patch('cmd2.rich_utils.Console', autospec=True) as mock_detect_console_class, + mock.patch("rich.console.Console.__init__", return_value=None) as mock_base_init, + mock.patch("cmd2.rich_utils.Console", autospec=True) as mock_detect_console_class, ): mock_detect_console = mock_detect_console_class.return_value mock_detect_console.is_terminal = False @@ -242,21 +242,21 @@ def test_cmd2_base_console_init_terminal_false() -> None: ru.Cmd2BaseConsole() _, kwargs = mock_base_init.call_args - assert kwargs['color_system'] is None - assert kwargs['force_terminal'] is None - assert kwargs['force_interactive'] is None + assert kwargs["color_system"] is None + assert kwargs["force_terminal"] is None + assert kwargs["force_interactive"] is None @with_ansi_style(ru.AllowStyle.NEVER) def test_cmd2_base_console_init_never() -> None: """Test Cmd2BaseConsole initialization when ALLOW_STYLE is NEVER.""" - with mock.patch('rich.console.Console.__init__', return_value=None) as mock_base_init: + with mock.patch("rich.console.Console.__init__", return_value=None) as mock_base_init: ru.Cmd2BaseConsole() _, kwargs = mock_base_init.call_args - assert kwargs['color_system'] is None - assert kwargs['force_terminal'] is False - assert kwargs['force_interactive'] is None + assert kwargs["color_system"] is None + assert kwargs["force_terminal"] is False + assert kwargs["force_interactive"] is None def test_text_group_direct_cmd2() -> None: @@ -321,7 +321,7 @@ def _get_formatter(self, **kwargs: Any) -> ru.Cmd2HelpFormatter: def test_formatter_console() -> None: # self._console = console (inside console.setter) - formatter = ru.Cmd2HelpFormatter(prog='test') + formatter = ru.Cmd2HelpFormatter(prog="test") new_console = ru.Cmd2RichArgparseConsole() formatter.console = new_console assert formatter._console is new_console @@ -332,17 +332,17 @@ def test_formatter_console() -> None: reason="Argparse didn't support color until Python 3.14", ) def test_formatter_set_color(mocker: MockerFixture) -> None: - formatter = ru.Cmd2HelpFormatter(prog='test') + formatter = ru.Cmd2HelpFormatter(prog="test") # return (inside _set_color if sys.version_info < (3, 14)) - mocker.patch('cmd2.argparse_utils.sys.version_info', (3, 13, 0)) + mocker.patch("cmd2.argparse_utils.sys.version_info", (3, 13, 0)) # This should return early without calling super()._set_color - mock_set_color = mocker.patch('rich_argparse.RichHelpFormatter._set_color') + mock_set_color = mocker.patch("rich_argparse.RichHelpFormatter._set_color") formatter._set_color(True) mock_set_color.assert_not_called() # except TypeError and super()._set_color(color) - mocker.patch('cmd2.argparse_utils.sys.version_info', (3, 15, 0)) + mocker.patch("cmd2.argparse_utils.sys.version_info", (3, 15, 0)) # Reset mock and make it raise TypeError when called with kwargs mock_set_color.reset_mock() diff --git a/tests/test_run_pyscript.py b/tests/test_run_pyscript.py index d085a464d..e19f77c59 100644 --- a/tests/test_run_pyscript.py +++ b/tests/test_run_pyscript.py @@ -17,8 +17,8 @@ def test_run_pyscript(base_app, request) -> None: test_dir = os.path.dirname(request.module.__file__) - python_script = os.path.join(test_dir, 'script.py') - expected = 'This is a python script running ...' + python_script = os.path.join(test_dir, "script.py") + expected = "This is a python script running ..." out, _err = run_cmd(base_app, f"run_pyscript {python_script}") assert expected in out @@ -27,8 +27,8 @@ def test_run_pyscript(base_app, request) -> None: def test_run_pyscript_recursive_not_allowed(base_app, request) -> None: test_dir = os.path.dirname(request.module.__file__) - python_script = os.path.join(test_dir, 'pyscript', 'recursive.py') - expected = 'Recursively entering interactive Python shells is not allowed' + python_script = os.path.join(test_dir, "pyscript", "recursive.py") + expected = "Recursively entering interactive Python shells is not allowed" _out, err = run_cmd(base_app, f"run_pyscript {python_script}") assert err[0] == expected @@ -36,7 +36,7 @@ def test_run_pyscript_recursive_not_allowed(base_app, request) -> None: def test_run_pyscript_with_nonexist_file(base_app) -> None: - python_script = 'does_not_exist.py' + python_script = "does_not_exist.py" _out, err = run_cmd(base_app, f"run_pyscript {python_script}") assert "Error reading script file" in err[0] assert base_app.last_result is False @@ -44,36 +44,36 @@ def test_run_pyscript_with_nonexist_file(base_app) -> None: def test_run_pyscript_with_non_python_file(base_app, request, monkeypatch) -> None: # Mock out the read_input call so we don't actually wait for a user's response on stdin - read_input_mock = mock.MagicMock(name='read_input', return_value='2') + read_input_mock = mock.MagicMock(name="read_input", return_value="2") monkeypatch.setattr("cmd2.Cmd.read_input", read_input_mock) test_dir = os.path.dirname(request.module.__file__) - filename = os.path.join(test_dir, 'scripts', 'help.txt') - _out, err = run_cmd(base_app, f'run_pyscript {filename}') + filename = os.path.join(test_dir, "scripts", "help.txt") + _out, err = run_cmd(base_app, f"run_pyscript {filename}") assert "does not have a .py extension" in err[0] assert base_app.last_result is False -@pytest.mark.parametrize('python_script', odd_file_names) +@pytest.mark.parametrize("python_script", odd_file_names) def test_run_pyscript_with_odd_file_names(base_app, python_script, monkeypatch) -> None: """Pass in file names with various patterns. Since these files don't exist, we will rely on the error text to make sure the file names were processed correctly. """ # Mock read_input to get us passed the warning about not ending in .py - read_input_mock = mock.MagicMock(name='read_input', return_value='1') + read_input_mock = mock.MagicMock(name="read_input", return_value="1") monkeypatch.setattr("cmd2.Cmd.read_input", read_input_mock) _out, err = run_cmd(base_app, f"run_pyscript {quote(python_script)}") - err = ''.join(err) + err = "".join(err) assert f"Error reading script file '{python_script}'" in err assert base_app.last_result is False def test_run_pyscript_with_exception(base_app, request) -> None: test_dir = os.path.dirname(request.module.__file__) - python_script = os.path.join(test_dir, 'pyscript', 'raises_exception.py') + python_script = os.path.join(test_dir, "pyscript", "raises_exception.py") _out, err = run_cmd(base_app, f"run_pyscript {python_script}") - assert err[0].startswith('Traceback') + assert err[0].startswith("Traceback") assert "TypeError: unsupported operand type(s) for +: 'int' and 'str'" in err[-1] assert base_app.last_result is True @@ -86,17 +86,17 @@ def test_run_pyscript_requires_an_argument(base_app) -> None: def test_run_pyscript_help(base_app, request) -> None: test_dir = os.path.dirname(request.module.__file__) - python_script = os.path.join(test_dir, 'pyscript', 'help.py') - out1, _err1 = run_cmd(base_app, 'help') - out2, _err2 = run_cmd(base_app, f'run_pyscript {python_script}') + python_script = os.path.join(test_dir, "pyscript", "help.py") + out1, _err1 = run_cmd(base_app, "help") + out2, _err2 = run_cmd(base_app, f"run_pyscript {python_script}") assert out1 assert out1 == out2 def test_scripts_add_to_history(base_app, request) -> None: test_dir = os.path.dirname(request.module.__file__) - python_script = os.path.join(test_dir, 'pyscript', 'help.py') - command = f'run_pyscript {python_script}' + python_script = os.path.join(test_dir, "pyscript", "help.py") + command = f"run_pyscript {python_script}" # Add to history base_app.scripts_add_to_history = True @@ -104,7 +104,7 @@ def test_scripts_add_to_history(base_app, request) -> None: run_cmd(base_app, command) assert len(base_app.history) == 2 assert base_app.history.get(1).raw == command - assert base_app.history.get(2).raw == 'help' + assert base_app.history.get(2).raw == "help" # Do not add to history base_app.scripts_add_to_history = False @@ -116,17 +116,17 @@ def test_scripts_add_to_history(base_app, request) -> None: def test_run_pyscript_dir(base_app, request) -> None: test_dir = os.path.dirname(request.module.__file__) - python_script = os.path.join(test_dir, 'pyscript', 'pyscript_dir.py') + python_script = os.path.join(test_dir, "pyscript", "pyscript_dir.py") - out, _err = run_cmd(base_app, f'run_pyscript {python_script}') + out, _err = run_cmd(base_app, f"run_pyscript {python_script}") assert out[0] == "['cmd_echo']" def test_run_pyscript_capture(base_app, request) -> None: base_app.self_in_py = True test_dir = os.path.dirname(request.module.__file__) - python_script = os.path.join(test_dir, 'pyscript', 'stdout_capture.py') - out, _err = run_cmd(base_app, f'run_pyscript {python_script}') + python_script = os.path.join(test_dir, "pyscript", "stdout_capture.py") + out, _err = run_cmd(base_app, f"run_pyscript {python_script}") assert out[0] == "print" assert out[1] == "poutput" @@ -140,8 +140,8 @@ def test_run_pyscript_capture_custom_stdout(base_app, request) -> None: base_app.self_in_py = True test_dir = os.path.dirname(request.module.__file__) - python_script = os.path.join(test_dir, 'pyscript', 'stdout_capture.py') - out, _err = run_cmd(base_app, f'run_pyscript {python_script}') + python_script = os.path.join(test_dir, "pyscript", "stdout_capture.py") + out, _err = run_cmd(base_app, f"run_pyscript {python_script}") assert "print" not in out assert out[0] == "poutput" @@ -152,64 +152,64 @@ def test_run_pyscript_stop(base_app, request) -> None: test_dir = os.path.dirname(request.module.__file__) # help.py doesn't run any commands that return True for stop - python_script = os.path.join(test_dir, 'pyscript', 'help.py') - stop = base_app.onecmd_plus_hooks(f'run_pyscript {python_script}') + python_script = os.path.join(test_dir, "pyscript", "help.py") + stop = base_app.onecmd_plus_hooks(f"run_pyscript {python_script}") assert not stop # stop.py runs the quit command which does return True for stop - python_script = os.path.join(test_dir, 'pyscript', 'stop.py') - stop = base_app.onecmd_plus_hooks(f'run_pyscript {python_script}') + python_script = os.path.join(test_dir, "pyscript", "stop.py") + stop = base_app.onecmd_plus_hooks(f"run_pyscript {python_script}") assert stop def test_run_pyscript_environment(base_app, request) -> None: test_dir = os.path.dirname(request.module.__file__) - python_script = os.path.join(test_dir, 'pyscript', 'environment.py') - out, _err = run_cmd(base_app, f'run_pyscript {python_script}') + python_script = os.path.join(test_dir, "pyscript", "environment.py") + out, _err = run_cmd(base_app, f"run_pyscript {python_script}") assert out[0] == "PASSED" def test_run_pyscript_self_in_py(base_app, request) -> None: test_dir = os.path.dirname(request.module.__file__) - python_script = os.path.join(test_dir, 'pyscript', 'self_in_py.py') + python_script = os.path.join(test_dir, "pyscript", "self_in_py.py") # Set self_in_py to True and make sure we see self base_app.self_in_py = True - out, _err = run_cmd(base_app, f'run_pyscript {python_script}') - assert 'I see self' in out[0] + out, _err = run_cmd(base_app, f"run_pyscript {python_script}") + assert "I see self" in out[0] # Set self_in_py to False and make sure we can't see self base_app.self_in_py = False - out, _err = run_cmd(base_app, f'run_pyscript {python_script}') - assert 'I do not see self' in out[0] + out, _err = run_cmd(base_app, f"run_pyscript {python_script}") + assert "I do not see self" in out[0] def test_run_pyscript_py_locals(base_app, request) -> None: test_dir = os.path.dirname(request.module.__file__) - python_script = os.path.join(test_dir, 'pyscript', 'py_locals.py') + python_script = os.path.join(test_dir, "pyscript", "py_locals.py") # Make sure pyscripts can't edit Cmd.py_locals. It used to be that cmd2 was passing its py_locals # dictionary to the py environment instead of a shallow copy. - base_app.py_locals['test_var'] = 5 + base_app.py_locals["test_var"] = 5 # Place an editable object in py_locals. Since we make a shallow copy of py_locals, # this object should be editable from the py environment. - base_app.py_locals['my_list'] = [] + base_app.py_locals["my_list"] = [] - run_cmd(base_app, f'run_pyscript {python_script}') + run_cmd(base_app, f"run_pyscript {python_script}") # test_var should still exist - assert base_app.py_locals['test_var'] == 5 + assert base_app.py_locals["test_var"] == 5 # my_list should be edited - assert base_app.py_locals['my_list'][0] == 2 + assert base_app.py_locals["my_list"][0] == 2 def test_run_pyscript_app_echo(base_app, request) -> None: test_dir = os.path.dirname(request.module.__file__) - python_script = os.path.join(test_dir, 'pyscript', 'echo.py') - out, _err = run_cmd(base_app, f'run_pyscript {python_script}') + python_script = os.path.join(test_dir, "pyscript", "echo.py") + out, _err = run_cmd(base_app, f"run_pyscript {python_script}") # Only the edit help text should have been echoed to pytest's stdout assert out[0] == "Usage: edit [-h] [file_path]" diff --git a/tests/test_string_utils.py b/tests/test_string_utils.py index 5e84d5350..ae0a8cd72 100644 --- a/tests/test_string_utils.py +++ b/tests/test_string_utils.py @@ -6,28 +6,28 @@ from cmd2 import rich_utils as ru from cmd2 import string_utils as su -HELLO_WORLD = 'Hello, world!' +HELLO_WORLD = "Hello, world!" def test_align_blank() -> None: - text = '' - character = '-' + text = "" + character = "-" width = 5 aligned = su.align(text, "left", width=width, character=character) assert aligned == character * width def test_align_wider_than_width() -> None: - text = 'long text field' - character = '-' + text = "long text field" + character = "-" width = 8 aligned = su.align(text, "left", width=width, character=character) assert aligned == text[:width] def test_align_term_width() -> None: - text = 'foo' - character = ' ' + text = "foo" + character = " " term_width = ru.console_width() expected_padding = (term_width - su.str_width(text)) * character @@ -37,25 +37,25 @@ def test_align_term_width() -> None: def test_align_left() -> None: - text = 'foo' - character = '-' + text = "foo" + character = "-" width = 5 aligned = su.align_left(text, width=width, character=character) assert aligned == text + character * 2 def test_align_left_wide_text() -> None: - text = '苹' - character = '-' + text = "苹" + character = "-" width = 4 aligned = su.align_left(text, width=width, character=character) assert aligned == text + character * 2 def test_align_left_with_style() -> None: - character = '-' + character = "-" - styled_text = su.stylize('table', style=Color.BRIGHT_BLUE) + styled_text = su.stylize("table", style=Color.BRIGHT_BLUE) width = 8 aligned = su.align_left(styled_text, width=width, character=character) @@ -63,25 +63,25 @@ def test_align_left_with_style() -> None: def test_align_center() -> None: - text = 'foo' - character = '-' + text = "foo" + character = "-" width = 5 aligned = su.align_center(text, width=width, character=character) assert aligned == character + text + character def test_align_center_wide_text() -> None: - text = '苹' - character = '-' + text = "苹" + character = "-" width = 4 aligned = su.align_center(text, width=width, character=character) assert aligned == character + text + character def test_align_center_with_style() -> None: - character = '-' + character = "-" - styled_text = su.stylize('table', style=Color.BRIGHT_BLUE) + styled_text = su.stylize("table", style=Color.BRIGHT_BLUE) width = 8 aligned = su.align_center(styled_text, width=width, character=character) @@ -89,25 +89,25 @@ def test_align_center_with_style() -> None: def test_align_right() -> None: - text = 'foo' - character = '-' + text = "foo" + character = "-" width = 5 aligned = su.align_right(text, width=width, character=character) assert aligned == character * 2 + text def test_align_right_wide_text() -> None: - text = '苹' - character = '-' + text = "苹" + character = "-" width = 4 aligned = su.align_right(text, width=width, character=character) assert aligned == character * 2 + text def test_align_right_with_style() -> None: - character = '-' + character = "-" - styled_text = su.stylize('table', style=Color.BRIGHT_BLUE) + styled_text = su.stylize("table", style=Color.BRIGHT_BLUE) width = 8 aligned = su.align_right(styled_text, width=width, character=character) @@ -175,7 +175,7 @@ def test_str_width() -> None: def test_is_quoted_short() -> None: - my_str = '' + my_str = "" assert not su.is_quoted(my_str) your_str = '"' assert not su.is_quoted(your_str) @@ -235,14 +235,14 @@ def test_strip_quotes_with_quotes() -> None: def test_unicode_normalization() -> None: - s1 = 'café' - s2 = 'cafe\u0301' + s1 = "café" + s2 = "cafe\u0301" assert s1 != s2 assert su.norm_fold(s1) == su.norm_fold(s2) def test_unicode_casefold() -> None: - micro = 'µ' + micro = "µ" micro_cf = micro.casefold() assert micro != micro_cf assert su.norm_fold(micro) == su.norm_fold(micro_cf) diff --git a/tests/test_utils.py b/tests/test_utils.py index a5a83ba13..bcdc6a98e 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -12,7 +12,7 @@ import cmd2.utils as cu -HELLO_WORLD = 'Hello, world!' +HELLO_WORLD = "Hello, world!" def test_remove_duplicates_no_duplicates() -> None: @@ -26,37 +26,37 @@ def test_remove_duplicates_with_duplicates() -> None: def test_alphabetical_sort() -> None: - my_list = ['café', 'µ', 'A', 'micro', 'unity', 'cafeteria'] - assert cu.alphabetical_sort(my_list) == ['A', 'cafeteria', 'café', 'micro', 'unity', 'µ'] - my_list = ['a3', 'a22', 'A2', 'A11', 'a1'] - assert cu.alphabetical_sort(my_list) == ['a1', 'A11', 'A2', 'a22', 'a3'] + my_list = ["café", "µ", "A", "micro", "unity", "cafeteria"] + assert cu.alphabetical_sort(my_list) == ["A", "cafeteria", "café", "micro", "unity", "µ"] + my_list = ["a3", "a22", "A2", "A11", "a1"] + assert cu.alphabetical_sort(my_list) == ["a1", "A11", "A2", "a22", "a3"] def test_try_int_or_force_to_lower_case() -> None: - str1 = '17' + str1 = "17" assert cu.try_int_or_force_to_lower_case(str1) == 17 - str1 = 'ABC' - assert cu.try_int_or_force_to_lower_case(str1) == 'abc' - str1 = 'X19' - assert cu.try_int_or_force_to_lower_case(str1) == 'x19' - str1 = '' - assert cu.try_int_or_force_to_lower_case(str1) == '' + str1 = "ABC" + assert cu.try_int_or_force_to_lower_case(str1) == "abc" + str1 = "X19" + assert cu.try_int_or_force_to_lower_case(str1) == "x19" + str1 = "" + assert cu.try_int_or_force_to_lower_case(str1) == "" def test_natural_keys() -> None: - my_list = ['café', 'µ', 'A', 'micro', 'unity', 'x1', 'X2', 'X11', 'X0', 'x22'] + my_list = ["café", "µ", "A", "micro", "unity", "x1", "X2", "X11", "X0", "x22"] my_list.sort(key=cu.natural_keys) - assert my_list == ['A', 'café', 'micro', 'unity', 'X0', 'x1', 'X2', 'X11', 'x22', 'µ'] - my_list = ['a3', 'a22', 'A2', 'A11', 'a1'] + assert my_list == ["A", "café", "micro", "unity", "X0", "x1", "X2", "X11", "x22", "µ"] + my_list = ["a3", "a22", "A2", "A11", "a1"] my_list.sort(key=cu.natural_keys) - assert my_list == ['a1', 'A2', 'a3', 'A11', 'a22'] + assert my_list == ["a1", "A2", "a3", "A11", "a22"] def test_natural_sort() -> None: - my_list = ['café', 'µ', 'A', 'micro', 'unity', 'x1', 'X2', 'X11', 'X0', 'x22'] - assert cu.natural_sort(my_list) == ['A', 'café', 'micro', 'unity', 'X0', 'x1', 'X2', 'X11', 'x22', 'µ'] - my_list = ['a3', 'a22', 'A2', 'A11', 'a1'] - assert cu.natural_sort(my_list) == ['a1', 'A2', 'a3', 'A11', 'a22'] + my_list = ["café", "µ", "A", "micro", "unity", "x1", "X2", "X11", "X0", "x22"] + assert cu.natural_sort(my_list) == ["A", "café", "micro", "unity", "X0", "x1", "X2", "X11", "x22", "µ"] + my_list = ["a3", "a22", "A2", "A11", "a1"] + assert cu.natural_sort(my_list) == ["a1", "A2", "a3", "A11", "a22"] @pytest.fixture @@ -65,38 +65,38 @@ def stdout_sim(): def test_stdsim_write_str(stdout_sim) -> None: - my_str = 'Hello World' + my_str = "Hello World" stdout_sim.write(my_str) assert stdout_sim.getvalue() == my_str def test_stdsim_write_bytes(stdout_sim) -> None: - b_str = b'Hello World' + b_str = b"Hello World" with pytest.raises(TypeError): stdout_sim.write(b_str) def test_stdsim_buffer_write_bytes(stdout_sim) -> None: - b_str = b'Hello World' + b_str = b"Hello World" stdout_sim.buffer.write(b_str) assert stdout_sim.getvalue() == b_str.decode() assert stdout_sim.getbytes() == b_str def test_stdsim_buffer_write_str(stdout_sim) -> None: - my_str = 'Hello World' + my_str = "Hello World" with pytest.raises(TypeError): stdout_sim.buffer.write(my_str) def test_stdsim_read(stdout_sim) -> None: - my_str = 'Hello World' + my_str = "Hello World" stdout_sim.write(my_str) # getvalue() returns the value and leaves it unaffected internally assert stdout_sim.getvalue() == my_str # read() returns the value and then clears the internal buffer assert stdout_sim.read() == my_str - assert stdout_sim.getvalue() == '' + assert stdout_sim.getvalue() == "" stdout_sim.write(my_str) @@ -106,26 +106,26 @@ def test_stdsim_read(stdout_sim) -> None: def test_stdsim_read_bytes(stdout_sim) -> None: - b_str = b'Hello World' + b_str = b"Hello World" stdout_sim.buffer.write(b_str) # getbytes() returns the value and leaves it unaffected internally assert stdout_sim.getbytes() == b_str # read_bytes() returns the value and then clears the internal buffer assert stdout_sim.readbytes() == b_str - assert stdout_sim.getbytes() == b'' + assert stdout_sim.getbytes() == b"" def test_stdsim_clear(stdout_sim) -> None: - my_str = 'Hello World' + my_str = "Hello World" stdout_sim.write(my_str) assert stdout_sim.getvalue() == my_str stdout_sim.clear() - assert stdout_sim.getvalue() == '' + assert stdout_sim.getvalue() == "" def test_stdsim_getattr_exist(stdout_sim) -> None: # Here the StdSim getattr is allowing us to access methods within StdSim - my_str = 'Hello World' + my_str = "Hello World" stdout_sim.write(my_str) val_func = stdout_sim.getvalue assert val_func() == my_str @@ -138,7 +138,7 @@ def test_stdsim_getattr_noexist(stdout_sim) -> None: def test_stdsim_pause_storage(stdout_sim) -> None: # Test pausing storage for string data - my_str = 'Hello World' + my_str = "Hello World" stdout_sim.pause_storage = False stdout_sim.write(my_str) @@ -146,10 +146,10 @@ def test_stdsim_pause_storage(stdout_sim) -> None: stdout_sim.pause_storage = True stdout_sim.write(my_str) - assert stdout_sim.read() == '' + assert stdout_sim.read() == "" # Test pausing storage for binary data - b_str = b'Hello World' + b_str = b"Hello World" stdout_sim.pause_storage = False stdout_sim.buffer.write(b_str) @@ -157,7 +157,7 @@ def test_stdsim_pause_storage(stdout_sim) -> None: stdout_sim.pause_storage = True stdout_sim.buffer.write(b_str) - assert stdout_sim.getbytes() == b'' + assert stdout_sim.getbytes() == b"" def test_stdsim_line_buffering(base_app) -> None: @@ -166,18 +166,18 @@ def test_stdsim_line_buffering(base_app) -> None: import os import tempfile - with tempfile.NamedTemporaryFile(mode='wt') as file: + with tempfile.NamedTemporaryFile(mode="wt") as file: file.line_buffering = True stdsim = cu.StdSim(file, echo=True) saved_size = os.path.getsize(file.name) - bytes_to_write = b'hello\n' + bytes_to_write = b"hello\n" stdsim.buffer.write(bytes_to_write) assert os.path.getsize(file.name) == saved_size + len(bytes_to_write) saved_size = os.path.getsize(file.name) - bytes_to_write = b'hello\r' + bytes_to_write = b"hello\r" stdsim.buffer.write(bytes_to_write) assert os.path.getsize(file.name) == saved_size + len(bytes_to_write) @@ -189,12 +189,12 @@ def pr_none(): # Start a long running process so we have time to run tests on it before it finishes # Put the new process into a separate group so its signal are isolated from ours kwargs = {} - if sys.platform.startswith('win'): - command = 'timeout -t 5 /nobreak' - kwargs['creationflags'] = subprocess.CREATE_NEW_PROCESS_GROUP + if sys.platform.startswith("win"): + command = "timeout -t 5 /nobreak" + kwargs["creationflags"] = subprocess.CREATE_NEW_PROCESS_GROUP else: - command = 'sleep 5' - kwargs['start_new_session'] = True + command = "sleep 5" + kwargs["start_new_session"] = True proc = subprocess.Popen(command, shell=True, **kwargs) return cu.ProcReader(proc, None, None) @@ -207,7 +207,7 @@ def test_proc_reader_send_sigint(pr_none) -> None: ret_code = pr_none._proc.poll() # Make sure a SIGINT killed the process - if sys.platform.startswith('win'): + if sys.platform.startswith("win"): assert ret_code is not None else: assert ret_code == -signal.SIGINT @@ -226,7 +226,7 @@ def test_proc_reader_terminate(pr_none) -> None: assert wait_finish - wait_start < 3 ret_code = pr_none._proc.poll() - if sys.platform.startswith('win'): + if sys.platform.startswith("win"): assert ret_code is not None else: assert ret_code == -signal.SIGTERM @@ -249,22 +249,22 @@ def test_context_flag_exit_err(context_flag) -> None: def test_to_bool_str_true() -> None: - assert cu.to_bool('true') - assert cu.to_bool('True') - assert cu.to_bool('TRUE') - assert cu.to_bool('tRuE') + assert cu.to_bool("true") + assert cu.to_bool("True") + assert cu.to_bool("TRUE") + assert cu.to_bool("tRuE") def test_to_bool_str_false() -> None: - assert not cu.to_bool('false') - assert not cu.to_bool('False') - assert not cu.to_bool('FALSE') - assert not cu.to_bool('fAlSe') + assert not cu.to_bool("false") + assert not cu.to_bool("False") + assert not cu.to_bool("FALSE") + assert not cu.to_bool("fAlSe") def test_to_bool_str_invalid() -> None: with pytest.raises(ValueError): # noqa: PT011 - cu.to_bool('other') + cu.to_bool("other") def test_to_bool_bool() -> None: @@ -286,8 +286,8 @@ def test_to_bool_float() -> None: def test_find_editor_specified() -> None: - expected_editor = os.path.join('fake_dir', 'editor') - with mock.patch.dict(os.environ, {'EDITOR': expected_editor}): + expected_editor = os.path.join("fake_dir", "editor") + with mock.patch.dict(os.environ, {"EDITOR": expected_editor}): editor = cu.find_editor() assert editor == expected_editor @@ -298,7 +298,7 @@ def test_find_editor_not_specified() -> None: assert editor # Overwrite path env setting with invalid path, clear all other env vars so no editor should be found. - with mock.patch.dict(os.environ, {'PATH': 'fake_dir'}, clear=True): + with mock.patch.dict(os.environ, {"PATH": "fake_dir"}, clear=True): editor = cu.find_editor() assert editor is None @@ -320,16 +320,16 @@ def test_similarity_without_good_canididates() -> None: def test_similarity_overwrite_function() -> None: options = ["history", "test"] suggested_command = cu.suggest_similar("test", options) - assert suggested_command == 'test' + assert suggested_command == "test" def custom_similarity_function(s1, s2) -> float: - return 1.0 if 'history' in (s1, s2) else 0.0 + return 1.0 if "history" in (s1, s2) else 0.0 suggested_command = cu.suggest_similar("test", options, similarity_function_to_use=custom_similarity_function) - assert suggested_command == 'history' + assert suggested_command == "history" suggested_command = cu.suggest_similar("history", options, similarity_function_to_use=custom_similarity_function) - assert suggested_command == 'history' + assert suggested_command == "history" suggested_command = cu.suggest_similar("test", ["test"], similarity_function_to_use=custom_similarity_function) assert suggested_command is None @@ -357,7 +357,7 @@ def foo(x: int) -> str: param_ann, ret_ann = cu.get_types(foo) assert ret_ann is str param_name, param_value = next(iter(param_ann.items())) - assert param_name == 'x' + assert param_name == "x" assert param_value is int @@ -372,5 +372,5 @@ def bar(self, x: bool) -> None: assert ret_ann is None assert len(param_ann) == 1 param_name, param_value = next(iter(param_ann.items())) - assert param_name == 'x' + assert param_name == "x" assert param_value is bool