diff --git a/cmd2/__init__.py b/cmd2/__init__.py index abd6d59d..4b9f65ea 100644 --- a/cmd2/__init__.py +++ b/cmd2/__init__.py @@ -11,8 +11,6 @@ # package is not installed pass -from typing import List - from . import plugin from .ansi import ( Bg, @@ -64,7 +62,7 @@ categorize, ) -__all__: List[str] = [ +__all__: list[str] = [ 'COMMAND_NAME', 'DEFAULT_SHORTCUTS', # ANSI Exports diff --git a/cmd2/ansi.py b/cmd2/ansi.py index c0a3a2f1..772e9fff 100644 --- a/cmd2/ansi.py +++ b/cmd2/ansi.py @@ -12,7 +12,6 @@ from typing import ( IO, Any, - List, Optional, cast, ) @@ -992,11 +991,11 @@ def style( :raises: TypeError if bg isn't None or a subclass of BgColor :return: the stylized string """ - # List of strings that add style - additions: List[AnsiSequence] = [] + # list of strings that add style + additions: list[AnsiSequence] = [] - # List of strings that remove style - removals: List[AnsiSequence] = [] + # list of strings that remove style + removals: list[AnsiSequence] = [] # Process the style settings if fg is not None: diff --git a/cmd2/argparse_completer.py b/cmd2/argparse_completer.py index 1e4cd8b4..739f2e0d 100644 --- a/cmd2/argparse_completer.py +++ b/cmd2/argparse_completer.py @@ -14,8 +14,6 @@ ) from typing import ( TYPE_CHECKING, - Dict, - List, Optional, Type, Union, @@ -172,7 +170,7 @@ class ArgparseCompleter: """Automatic command line tab completion based on argparse parameters""" def __init__( - self, parser: argparse.ArgumentParser, cmd2_app: 'Cmd', *, parent_tokens: Optional[Dict[str, List[str]]] = None + self, parser: argparse.ArgumentParser, cmd2_app: 'Cmd', *, parent_tokens: Optional[dict[str, list[str]]] = None ) -> None: """ Create an ArgparseCompleter @@ -213,8 +211,8 @@ def __init__( self._subcommand_action = action def complete( - self, text: str, line: str, begidx: int, endidx: int, tokens: List[str], *, cmd_set: Optional[CommandSet] = None - ) -> List[str]: + self, text: str, line: str, begidx: int, endidx: int, tokens: list[str], *, cmd_set: Optional[CommandSet] = None + ) -> list[str]: """ Complete text using argparse metadata @@ -245,13 +243,13 @@ def complete( flag_arg_state: Optional[_ArgumentState] = None # Non-reusable flags that we've parsed - matched_flags: List[str] = [] + matched_flags: list[str] = [] # Keeps track of arguments we've seen and any tokens they consumed - consumed_arg_values: Dict[str, List[str]] = dict() # dict(arg_name -> List[tokens]) + consumed_arg_values: dict[str, list[str]] = dict() # dict(arg_name -> l[tokens]) # Completed mutually exclusive groups - completed_mutex_groups: Dict[argparse._MutuallyExclusiveGroup, argparse.Action] = dict() + completed_mutex_groups: dict[argparse._MutuallyExclusiveGroup, argparse.Action] = dict() def consume_argument(arg_state: _ArgumentState) -> None: """Consuming token as an argument""" @@ -507,7 +505,7 @@ def update_mutex_groups(arg_action: argparse.Action) -> None: return completion_results - def _complete_flags(self, text: str, line: str, begidx: int, endidx: int, matched_flags: List[str]) -> List[str]: + def _complete_flags(self, text: str, line: str, begidx: int, endidx: int, matched_flags: list[str]) -> list[str]: """Tab completion routine for a parsers unused flags""" # Build a list of flags that can be tab completed @@ -524,7 +522,7 @@ def _complete_flags(self, text: str, line: str, begidx: int, endidx: int, matche matches = self._cmd2_app.basic_complete(text, line, begidx, endidx, match_against) # Build a dictionary linking actions with their matched flag names - matched_actions: Dict[argparse.Action, List[str]] = dict() + matched_actions: dict[argparse.Action, list[str]] = dict() for flag in matches: action = self._flag_to_action[flag] matched_actions.setdefault(action, []) @@ -541,14 +539,14 @@ def _complete_flags(self, text: str, line: str, begidx: int, endidx: int, matche return matches - def _format_completions(self, arg_state: _ArgumentState, completions: Union[List[str], List[CompletionItem]]) -> List[str]: + def _format_completions(self, arg_state: _ArgumentState, completions: Union[list[str], list[CompletionItem]]) -> list[str]: """Format CompletionItems into hint table""" # Nothing to do if we don't have at least 2 completions which are all CompletionItems if len(completions) < 2 or not all(isinstance(c, CompletionItem) for c in completions): - return cast(List[str], completions) + return cast(list[str], completions) - completion_items = cast(List[CompletionItem], completions) + completion_items = cast(list[CompletionItem], completions) # Check if the data being completed have a numerical type all_nums = all(isinstance(c.orig_value, numbers.Number) for c in completion_items) @@ -616,9 +614,9 @@ def _format_completions(self, arg_state: _ArgumentState, completions: Union[List self._cmd2_app.formatted_completions = hint_table.generate_table(table_data, row_spacing=0) # Return sorted list of completions - return cast(List[str], completions) + return cast(list[str], completions) - def complete_subcommand_help(self, text: str, line: str, begidx: int, endidx: int, tokens: List[str]) -> List[str]: + def complete_subcommand_help(self, text: str, line: str, begidx: int, endidx: int, tokens: list[str]) -> list[str]: """ Supports cmd2's help command in the completion of subcommand names :param text: the string prefix we are attempting to match (all matches must begin with it) @@ -626,7 +624,7 @@ def complete_subcommand_help(self, text: str, line: str, begidx: int, endidx: in :param begidx: the beginning index of the prefix text :param endidx: the ending index of the prefix text :param tokens: arguments passed to command/subcommand - :return: List of subcommand completions + :return: list of subcommand completions """ # If our parser has subcommands, we must examine the tokens and check if they are subcommands # If so, we will let the subcommand's parser handle the rest of the tokens via another ArgparseCompleter. @@ -645,7 +643,7 @@ def complete_subcommand_help(self, text: str, line: str, begidx: int, endidx: in break return [] - def format_help(self, tokens: List[str]) -> str: + def format_help(self, tokens: list[str]) -> str: """ Supports cmd2's help command in the retrieval of help text :param tokens: arguments passed to help command @@ -672,17 +670,17 @@ def _complete_arg( begidx: int, endidx: int, arg_state: _ArgumentState, - consumed_arg_values: Dict[str, List[str]], + consumed_arg_values: dict[str, list[str]], *, cmd_set: Optional[CommandSet] = None, - ) -> List[str]: + ) -> list[str]: """ Tab completion routine for an argparse argument :return: list of completions :raises: CompletionError if the completer or choices function this calls raises one """ # Check if the arg provides choices to the user - arg_choices: Union[List[str], ChoicesCallable] + arg_choices: Union[list[str], ChoicesCallable] if arg_state.action.choices is not None: arg_choices = list(arg_state.action.choices) if not arg_choices: @@ -739,7 +737,7 @@ def _complete_arg( # Otherwise use basic_complete on the choices else: # Check if the choices come from a function - completion_items: List[str] = [] + completion_items: list[str] = [] if isinstance(arg_choices, ChoicesCallable): if not arg_choices.is_completer: choices_func = arg_choices.choices_provider diff --git a/cmd2/argparse_custom.py b/cmd2/argparse_custom.py index 010776c5..774c6f6e 100644 --- a/cmd2/argparse_custom.py +++ b/cmd2/argparse_custom.py @@ -250,15 +250,11 @@ def my_completer(self, text, line, begidx, endidx, arg_tokens) TYPE_CHECKING, Any, Callable, - Dict, Iterable, - List, NoReturn, Optional, Protocol, Sequence, - Set, - Tuple, Type, Union, cast, @@ -350,7 +346,7 @@ class ChoicesProviderFuncBase(Protocol): Function that returns a list of choices in support of tab completion """ - def __call__(self) -> List[str]: ... # pragma: no cover + def __call__(self) -> list[str]: ... # pragma: no cover @runtime_checkable @@ -359,7 +355,7 @@ class ChoicesProviderFuncWithTokens(Protocol): Function that returns a list of choices in support of tab completion and accepts a dictionary of prior arguments. """ - def __call__(self, *, arg_tokens: Dict[str, List[str]] = {}) -> List[str]: ... # pragma: no cover + def __call__(self, *, arg_tokens: dict[str, list[str]] = {}) -> list[str]: ... # pragma: no cover ChoicesProviderFunc = Union[ChoicesProviderFuncBase, ChoicesProviderFuncWithTokens] @@ -377,7 +373,7 @@ def __call__( line: str, begidx: int, endidx: int, - ) -> List[str]: ... # pragma: no cover + ) -> list[str]: ... # pragma: no cover @runtime_checkable @@ -394,8 +390,8 @@ def __call__( begidx: int, endidx: int, *, - arg_tokens: Dict[str, List[str]] = {}, - ) -> List[str]: ... # pragma: no cover + arg_tokens: dict[str, list[str]] = {}, + ) -> list[str]: ... # pragma: no cover CompleterFunc = Union[CompleterFuncBase, CompleterFuncWithTokens] @@ -598,7 +594,7 @@ def _action_set_descriptive_header(self: argparse.Action, descriptive_header: Op ############################################################################################################ # Patch argparse.Action with accessors for nargs_range attribute ############################################################################################################ -def _action_get_nargs_range(self: argparse.Action) -> Optional[Tuple[int, Union[int, float]]]: +def _action_get_nargs_range(self: argparse.Action) -> Optional[tuple[int, Union[int, float]]]: """ Get the nargs_range attribute of an argparse Action. @@ -609,13 +605,13 @@ def _action_get_nargs_range(self: argparse.Action) -> Optional[Tuple[int, Union[ :param self: argparse Action being queried :return: The value of nargs_range or None if attribute does not exist """ - return cast(Optional[Tuple[int, Union[int, float]]], getattr(self, ATTR_NARGS_RANGE, None)) + return cast(Optional[tuple[int, Union[int, float]]], getattr(self, ATTR_NARGS_RANGE, None)) setattr(argparse.Action, 'get_nargs_range', _action_get_nargs_range) -def _action_set_nargs_range(self: argparse.Action, nargs_range: Optional[Tuple[int, Union[int, float]]]) -> None: +def _action_set_nargs_range(self: argparse.Action, nargs_range: Optional[tuple[int, Union[int, float]]]) -> None: """ Set the nargs_range attribute of an argparse Action. @@ -673,7 +669,7 @@ def _action_set_suppress_tab_hint(self: argparse.Action, suppress_tab_hint: bool # Allow developers to add custom action attributes ############################################################################################################ -CUSTOM_ACTION_ATTRIBS: Set[str] = set() +CUSTOM_ACTION_ATTRIBS: set[str] = set() _CUSTOM_ATTRIB_PFX = '_attr_' @@ -746,7 +742,7 @@ def _action_set_custom_parameter(self: argparse.Action, value: Any) -> None: def _add_argument_wrapper( self: argparse._ActionsContainer, *args: Any, - nargs: Union[int, str, Tuple[int], Tuple[int, int], Tuple[int, float], None] = None, + nargs: Union[int, str, tuple[int], tuple[int, int], tuple[int, float], None] = None, choices_provider: Optional[ChoicesProviderFunc] = None, completer: Optional[CompleterFunc] = None, suppress_tab_hint: bool = False, @@ -797,7 +793,7 @@ def _add_argument_wrapper( nargs_range = None if nargs is not None: - nargs_adjusted: Union[int, str, Tuple[int], Tuple[int, int], Tuple[int, float], None] + nargs_adjusted: Union[int, str, tuple[int], tuple[int, int], tuple[int, float], None] # Check if nargs was given as a range if isinstance(nargs, tuple): # Handle 1-item tuple by setting max to INFINITY @@ -847,7 +843,7 @@ def _add_argument_wrapper( kwargs['nargs'] = nargs_adjusted # Extract registered custom keyword arguments - custom_attribs: Dict[str, Any] = {} + custom_attribs: dict[str, Any] = {} for keyword, value in kwargs.items(): if keyword in CUSTOM_ACTION_ATTRIBS: custom_attribs[keyword] = value @@ -1124,9 +1120,9 @@ def _format_usage( # End cmd2 customization # helper for wrapping lines - def get_lines(parts: List[str], indent: str, prefix: Optional[str] = None) -> List[str]: - lines: List[str] = [] - line: List[str] = [] + def get_lines(parts: list[str], indent: str, prefix: Optional[str] = None) -> list[str]: + lines: list[str] = [] + line: list[str] = [] if prefix is not None: line_len = len(prefix) - 1 else: @@ -1188,7 +1184,7 @@ def _format_action_invocation(self, action: argparse.Action) -> str: return metavar else: - parts: List[str] = [] + parts: list[str] = [] # if the Optional doesn't take a value, format is: # -s, --long @@ -1209,8 +1205,8 @@ def _format_action_invocation(self, action: argparse.Action) -> str: def _determine_metavar( self, action: argparse.Action, - default_metavar: Union[str, Tuple[str, ...]], - ) -> Union[str, Tuple[str, ...]]: + default_metavar: Union[str, tuple[str, ...]], + ) -> Union[str, tuple[str, ...]]: """Custom method to determine what to use as the metavar value of an action""" if action.metavar is not None: result = action.metavar @@ -1226,11 +1222,11 @@ def _determine_metavar( def _metavar_formatter( self, action: argparse.Action, - default_metavar: Union[str, Tuple[str, ...]], - ) -> Callable[[int], Tuple[str, ...]]: + default_metavar: Union[str, tuple[str, ...]], + ) -> Callable[[int], tuple[str, ...]]: metavar = self._determine_metavar(action, default_metavar) - def format(tuple_size: int) -> Tuple[str, ...]: + def format(tuple_size: int) -> tuple[str, ...]: if isinstance(metavar, tuple): return metavar else: @@ -1238,7 +1234,7 @@ def format(tuple_size: int) -> Tuple[str, ...]: return format - def _format_args(self, action: argparse.Action, default_metavar: Union[str, Tuple[str, ...]]) -> str: + def _format_args(self, action: argparse.Action, default_metavar: Union[str, tuple[str, ...]]) -> str: """Customized to handle ranged nargs and make other output less verbose""" metavar = self._determine_metavar(action, default_metavar) metavar_formatter = self._metavar_formatter(action, default_metavar) diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py index 5cbef6cb..7661b0ba 100644 --- a/cmd2/cmd2.py +++ b/cmd2/cmd2.py @@ -59,14 +59,10 @@ TYPE_CHECKING, Any, Callable, - Dict, Iterable, - List, Mapping, Optional, - Set, TextIO, - Tuple, Type, TypeVar, Union, @@ -199,7 +195,7 @@ class _SavedCmd2Env: def __init__(self) -> None: self.readline_settings = _SavedReadlineSettings() self.readline_module: Optional[ModuleType] = None - self.history: List[str] = [] + self.history: list[str] = [] self.sys_stdout: Optional[TextIO] = None self.sys_stdin: Optional[TextIO] = None @@ -244,11 +240,11 @@ def __init__( include_py: bool = False, include_ipy: bool = False, allow_cli_args: bool = True, - transcript_files: Optional[List[str]] = None, + transcript_files: Optional[list[str]] = None, allow_redirection: bool = True, - multiline_commands: Optional[List[str]] = None, - terminators: Optional[List[str]] = None, - shortcuts: Optional[Dict[str, str]] = None, + multiline_commands: Optional[list[str]] = None, + terminators: Optional[list[str]] = None, + shortcuts: Optional[dict[str, str]] = None, command_sets: Optional[Iterable[CommandSet]] = None, auto_load_commands: bool = True, allow_clipboard: bool = True, @@ -338,12 +334,12 @@ def __init__( self.max_completion_items = 50 # A dictionary mapping settable names to their Settable instance - self._settables: Dict[str, Settable] = dict() + self._settables: dict[str, Settable] = dict() self._always_prefix_settables: bool = False # CommandSet containers - self._installed_command_sets: Set[CommandSet] = set() - self._cmd_to_command_sets: Dict[str, CommandSet] = {} + self._installed_command_sets: set[CommandSet] = set() + self._cmd_to_command_sets: dict[str, CommandSet] = {} self.build_settables() @@ -365,13 +361,13 @@ def __init__( self.exclude_from_history = ['eof', 'history'] # Keeps track of typed command history in the Python shell - self._py_history: List[str] = [] + self._py_history: list[str] = [] # The name by which Python environments refer to the PyBridge to call app commands self.py_bridge_name = 'app' # Defines app-specific variables/functions available in Python shells and pyscripts - self.py_locals: Dict[str, Any] = dict() + self.py_locals: dict[str, Any] = dict() # True if running inside a Python shell or pyscript, False otherwise self._in_py = False @@ -384,7 +380,7 @@ def __init__( self.last_result: Any = None # Used by run_script command to store current script dir as a LIFO queue to support _relative_run_script command - self._script_dir: List[str] = [] + self._script_dir: list[str] = [] # Context manager used to protect critical sections in the main thread from stopping due to a KeyboardInterrupt self.sigint_protection = utils.ContextFlag() @@ -415,7 +411,7 @@ def __init__( self.broken_pipe_warning = '' # Commands that will run at the beginning of the command loop - self._startup_commands: List[str] = [] + self._startup_commands: list[str] = [] # If a startup script is provided and exists, then execute it in the startup commands if startup_script: @@ -427,7 +423,7 @@ def __init__( self._startup_commands.append(script_cmd) # Transcript files to run instead of interactive command loop - self._transcript_files: Optional[List[str]] = None + self._transcript_files: Optional[list[str]] = None # Check for command line args if allow_cli_args: @@ -470,7 +466,7 @@ def __init__( # Commands that have been disabled from use. This is to support commands that are only available # during specific states of the application. This dictionary's keys are the command names and its # values are DisabledCommand objects. - self.disabled_commands: Dict[str, DisabledCommand] = dict() + self.disabled_commands: dict[str, DisabledCommand] = dict() # If any command has been categorized, then all other commands that haven't been categorized # will display under this section in the help output. @@ -508,7 +504,7 @@ def __init__( self.formatted_completions = '' # Used by complete() for readline tab completion - self.completion_matches: List[str] = [] + self.completion_matches: list[str] = [] # Use this list if you need to display tab completion suggestions that are different than the actual text # of the matches. For instance, if you are completing strings that contain a common delimiter and you only @@ -516,7 +512,7 @@ def __init__( # still must be returned from your completer function. For an example, look at path_complete() which # uses this to show only the basename of paths as the suggestions. delimiter_complete() also populates # this list. These are ignored if self.formatted_completions is populated. - self.display_matches: List[str] = [] + self.display_matches: list[str] = [] # Used by functions like path_complete() and delimiter_complete() to properly # quote matches that are completed in a delimited fashion @@ -528,7 +524,7 @@ def __init__( self.matches_sorted = False # Command parsers for this Cmd instance. - self._command_parsers: Dict[str, argparse.ArgumentParser] = {} + self._command_parsers: dict[str, argparse.ArgumentParser] = {} # Locates the command parser template or factory and creates an instance-specific parser for command in self.get_all_commands(): @@ -562,7 +558,7 @@ def __init__( # the current command being executed self.current_command: Optional[Statement] = None - def find_commandsets(self, commandset_type: Type[CommandSet], *, subclass_match: bool = False) -> List[CommandSet]: + def find_commandsets(self, commandset_type: Type[CommandSet], *, subclass_match: bool = False) -> list[CommandSet]: """ Find all CommandSets that match the provided CommandSet type. By default, locates a CommandSet that is an exact type match but may optionally return all CommandSets that @@ -591,7 +587,7 @@ def _autoload_commands(self) -> None: all_commandset_defs = CommandSet.__subclasses__() existing_commandset_types = [type(command_set) for command_set in self._installed_command_sets] - def load_commandset_by_type(commandset_types: List[Type[CommandSet]]) -> None: + def load_commandset_by_type(commandset_types: list[Type[CommandSet]]) -> None: for cmdset_type in commandset_types: # check if the type has sub-classes. We will only auto-load leaf class types. subclasses = cmdset_type.__subclasses__() @@ -635,7 +631,7 @@ def register_command_set(self, cmdset: CommandSet) -> None: cmdset.on_register(self) methods = cast( - List[Tuple[str, Callable[..., Any]]], + list[tuple[str, Callable[..., Any]]], inspect.getmembers( cmdset, predicate=lambda meth: isinstance(meth, Callable) # type: ignore[arg-type] @@ -776,7 +772,7 @@ def unregister_command_set(self, cmdset: CommandSet) -> None: cmdset.on_unregister() self._unregister_subcommands(cmdset) - methods: List[Tuple[str, Callable[[Any], Any]]] = inspect.getmembers( + methods: list[tuple[str, Callable[[Any], Any]]] = inspect.getmembers( cmdset, predicate=lambda meth: isinstance(meth, Callable) # type: ignore[arg-type] and hasattr(meth, '__name__') @@ -807,7 +803,7 @@ def unregister_command_set(self, cmdset: CommandSet) -> None: self._installed_command_sets.remove(cmdset) def _check_uninstallable(self, cmdset: CommandSet) -> None: - methods: List[Tuple[str, Callable[[Any], Any]]] = inspect.getmembers( + methods: list[tuple[str, Callable[[Any], Any]]] = inspect.getmembers( cmdset, predicate=lambda meth: isinstance(meth, Callable) # type: ignore[arg-type] and hasattr(meth, '__name__') @@ -881,7 +877,7 @@ def _register_subcommands(self, cmdset: Union[CommandSet, 'Cmd']) -> None: f"Could not find argparser for command '{command_name}' needed by subcommand: {str(method)}" ) - def find_subcommand(action: argparse.ArgumentParser, subcmd_names: List[str]) -> argparse.ArgumentParser: + def find_subcommand(action: argparse.ArgumentParser, subcmd_names: list[str]) -> argparse.ArgumentParser: if not subcmd_names: return action cur_subcmd = subcmd_names.pop(0) @@ -1059,7 +1055,7 @@ def remove_settable(self, name: str) -> None: def build_settables(self) -> None: """Create the dictionary of user-settable parameters""" - def get_allow_style_choices(cli_self: Cmd) -> List[str]: + def get_allow_style_choices(cli_self: Cmd) -> list[str]: """Used to tab complete allow_style values""" return [val.name.lower() for val in ansi.AllowStyle] @@ -1383,7 +1379,7 @@ def _reset_completion_defaults(self) -> None: elif rl_type == RlType.PYREADLINE: readline.rl.mode._display_completions = self._display_matches_pyreadline - def tokens_for_completion(self, line: str, begidx: int, endidx: int) -> Tuple[List[str], List[str]]: + def tokens_for_completion(self, line: str, begidx: int, endidx: int) -> tuple[list[str], list[str]]: """Used by tab completion functions to get all tokens through the one being completed. :param line: the current input line with leading whitespace removed @@ -1454,7 +1450,7 @@ def basic_complete( begidx: int, endidx: int, match_against: Iterable[str], - ) -> List[str]: + ) -> list[str]: """ Basic tab completion function that matches against a list of strings without considering line contents or cursor position. The args required by this function are defined in the header of Python's cmd.py. @@ -1476,7 +1472,7 @@ def delimiter_complete( endidx: int, match_against: Iterable[str], delimiter: str, - ) -> List[str]: + ) -> list[str]: """ Performs tab completion against a list but each match is split on a delimiter and only the portion of the match being tab completed is shown as the completion suggestions. @@ -1542,10 +1538,10 @@ def flag_based_complete( line: str, begidx: int, endidx: int, - flag_dict: Dict[str, Union[Iterable[str], CompleterFunc]], + flag_dict: dict[str, Union[Iterable[str], CompleterFunc]], *, all_else: Union[None, Iterable[str], CompleterFunc] = None, - ) -> List[str]: + ) -> list[str]: """Tab completes based on a particular flag preceding the token being completed. :param text: the string prefix we are attempting to match (all matches must begin with it) @@ -1594,7 +1590,7 @@ def index_based_complete( index_dict: Mapping[int, Union[Iterable[str], CompleterFunc]], *, all_else: Optional[Union[Iterable[str], CompleterFunc]] = None, - ) -> List[str]: + ) -> list[str]: """Tab completes based on a fixed position in the input string. :param text: the string prefix we are attempting to match (all matches must begin with it) @@ -1639,7 +1635,7 @@ def index_based_complete( def path_complete( self, text: str, line: str, begidx: int, endidx: int, *, path_filter: Optional[Callable[[str], bool]] = None - ) -> List[str]: + ) -> list[str]: """Performs completion of local file system paths :param text: the string prefix we are attempting to match (all matches must begin with it) @@ -1653,7 +1649,7 @@ def path_complete( """ # Used to complete ~ and ~user strings - def complete_users() -> List[str]: + def complete_users() -> list[str]: users = [] # Windows lacks the pwd module so we can't get a list of users. @@ -1780,7 +1776,7 @@ def complete_users() -> List[str]: return matches - def shell_cmd_complete(self, text: str, line: str, begidx: int, endidx: int, *, complete_blank: bool = False) -> List[str]: + def shell_cmd_complete(self, text: str, line: str, begidx: int, endidx: int, *, complete_blank: bool = False) -> list[str]: """Performs completion of executables either in a user's path or a given path :param text: the string prefix we are attempting to match (all matches must begin with it) @@ -1805,7 +1801,7 @@ def shell_cmd_complete(self, text: str, line: str, begidx: int, endidx: int, *, text, line, begidx, endidx, path_filter=lambda path: os.path.isdir(path) or os.access(path, os.X_OK) ) - def _redirect_complete(self, text: str, line: str, begidx: int, endidx: int, compfunc: CompleterFunc) -> List[str]: + def _redirect_complete(self, text: str, line: str, begidx: int, endidx: int, compfunc: CompleterFunc) -> list[str]: """Called by complete() as the first tab completion function for all commands It determines if it should tab complete for redirection (|, >, >>) or use the completer function for the current command @@ -1886,7 +1882,7 @@ def _redirect_complete(self, text: str, line: str, begidx: int, endidx: int, com return compfunc(text, line, begidx, endidx) @staticmethod - def _pad_matches_to_display(matches_to_display: List[str]) -> Tuple[List[str], int]: # pragma: no cover + def _pad_matches_to_display(matches_to_display: list[str]) -> tuple[list[str], int]: # pragma: no cover """Adds padding to the matches being displayed as tab completion suggestions. The default padding of readline/pyreadine is small and not visually appealing especially if matches have spaces. It appears very squished together. @@ -1908,7 +1904,7 @@ def _pad_matches_to_display(matches_to_display: List[str]) -> Tuple[List[str], i return [cur_match + padding for cur_match in matches_to_display], len(padding) def _display_matches_gnu_readline( - self, substitution: str, matches: List[str], longest_match_length: int + self, substitution: str, matches: list[str], longest_match_length: int ) -> None: # pragma: no cover """Prints a match list using GNU readline's rl_display_match_list() @@ -1956,7 +1952,7 @@ def _display_matches_gnu_readline( # rl_display_match_list() expects matches to be in argv format where # substitution is the first element, followed by the matches, and then a NULL. - strings_array = cast(List[Optional[bytes]], (ctypes.c_char_p * (1 + len(encoded_matches) + 1))()) + strings_array = cast(list[Optional[bytes]], (ctypes.c_char_p * (1 + len(encoded_matches) + 1))()) # Copy in the encoded strings and add a NULL to the end strings_array[0] = encoded_substitution @@ -1969,7 +1965,7 @@ def _display_matches_gnu_readline( # Redraw prompt and input line rl_force_redisplay() - def _display_matches_pyreadline(self, matches: List[str]) -> None: # pragma: no cover + def _display_matches_pyreadline(self, matches: list[str]) -> None: # pragma: no cover """Prints a match list using pyreadline3's _display_completions() :param matches: the tab completion matches to display @@ -2312,15 +2308,15 @@ def in_pyscript(self) -> bool: return self._in_py @property - def aliases(self) -> Dict[str, str]: + def aliases(self) -> dict[str, str]: """Read-only property to access the aliases stored in the StatementParser""" return self.statement_parser.aliases - def get_names(self) -> List[str]: + def get_names(self) -> list[str]: """Return an alphabetized list of names comprising the attributes of the cmd2 class instance.""" return dir(self) - def get_all_commands(self) -> List[str]: + def get_all_commands(self) -> list[str]: """Return a list of all commands""" return [ name[len(constants.COMMAND_FUNC_PREFIX) :] @@ -2328,7 +2324,7 @@ def get_all_commands(self) -> List[str]: if name.startswith(constants.COMMAND_FUNC_PREFIX) and callable(getattr(self, name)) ] - def get_visible_commands(self) -> List[str]: + def get_visible_commands(self) -> list[str]: """Return a list of commands that have not been hidden or disabled""" return [ command @@ -2339,9 +2335,9 @@ def get_visible_commands(self) -> List[str]: # Table displayed when tab completing aliases _alias_completion_table = SimpleTable([Column('Value', width=80)], divider_char=None) - def _get_alias_completion_items(self) -> List[CompletionItem]: + def _get_alias_completion_items(self) -> list[CompletionItem]: """Return list of alias names and values as CompletionItems""" - results: List[CompletionItem] = [] + results: list[CompletionItem] = [] for cur_key in self.aliases: row_data = [self.aliases[cur_key]] @@ -2352,9 +2348,9 @@ def _get_alias_completion_items(self) -> List[CompletionItem]: # Table displayed when tab completing Settables _settable_completion_table = SimpleTable([Column('Value', width=30), Column('Description', width=60)], divider_char=None) - def _get_settable_completion_items(self) -> List[CompletionItem]: + def _get_settable_completion_items(self) -> list[CompletionItem]: """Return list of Settable names, values, and descriptions as CompletionItems""" - results: List[CompletionItem] = [] + results: list[CompletionItem] = [] for cur_key in self.settables: row_data = [self.settables[cur_key].get_value(), self.settables[cur_key].description] @@ -2362,13 +2358,13 @@ def _get_settable_completion_items(self) -> List[CompletionItem]: return results - def _get_commands_and_aliases_for_completion(self) -> List[str]: + def _get_commands_and_aliases_for_completion(self) -> list[str]: """Return a list of visible commands and aliases for tab completion""" visible_commands = set(self.get_visible_commands()) alias_names = set(self.aliases) return list(visible_commands | alias_names) - def get_help_topics(self) -> List[str]: + def get_help_topics(self) -> list[str]: """Return a list of help topics""" all_topics = [ name[len(constants.HELP_FUNC_PREFIX) :] @@ -2471,7 +2467,7 @@ def postloop(self) -> None: """ pass - def parseline(self, line: str) -> Tuple[str, str, str]: + def parseline(self, line: str) -> tuple[str, str, str]: """Parse the line into a command name and a string containing the arguments. NOTE: This is an override of a parent class method. It is only used by other parent class methods. @@ -2634,7 +2630,7 @@ def _run_cmdfinalization_hooks(self, stop: bool, statement: Optional[Statement]) def runcmds_plus_hooks( self, - cmds: Union[List[HistoryItem], List[str]], + cmds: Union[list[HistoryItem], list[str]], *, add_to_history: bool = True, stop_on_keyboard_interrupt: bool = False, @@ -2793,7 +2789,7 @@ def _redirect_output(self, statement: Statement) -> utils.RedirectionSavedState: # 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] = dict() + kwargs: dict[str, Any] = dict() if sys.platform == 'win32': kwargs['creationflags'] = subprocess.CREATE_NEW_PROCESS_GROUP else: @@ -2992,7 +2988,7 @@ def read_input( self, prompt: str, *, - history: Optional[List[str]] = None, + history: Optional[list[str]] = None, completion_mode: utils.CompletionMode = utils.CompletionMode.NONE, preserve_quotes: bool = False, choices: Optional[Iterable[Any]] = None, @@ -3033,7 +3029,7 @@ def read_input( """ readline_configured = False saved_completer: Optional[CompleterFunc] = None - saved_history: Optional[List[str]] = None + saved_history: Optional[list[str]] = None def configure_readline() -> None: """Configure readline tab completion and history""" @@ -3419,7 +3415,7 @@ def _build_alias_list_parser() -> Cmd2ArgumentParser: @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] + self.last_result = {} # dict[alias_name, alias_value] tokens_to_quote = constants.REDIRECTION_TOKENS tokens_to_quote.extend(self.statement_parser.terminators) @@ -3429,7 +3425,7 @@ def _alias_list(self, args: argparse.Namespace) -> None: else: to_list = sorted(self.aliases, key=self.default_sort_key) - not_found: List[str] = [] + not_found: list[str] = [] for name in to_list: if name not in self.aliases: not_found.append(name) @@ -3451,7 +3447,7 @@ def _alias_list(self, args: argparse.Namespace) -> None: for name in not_found: self.perror(f"Alias '{name}' not found") - def complete_help_command(self, text: str, line: str, begidx: int, endidx: int) -> List[str]: + def complete_help_command(self, text: str, line: str, begidx: int, endidx: int) -> list[str]: """Completes the command argument of help""" # Complete token against topics and visible commands @@ -3461,8 +3457,8 @@ def complete_help_command(self, text: str, line: str, begidx: int, endidx: int) return self.basic_complete(text, line, begidx, endidx, strs_to_match) def complete_help_subcommands( - self, text: str, line: str, begidx: int, endidx: int, arg_tokens: Dict[str, List[str]] - ) -> List[str]: + self, text: str, line: str, begidx: int, endidx: int, arg_tokens: dict[str, list[str]] + ) -> list[str]: """Completes the subcommands argument of help""" # Make sure we have a command whose subcommands we will complete @@ -3545,7 +3541,7 @@ def do_help(self, args: argparse.Namespace) -> None: self.perror(err_msg, apply_style=False) self.last_result = False - def print_topics(self, header: str, cmds: Optional[List[str]], cmdlen: int, maxcol: int) -> None: + def print_topics(self, header: str, cmds: Optional[list[str]], cmdlen: int, maxcol: int) -> None: """ Print groups of commands and topics in columns and an optional header Override of cmd's print_topics() to handle headers with newlines, ANSI style sequences, and wide characters @@ -3563,7 +3559,7 @@ def print_topics(self, header: str, cmds: Optional[List[str]], cmdlen: int, maxc self.columnize(cmds, maxcol - 1) self.poutput() - def columnize(self, str_list: Optional[List[str]], display_width: int = 80) -> None: + def columnize(self, str_list: Optional[list[str]], display_width: int = 80) -> None: """Display a list of single-line strings as a compact set of columns. Override of cmd's columnize() to handle strings with ANSI style sequences and wide characters @@ -3639,14 +3635,14 @@ def _help_menu(self, verbose: bool = False) -> None: self.print_topics(self.misc_header, help_topics, 15, 80) self.print_topics(self.undoc_header, cmds_undoc, 15, 80) - def _build_command_info(self) -> Tuple[Dict[str, List[str]], List[str], List[str], List[str]]: + def _build_command_info(self) -> tuple[dict[str, list[str]], list[str], list[str], list[str]]: # Get a sorted list of help topics help_topics = sorted(self.get_help_topics(), key=self.default_sort_key) # Get a sorted list of visible command names visible_commands = sorted(self.get_visible_commands(), key=self.default_sort_key) - cmds_doc: List[str] = [] - cmds_undoc: List[str] = [] - cmds_cats: Dict[str, List[str]] = {} + cmds_doc: list[str] = [] + cmds_undoc: list[str] = [] + cmds_cats: dict[str, list[str]] = {} for command in visible_commands: func = self.cmd_func(command) has_help_func = False @@ -3669,7 +3665,7 @@ def _build_command_info(self) -> Tuple[Dict[str, List[str]], List[str], List[str cmds_undoc.append(command) return cmds_cats, cmds_doc, cmds_undoc, help_topics - def _print_topics(self, header: str, cmds: List[str], verbose: bool) -> None: + def _print_topics(self, header: str, cmds: list[str], verbose: bool) -> None: """Customized version of print_topics that can switch between verbose or traditional output""" import io @@ -3776,7 +3772,7 @@ def do_quit(self, _: argparse.Namespace) -> Optional[bool]: self.last_result = True return True - def select(self, opts: Union[str, List[str], List[Tuple[Any, Optional[str]]]], prompt: str = 'Your choice? ') -> Any: + def select(self, opts: Union[str, list[str], list[tuple[Any, Optional[str]]]], prompt: str = 'Your choice? ') -> Any: """Presents a numbered menu to the user. Modeled after the bash shell's SELECT. Returns the item chosen. @@ -3787,12 +3783,12 @@ def select(self, opts: Union[str, List[str], List[Tuple[Any, Optional[str]]]], p | a list of tuples -> interpreted as (value, text), so that the return value can differ from the text advertised to the user""" - local_opts: Union[List[str], List[Tuple[Any, Optional[str]]]] + local_opts: Union[list[str], list[tuple[Any, Optional[str]]]] if isinstance(opts, str): - local_opts = cast(List[Tuple[Any, Optional[str]]], list(zip(opts.split(), opts.split()))) + local_opts = cast(list[tuple[Any, Optional[str]]], list(zip(opts.split(), opts.split()))) else: local_opts = opts - fulloptions: List[Tuple[Any, Optional[str]]] = [] + fulloptions: list[tuple[Any, Optional[str]]] = [] for opt in local_opts: if isinstance(opt, str): fulloptions.append((opt, opt)) @@ -3826,8 +3822,8 @@ def select(self, opts: Union[str, List[str], List[Tuple[Any, Optional[str]]]], p self.poutput(f"'{response}' isn't a valid choice. Pick a number between 1 and {len(fulloptions)}:") def complete_set_value( - self, text: str, line: str, begidx: int, endidx: int, arg_tokens: Dict[str, List[str]] - ) -> List[str]: + self, text: str, line: str, begidx: int, endidx: int, arg_tokens: dict[str, list[str]] + ) -> list[str]: """Completes the value argument of set""" param = arg_tokens['param'][0] try: @@ -3933,7 +3929,7 @@ def do_set(self, args: argparse.Namespace) -> None: max_name_width = max([ansi.style_aware_wcswidth(param) for param in to_show]) max_name_width = max(max_name_width, ansi.style_aware_wcswidth(name_label)) - cols: List[Column] = [ + cols: list[Column] = [ Column(name_label, width=max_name_width), Column('Value', width=30), Column('Description', width=60), @@ -3943,7 +3939,7 @@ def do_set(self, args: argparse.Namespace) -> None: self.poutput(table.generate_header()) # Build the table and populate self.last_result - self.last_result = {} # Dict[settable_name, settable_value] + self.last_result = {} # dict[settable_name, settable_value] for param in sorted(to_show, key=self.default_sort_key): settable = self.settables[param] @@ -3968,7 +3964,7 @@ def do_shell(self, args: argparse.Namespace) -> None: import signal import subprocess - kwargs: Dict[str, Any] = dict() + kwargs: dict[str, Any] = dict() # Set OS-specific parameters if sys.platform.startswith('win'): @@ -4555,7 +4551,7 @@ def do_history(self, args: argparse.Namespace) -> Optional[bool]: self.last_result = history return None - def _get_history(self, args: argparse.Namespace) -> 'OrderedDict[int, HistoryItem]': + def _get_history(self, args: argparse.Namespace) -> OrderedDict[int, HistoryItem]: """If an argument was supplied, then retrieve partial contents of the history; otherwise retrieve entire history. This function returns a dictionary with history items keyed by their 1-based index in ascending order. @@ -4633,11 +4629,11 @@ def _initialize_history(self, hist_file: str) -> None: try: import lzma as decompress_lib - decompress_exceptions: Tuple[type[Exception]] = (decompress_lib.LZMAError,) + decompress_exceptions: tuple[type[Exception]] = (decompress_lib.LZMAError,) except ModuleNotFoundError: # pragma: no cover import bz2 as decompress_lib # type: ignore[no-redef] - decompress_exceptions: Tuple[type[Exception]] = (OSError, ValueError) # type: ignore[no-redef] + decompress_exceptions: tuple[type[Exception]] = (OSError, ValueError) # type: ignore[no-redef] try: history_json = decompress_lib.decompress(compressed_bytes).decode(encoding='utf-8') @@ -4694,7 +4690,7 @@ def _persist_history(self) -> None: def _generate_transcript( self, - history: Union[List[HistoryItem], List[str]], + history: Union[list[HistoryItem], list[str]], transcript_file: str, *, add_to_history: bool = True, @@ -4968,7 +4964,7 @@ def do__relative_run_script(self, args: argparse.Namespace) -> Optional[bool]: # self.last_result will be set by do_run_script() return self.do_run_script(utils.quote_string(relative_path)) - def _run_transcript_tests(self, transcript_paths: List[str]) -> None: + def _run_transcript_tests(self, transcript_paths: list[str]) -> None: """Runs transcript tests for provided file(s). This is called when either -t is provided on the command line or the transcript_files argument is provided @@ -5355,12 +5351,12 @@ def cmdloop(self, intro: Optional[str] = None) -> int: # type: ignore[override] ### def _initialize_plugin_system(self) -> None: """Initialize the plugin system""" - self._preloop_hooks: List[Callable[[], None]] = [] - self._postloop_hooks: List[Callable[[], None]] = [] - self._postparsing_hooks: List[Callable[[plugin.PostparsingData], plugin.PostparsingData]] = [] - self._precmd_hooks: List[Callable[[plugin.PrecommandData], plugin.PrecommandData]] = [] - self._postcmd_hooks: List[Callable[[plugin.PostcommandData], plugin.PostcommandData]] = [] - self._cmdfinalization_hooks: List[Callable[[plugin.CommandFinalizationData], plugin.CommandFinalizationData]] = [] + self._preloop_hooks: list[Callable[[], None]] = [] + self._postloop_hooks: list[Callable[[], None]] = [] + self._postparsing_hooks: list[Callable[[plugin.PostparsingData], plugin.PostparsingData]] = [] + self._precmd_hooks: list[Callable[[plugin.PrecommandData], plugin.PrecommandData]] = [] + self._postcmd_hooks: list[Callable[[plugin.PostcommandData], plugin.PostcommandData]] = [] + self._cmdfinalization_hooks: list[Callable[[plugin.CommandFinalizationData], plugin.CommandFinalizationData]] = [] @classmethod def _validate_callable_param_count(cls, func: Callable[..., Any], count: int) -> None: @@ -5494,7 +5490,7 @@ def _resolve_func_self( else: # Search all registered CommandSets func_self = None - candidate_sets: List[CommandSet] = [] + candidate_sets: list[CommandSet] = [] for installed_cmd_set in self._installed_command_sets: if type(installed_cmd_set) == func_class: # noqa: E721 # Case 2: CommandSet is an exact type match for the function's CommandSet diff --git a/cmd2/command_definition.py b/cmd2/command_definition.py index 1c1ff0cf..f6d69322 100644 --- a/cmd2/command_definition.py +++ b/cmd2/command_definition.py @@ -6,7 +6,6 @@ from typing import ( TYPE_CHECKING, Callable, - Dict, Mapping, Optional, Type, @@ -97,7 +96,7 @@ def __init__(self) -> None: # accessed by child classes using the self._cmd property. self.__cmd_internal: Optional[cmd2.Cmd] = None - self._settables: Dict[str, Settable] = {} + self._settables: dict[str, Settable] = {} self._settable_prefix = self.__class__.__name__ @property diff --git a/cmd2/decorators.py b/cmd2/decorators.py index 72a675f9..da9a7ba2 100644 --- a/cmd2/decorators.py +++ b/cmd2/decorators.py @@ -6,11 +6,8 @@ TYPE_CHECKING, Any, Callable, - Dict, - List, Optional, Sequence, - Tuple, Type, TypeVar, Union, @@ -77,7 +74,7 @@ def cat_decorator(func: CommandFunc) -> CommandFunc: # 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['cmd2.Cmd', Union[Statement, str]]: +def _parse_positionals(args: tuple[Any, ...]) -> tuple['cmd2.Cmd', Union[Statement, str]]: """ Helper function for cmd2 decorators to 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. @@ -101,7 +98,7 @@ def _parse_positionals(args: Tuple[Any, ...]) -> Tuple['cmd2.Cmd', Union[Stateme raise TypeError('Expected arguments: cmd: cmd2.Cmd, statement: Union[Statement, str] Not found') # pragma: no cover -def _arg_swap(args: Union[Sequence[Any]], search_arg: Any, *replace_arg: Any) -> List[Any]: +def _arg_swap(args: Union[Sequence[Any]], search_arg: Any, *replace_arg: Any) -> list[Any]: """ Helper function for cmd2 decorators to swap the Statement parameter with one or more decorator-specific parameters @@ -118,13 +115,13 @@ def _arg_swap(args: Union[Sequence[Any]], search_arg: Any, *replace_arg: Any) -> #: Function signature for an Command Function that accepts a pre-processed argument list from user input #: and optionally returns a boolean -ArgListCommandFuncOptionalBoolReturn = Callable[[CommandParent, List[str]], Optional[bool]] +ArgListCommandFuncOptionalBoolReturn = Callable[[CommandParent, list[str]], Optional[bool]] #: Function signature for an Command Function that accepts a pre-processed argument list from user input #: and returns a boolean -ArgListCommandFuncBoolReturn = Callable[[CommandParent, List[str]], bool] +ArgListCommandFuncBoolReturn = Callable[[CommandParent, list[str]], bool] #: Function signature for an Command Function that accepts a pre-processed argument list from user input #: and returns Nothing -ArgListCommandFuncNoneReturn = Callable[[CommandParent, List[str]], None] +ArgListCommandFuncNoneReturn = Callable[[CommandParent, list[str]], None] #: Aggregate of all accepted function signatures for Command Functions that accept a pre-processed argument list ArgListCommandFunc = Union[ @@ -206,7 +203,7 @@ def _set_parser_prog(parser: argparse.ArgumentParser, prog: str) -> None: """ # Set the prog value for this parser parser.prog = prog - req_args: List[str] = [] + req_args: list[str] = [] # Set the prog value for the parser's subcommands for action in parser._actions: @@ -330,7 +327,7 @@ def arg_decorator(func: ArgparseCommandFunc[CommandParent]) -> RawCommandFuncOpt """ @functools.wraps(func) - def cmd_wrapper(*args: Any, **kwargs: Dict[str, Any]) -> Optional[bool]: + def cmd_wrapper(*args: Any, **kwargs: dict[str, Any]) -> Optional[bool]: """ Command function wrapper which translates command line into argparse Namespace and calls actual command function @@ -361,7 +358,7 @@ def cmd_wrapper(*args: Any, **kwargs: Dict[str, Any]) -> Optional[bool]: namespace = ns_provider(provider_self if provider_self is not None else cmd2_app) try: - new_args: Union[Tuple[argparse.Namespace], Tuple[argparse.Namespace, List[str]]] + new_args: Union[tuple[argparse.Namespace], tuple[argparse.Namespace, list[str]]] if with_unknown_args: new_args = arg_parser.parse_known_args(parsed_arglist, namespace) else: @@ -406,7 +403,7 @@ def as_subcommand_to( ], *, help: Optional[str] = None, - aliases: Optional[List[str]] = None, + aliases: Optional[list[str]] = None, ) -> Callable[[ArgparseCommandFunc[CommandParent]], ArgparseCommandFunc[CommandParent]]: """ Tag this method as a subcommand to an existing argparse decorated command. @@ -428,7 +425,7 @@ def arg_decorator(func: ArgparseCommandFunc[CommandParent]) -> ArgparseCommandFu setattr(func, constants.SUBCMD_ATTR_NAME, subcommand) # Keyword arguments for subparsers.add_parser() - add_parser_kwargs: Dict[str, Any] = dict() + add_parser_kwargs: dict[str, Any] = dict() if help is not None: add_parser_kwargs['help'] = help if aliases: diff --git a/cmd2/history.py b/cmd2/history.py index ea157cd3..707ebd3e 100644 --- a/cmd2/history.py +++ b/cmd2/history.py @@ -14,9 +14,7 @@ from typing import ( Any, Callable, - Dict, Iterable, - List, Optional, Union, overload, @@ -132,12 +130,12 @@ def pr(self, idx: int, script: bool = False, expanded: bool = False, verbose: bo return ret_str - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: """Utility method to convert this HistoryItem into a dictionary for use in persistent JSON history files""" 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': """ Utility method to restore a HistoryItem from a dictionary @@ -149,7 +147,7 @@ def from_dict(source_dict: Dict[str, Any]) -> 'HistoryItem': return HistoryItem(Statement.from_dict(statement_dict)) -class History(List[HistoryItem]): +class History(list[HistoryItem]): """A list of :class:`~cmd2.history.HistoryItem` objects with additional methods for searching and managing the list. @@ -242,7 +240,7 @@ def get(self, index: int) -> HistoryItem: # 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) -> 'OrderedDict[int, HistoryItem]': + def span(self, span: str, include_persisted: bool = False) -> OrderedDict[int, HistoryItem]: """Return a slice of the History list :param span: string containing an index or a slice @@ -291,7 +289,7 @@ def span(self, span: str, include_persisted: bool = False) -> 'OrderedDict[int, return self._build_result_dictionary(start, end) - def str_search(self, search: str, include_persisted: bool = False) -> 'OrderedDict[int, HistoryItem]': + def str_search(self, search: str, include_persisted: bool = False) -> OrderedDict[int, HistoryItem]: """Find history items which contain a given string :param search: the string to search for @@ -310,7 +308,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) -> 'OrderedDict[int, HistoryItem]': + def regex_search(self, regex: str, include_persisted: bool = False) -> OrderedDict[int, HistoryItem]: """Find history items which match a given regular expression :param regex: the regular expression to search for. @@ -346,7 +344,7 @@ def truncate(self, max_length: int) -> None: def _build_result_dictionary( self, start: int, end: int, filter_func: Optional[Callable[[HistoryItem], bool]] = None - ) -> 'OrderedDict[int, HistoryItem]': + ) -> OrderedDict[int, HistoryItem]: """ Build history search results :param start: start index to search from diff --git a/cmd2/parsing.py b/cmd2/parsing.py index 7e59a558..b72fdf68 100644 --- a/cmd2/parsing.py +++ b/cmd2/parsing.py @@ -10,11 +10,8 @@ ) from typing import ( Any, - Dict, Iterable, - List, Optional, - Tuple, Union, ) @@ -27,7 +24,7 @@ ) -def shlex_split(str_to_split: str) -> List[str]: +def shlex_split(str_to_split: str) -> list[str]: """ A wrapper around shlex.split() that uses cmd2's preferred arguments. This allows other classes to easily call split() the same way StatementParser does. @@ -79,7 +76,7 @@ class Statement(str): command: str = '' # list of arguments to the command, not including any output redirection or terminators; quoted args remain quoted - arg_list: List[str] = field(default_factory=list) + arg_list: list[str] = field(default_factory=list) # if the command is a multiline command, the name of the command, otherwise empty multiline_command: str = '' @@ -157,7 +154,7 @@ def expanded_command_line(self) -> str: return self.command_and_args + self.post_command @property - def argv(self) -> List[str]: + def argv(self) -> list[str]: """a list of arguments a-la ``sys.argv``. The first element of the list is the command after shortcut expansion. @@ -176,12 +173,12 @@ def argv(self) -> List[str]: return rtn - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: """Utility method to convert this Statement into a dictionary for use in persistent JSON history files""" return self.__dict__.copy() @staticmethod - def from_dict(source_dict: Dict[str, Any]) -> 'Statement': + def from_dict(source_dict: dict[str, Any]) -> 'Statement': """ Utility method to restore a Statement from a dictionary @@ -209,8 +206,8 @@ def __init__( self, terminators: Optional[Iterable[str]] = None, multiline_commands: Optional[Iterable[str]] = None, - aliases: Optional[Dict[str, str]] = None, - shortcuts: Optional[Dict[str, str]] = None, + aliases: Optional[dict[str, str]] = None, + shortcuts: Optional[dict[str, str]] = None, ) -> None: """Initialize an instance of StatementParser. @@ -222,13 +219,13 @@ def __init__( :param aliases: dictionary containing aliases :param shortcuts: dictionary containing shortcuts """ - self.terminators: Tuple[str, ...] + self.terminators: tuple[str, ...] if terminators is None: self.terminators = (constants.MULTILINE_TERMINATOR,) else: self.terminators = tuple(terminators) - self.multiline_commands: Tuple[str, ...] = tuple(multiline_commands) if multiline_commands is not None else () - self.aliases: Dict[str, str] = aliases if aliases is not None else {} + self.multiline_commands: tuple[str, ...] = tuple(multiline_commands) if multiline_commands is not None else () + self.aliases: dict[str, str] = aliases if aliases is not None else {} if shortcuts is None: shortcuts = constants.DEFAULT_SHORTCUTS @@ -269,7 +266,7 @@ def __init__( 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]: + def is_valid_command(self, word: str, *, is_subcommand: bool = False) -> tuple[bool, str]: """Determine whether a word is a valid name for a command. Commands cannot include redirection characters, whitespace, @@ -320,7 +317,7 @@ def is_valid_command(self, word: str, *, is_subcommand: bool = False) -> Tuple[b errmsg = '' return valid, errmsg - def tokenize(self, line: str) -> List[str]: + def tokenize(self, line: str) -> list[str]: """ Lex a string into a list of tokens. Shortcuts and aliases are expanded and comments are removed. @@ -558,7 +555,7 @@ def parse_command_only(self, rawinput: str) -> Statement: def get_command_arg_list( self, command_name: str, to_parse: Union[Statement, str], preserve_quotes: bool - ) -> Tuple[Statement, List[str]]: + ) -> tuple[Statement, list[str]]: """ Convenience method used by the argument parsing decorators. @@ -627,7 +624,7 @@ def _expand(self, line: str) -> str: return line @staticmethod - def _command_and_args(tokens: List[str]) -> Tuple[str, str]: + 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. """ @@ -642,7 +639,7 @@ def _command_and_args(tokens: List[str]) -> Tuple[str, str]: return command, args - def split_on_punctuation(self, tokens: List[str]) -> List[str]: + def split_on_punctuation(self, tokens: list[str]) -> list[str]: """Further splits tokens from a command line using punctuation characters. Punctuation characters are treated as word breaks when they are in @@ -652,7 +649,7 @@ def split_on_punctuation(self, tokens: List[str]) -> List[str]: :param tokens: the tokens as parsed by shlex :return: a new list of tokens, further split using punctuation """ - punctuation: List[str] = [] + punctuation: list[str] = [] punctuation.extend(self.terminators) punctuation.extend(constants.REDIRECTION_CHARS) diff --git a/cmd2/py_bridge.py b/cmd2/py_bridge.py index 7873f9dc..e4aa505f 100644 --- a/cmd2/py_bridge.py +++ b/cmd2/py_bridge.py @@ -13,7 +13,6 @@ IO, TYPE_CHECKING, Any, - List, NamedTuple, Optional, TextIO, @@ -99,9 +98,9 @@ def __init__(self, cmd2_app: 'cmd2.Cmd', *, add_to_history: bool = True) -> None # Tells if any of the commands run via __call__ returned True for stop self.stop = False - def __dir__(self) -> List[str]: + def __dir__(self) -> list[str]: """Return a custom set of attribute names""" - attributes: List[str] = [] + attributes: list[str] = [] attributes.insert(0, 'cmd_echo') return attributes diff --git a/cmd2/table_creator.py b/cmd2/table_creator.py index 8abe6626..f86cccb2 100644 --- a/cmd2/table_creator.py +++ b/cmd2/table_creator.py @@ -17,10 +17,8 @@ from typing import ( Any, Deque, - List, Optional, Sequence, - Tuple, Union, ) @@ -156,7 +154,7 @@ def __init__(self, cols: Sequence[Column], *, tab_width: int = 4) -> None: col.width = max(1, ansi.widest_line(col.header)) @staticmethod - def _wrap_long_word(word: str, max_width: int, max_lines: Union[int, float], is_last_word: bool) -> Tuple[str, int, int]: + def _wrap_long_word(word: str, max_width: int, max_lines: Union[int, float], is_last_word: bool) -> tuple[str, int, int]: """ Used by _wrap_text() to wrap a long word over multiple lines @@ -164,7 +162,7 @@ def _wrap_long_word(word: str, max_width: int, max_lines: Union[int, float], is_ :param max_width: maximum display width of a line :param max_lines: maximum lines to wrap before ending the last line displayed with an ellipsis :param is_last_word: True if this is the last word of the total text being wrapped - :return: Tuple(wrapped text, lines used, display width of last line) + :return: tuple(wrapped text, lines used, display width of last line) """ styles_dict = utils.get_styles_dict(word) wrapped_buf = io.StringIO() @@ -382,7 +380,7 @@ def add_word(word_to_add: str, is_last_word: bool) -> None: return wrapped_buf.getvalue() - def _generate_cell_lines(self, cell_data: Any, is_header: bool, col: Column, fill_char: str) -> Tuple[Deque[str], int]: + def _generate_cell_lines(self, cell_data: Any, is_header: bool, col: Column, fill_char: str) -> tuple[Deque[str], int]: """ Generate the lines of a table cell @@ -392,7 +390,7 @@ def _generate_cell_lines(self, cell_data: Any, is_header: bool, col: Column, fil :param col: Column definition for this cell :param fill_char: character that fills remaining space in a cell. If your text has a background color, then give fill_char the same background color. (Cannot be a line breaking character) - :return: Tuple(deque of cell lines, display width of the cell) + :return: tuple(deque of cell lines, display width of the cell) """ # Convert data to string and replace tabs with spaces data_str = str(cell_data).replace('\t', SPACE * self.tab_width) @@ -654,7 +652,7 @@ def generate_header(self) -> str: inter_cell = self.apply_header_bg(self.column_spacing * SPACE) # Apply background color to header text in Columns which allow it - to_display: List[Any] = [] + to_display: list[Any] = [] for col in self.cols: if col.style_header_text: to_display.append(self.apply_header_bg(col.header)) @@ -694,7 +692,7 @@ def generate_data_row(self, row_data: Sequence[Any]) -> str: inter_cell = self.apply_data_bg(self.column_spacing * SPACE) # Apply background color to data text in Columns which allow it - to_display: List[Any] = [] + to_display: list[Any] = [] for index, col in enumerate(self.cols): if col.style_data_text: to_display.append(self.apply_data_bg(row_data[index])) @@ -949,7 +947,7 @@ def generate_header(self) -> str: post_line = self.apply_header_bg(self.padding * SPACE) + self.apply_border_color('║') # Apply background color to header text in Columns which allow it - to_display: List[Any] = [] + to_display: list[Any] = [] for col in self.cols: if col.style_header_text: to_display.append(self.apply_header_bg(col.header)) @@ -993,7 +991,7 @@ def generate_data_row(self, row_data: Sequence[Any]) -> str: post_line = self.apply_data_bg(self.padding * SPACE) + self.apply_border_color('║') # Apply background color to data text in Columns which allow it - to_display: List[Any] = [] + to_display: list[Any] = [] for index, col in enumerate(self.cols): if col.style_data_text: to_display.append(self.apply_data_bg(row_data[index])) diff --git a/cmd2/transcript.py b/cmd2/transcript.py index f4781fd9..47c76878 100644 --- a/cmd2/transcript.py +++ b/cmd2/transcript.py @@ -15,10 +15,8 @@ class is used in cmd2.py::run_transcript_tests() from typing import ( TYPE_CHECKING, Iterator, - List, Optional, TextIO, - Tuple, cast, ) @@ -66,7 +64,7 @@ def runTest(self) -> None: # was testall def _fetchTranscripts(self) -> None: self.transcripts = {} - testfiles = cast(List[str], getattr(self.cmdapp, 'testfiles', [])) + testfiles = cast(list[str], getattr(self.cmdapp, 'testfiles', [])) for fname in testfiles: tfile = open(fname) self.transcripts[fname] = iter(tfile.readlines()) @@ -184,7 +182,7 @@ def _transform_transcript_expected(self, s: str) -> str: return regex @staticmethod - def _escaped_find(regex: str, s: str, start: int, in_regex: bool) -> Tuple[str, int, int]: + def _escaped_find(regex: str, s: str, start: int, in_regex: bool) -> tuple[str, int, int]: """Find the next slash in {s} after {start} that is not preceded by a backslash. If we find an escaped slash, add everything up to and including it to regex, diff --git a/cmd2/utils.py b/cmd2/utils.py index f2d1af5f..1cac0a54 100644 --- a/cmd2/utils.py +++ b/cmd2/utils.py @@ -23,9 +23,7 @@ TYPE_CHECKING, Any, Callable, - Dict, Iterable, - List, Optional, TextIO, Type, @@ -160,7 +158,7 @@ def __init__( """ if val_type is bool: - def get_bool_choices(_) -> List[str]: # type: ignore[no-untyped-def] + def get_bool_choices(_) -> list[str]: # type: ignore[no-untyped-def] """Used to tab complete lowercase boolean values""" return ['true', 'false'] @@ -235,7 +233,7 @@ def is_text_file(file_path: str) -> bool: return valid_text_file -def remove_duplicates(list_to_prune: List[_T]) -> List[_T]: +def remove_duplicates(list_to_prune: list[_T]) -> list[_T]: """Removes duplicates from a list while preserving order of the items. :param list_to_prune: the list being pruned of duplicates @@ -257,7 +255,7 @@ def norm_fold(astr: str) -> str: return unicodedata.normalize('NFC', astr).casefold() -def alphabetical_sort(list_to_sort: Iterable[str]) -> List[str]: +def alphabetical_sort(list_to_sort: Iterable[str]) -> list[str]: """Sorts a list of strings alphabetically. For example: ['a1', 'A11', 'A2', 'a22', 'a3'] @@ -284,7 +282,7 @@ def try_int_or_force_to_lower_case(input_str: str) -> Union[int, str]: return norm_fold(input_str) -def natural_keys(input_str: str) -> List[Union[int, str]]: +def natural_keys(input_str: str) -> list[Union[int, str]]: """ Converts a string into a list of integers and strings to support natural sorting (see natural_sort). @@ -295,7 +293,7 @@ def natural_keys(input_str: str) -> List[Union[int, 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]: +def natural_sort(list_to_sort: Iterable[str]) -> list[str]: """ Sorts a list of strings case insensitively as well as numerically. @@ -311,7 +309,7 @@ def natural_sort(list_to_sort: Iterable[str]) -> List[str]: return sorted(list_to_sort, key=natural_keys) -def quote_specific_tokens(tokens: List[str], tokens_to_quote: List[str]) -> None: +def quote_specific_tokens(tokens: list[str], tokens_to_quote: list[str]) -> None: """ Quote specific tokens in a list @@ -323,7 +321,7 @@ def quote_specific_tokens(tokens: List[str], tokens_to_quote: List[str]) -> None tokens[i] = quote_string(token) -def unquote_specific_tokens(tokens: List[str], tokens_to_unquote: List[str]) -> None: +def unquote_specific_tokens(tokens: list[str], tokens_to_unquote: list[str]) -> None: """ Unquote specific tokens in a list @@ -357,7 +355,7 @@ def expand_user(token: str) -> str: return token -def expand_user_in_tokens(tokens: List[str]) -> None: +def expand_user_in_tokens(tokens: list[str]) -> None: """ Call expand_user() on all tokens in a list of strings :param tokens: tokens to expand @@ -399,7 +397,7 @@ def find_editor() -> Optional[str]: return editor -def files_from_glob_pattern(pattern: str, access: int = os.F_OK) -> List[str]: +def files_from_glob_pattern(pattern: str, access: int = os.F_OK) -> list[str]: """Return a list of file paths based on a glob pattern. Only files are returned, not directories, and optionally only files for which the user has a specified access to. @@ -411,7 +409,7 @@ def files_from_glob_pattern(pattern: str, access: int = os.F_OK) -> List[str]: return [f for f in glob.glob(pattern) if os.path.isfile(f) and os.access(f, access)] -def files_from_glob_patterns(patterns: List[str], access: int = os.F_OK) -> List[str]: +def files_from_glob_patterns(patterns: list[str], access: int = os.F_OK) -> list[str]: """Return a list of file paths based on a list of glob patterns. Only files are returned, not directories, and optionally only files for which the user has a specified access to. @@ -427,7 +425,7 @@ def files_from_glob_patterns(patterns: List[str], access: int = os.F_OK) -> List return files -def get_exes_in_path(starts_with: str) -> List[str]: +def get_exes_in_path(starts_with: str) -> list[str]: """Returns names of executables in a user's path :param starts_with: what the exes should start with. leave blank for all exes in path. @@ -748,7 +746,7 @@ def __init__( self.saved_redirecting = saved_redirecting -def _remove_overridden_styles(styles_to_parse: List[str]) -> List[str]: +def _remove_overridden_styles(styles_to_parse: list[str]) -> list[str]: """ Utility function for align_text() / truncate_line() which filters a style list down to only those which would still be in effect if all were processed in order. @@ -769,7 +767,7 @@ class StyleState: def __init__(self) -> None: # Contains styles still in effect, keyed by their index in styles_to_parse - self.style_dict: Dict[int, str] = dict() + self.style_dict: dict[int, str] = dict() # Indexes into style_dict self.reset_all: Optional[int] = None @@ -907,7 +905,7 @@ def align_text( # ANSI style sequences that may affect subsequent lines will be cancelled by the fill_char's style. # To avoid this, we save styles which are still in effect so we can restore them when beginning the next line. # This also allows lines to be used independently and still have their style. TableCreator does this. - previous_styles: List[str] = [] + previous_styles: list[str] = [] for index, line in enumerate(lines): if index > 0: @@ -1118,7 +1116,7 @@ def truncate_line(line: str, max_width: int, *, tab_width: int = 4) -> str: return truncated_buf.getvalue() -def get_styles_dict(text: str) -> Dict[int, str]: +def get_styles_dict(text: str) -> dict[int, str]: """ Return an OrderedDict containing all ANSI style sequences found in a string diff --git a/examples/argparse_completion.py b/examples/argparse_completion.py index daad63ab..6b706488 100755 --- a/examples/argparse_completion.py +++ b/examples/argparse_completion.py @@ -5,10 +5,6 @@ """ import argparse -from typing import ( - Dict, - List, -) from cmd2 import ( Cmd, @@ -28,11 +24,11 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.sport_item_strs = ['Bat', 'Basket', 'Basketball', 'Football', 'Space Ball'] - def choices_provider(self) -> List[str]: + def choices_provider(self) -> list[str]: """A choices provider is useful when the choice list is based on instance data of your application""" return self.sport_item_strs - def choices_completion_error(self) -> List[str]: + def choices_completion_error(self) -> list[str]: """ CompletionErrors can be raised if an error occurs while tab completing. @@ -44,14 +40,14 @@ def choices_completion_error(self) -> List[str]: return self.sport_item_strs raise CompletionError("debug must be true") - def choices_completion_item(self) -> List[CompletionItem]: + def choices_completion_item(self) -> list[CompletionItem]: """Return CompletionItem instead of strings. These give more context to what's being tab completed.""" fancy_item = "These things can\ncontain newlines and\n" fancy_item += ansi.style("styled text!!", fg=ansi.Fg.LIGHT_YELLOW, underline=True) items = {1: "My item", 2: "Another item", 3: "Yet another item", 4: fancy_item} return [CompletionItem(item_id, description) for item_id, description in items.items()] - def choices_arg_tokens(self, arg_tokens: Dict[str, List[str]]) -> List[str]: + def choices_arg_tokens(self, arg_tokens: dict[str, list[str]]) -> list[str]: """ If a choices or completer function/method takes a value called arg_tokens, then it will be passed a dictionary that maps the command line tokens up through the one being completed diff --git a/examples/async_printing.py b/examples/async_printing.py index e94ee89a..221c52e7 100755 --- a/examples/async_printing.py +++ b/examples/async_printing.py @@ -8,9 +8,6 @@ import random import threading import time -from typing import ( - List, -) import cmd2 from cmd2 import ( @@ -89,7 +86,7 @@ def do_stop_alerts(self, _): else: print("The alert thread is already stopped") - def _get_alerts(self) -> List[str]: + def _get_alerts(self) -> list[str]: """ Reports alerts :return: the list of alerts diff --git a/examples/basic_completion.py b/examples/basic_completion.py index c713f2b0..ba32d67b 100755 --- a/examples/basic_completion.py +++ b/examples/basic_completion.py @@ -14,13 +14,10 @@ """ import functools -from typing import ( - List, -) import cmd2 -# List of strings used with completion functions +# list of strings used with completion functions food_item_strs = ['Pizza', 'Ham', 'Ham Sandwich', 'Potato'] sport_item_strs = ['Bat', 'Basket', 'Basketball', 'Football', 'Space Ball'] @@ -46,7 +43,7 @@ def do_flag_based(self, statement: cmd2.Statement): """ self.poutput("Args: {}".format(statement.args)) - def complete_flag_based(self, text, line, begidx, endidx) -> List[str]: + def complete_flag_based(self, text, line, begidx, endidx) -> list[str]: """Completion function for do_flag_based""" flag_dict = { # Tab complete food items after -f and --food flags in command line @@ -66,7 +63,7 @@ def do_index_based(self, statement: cmd2.Statement): """Tab completes first 3 arguments using index_based_complete""" self.poutput("Args: {}".format(statement.args)) - def complete_index_based(self, text, line, begidx, endidx) -> List[str]: + def complete_index_based(self, text, line, begidx, endidx) -> list[str]: """Completion function for do_index_based""" index_dict = { 1: food_item_strs, # Tab complete food items at index 1 in command line @@ -87,7 +84,7 @@ def do_raise_error(self, statement: cmd2.Statement): """Demonstrates effect of raising CompletionError""" self.poutput("Args: {}".format(statement.args)) - def complete_raise_error(self, text, line, begidx, endidx) -> List[str]: + def complete_raise_error(self, text, line, begidx, endidx) -> list[str]: """ CompletionErrors can be raised if an error occurs while tab completing. diff --git a/examples/decorator_example.py b/examples/decorator_example.py index ea8fd3b5..2a5449dc 100755 --- a/examples/decorator_example.py +++ b/examples/decorator_example.py @@ -12,9 +12,6 @@ """ import argparse -from typing import ( - List, -) import cmd2 @@ -76,7 +73,7 @@ def do_tag(self, args: argparse.Namespace): self.poutput('<{0}>{1}'.format(args.tag, ' '.join(args.content))) @cmd2.with_argument_list - def do_tagg(self, arglist: List[str]): + def do_tagg(self, arglist: list[str]): """version of creating an html tag using arglist instead of argparser""" if len(arglist) >= 2: tag = arglist[0] diff --git a/examples/exit_code.py b/examples/exit_code.py index d8e538ce..12d73176 100755 --- a/examples/exit_code.py +++ b/examples/exit_code.py @@ -2,10 +2,6 @@ # coding=utf-8 """A simple example demonstrating the following how to emit a non-zero exit code in your cmd2 application.""" -from typing import ( - List, -) - import cmd2 @@ -16,7 +12,7 @@ def __init__(self): super().__init__() @cmd2.with_argument_list - def do_exit(self, arg_list: List[str]) -> bool: + def do_exit(self, arg_list: list[str]) -> bool: """Exit the application with an optional exit code. Usage: exit [exit_code] diff --git a/examples/hooks.py b/examples/hooks.py index 97b90739..6c1001e5 100755 --- a/examples/hooks.py +++ b/examples/hooks.py @@ -10,9 +10,6 @@ """ import re -from typing import ( - List, -) import cmd2 @@ -102,7 +99,7 @@ def proof_hook(self, data: cmd2.plugin.PostcommandData) -> cmd2.plugin.Postcomma return data @cmd2.with_argument_list - def do_list(self, arglist: List[str]) -> None: + def do_list(self, arglist: list[str]) -> None: """Generate a list of 10 numbers.""" if arglist: first = arglist[0] diff --git a/examples/modular_commands/commandset_basic.py b/examples/modular_commands/commandset_basic.py index a4b7582f..4fe63bbf 100644 --- a/examples/modular_commands/commandset_basic.py +++ b/examples/modular_commands/commandset_basic.py @@ -3,10 +3,6 @@ A simple example demonstrating a loadable command set """ -from typing import ( - List, -) - from cmd2 import ( Cmd, CommandSet, @@ -19,7 +15,7 @@ @with_default_category('Basic Completion') class BasicCompletionCommandSet(CommandSet): - # List of strings used with completion functions + # list of strings used with completion functions food_item_strs = ['Pizza', 'Ham', 'Ham Sandwich', 'Potato'] sport_item_strs = ['Bat', 'Basket', 'Basketball', 'Football', 'Space Ball'] @@ -40,7 +36,7 @@ def do_flag_based(self, cmd: Cmd, statement: Statement): """ self._cmd.poutput("Args: {}".format(statement.args)) - def complete_flag_based(self, cmd: Cmd, text: str, line: str, begidx: int, endidx: int) -> List[str]: + def complete_flag_based(self, cmd: Cmd, text: str, line: str, begidx: int, endidx: int) -> list[str]: """Completion function for do_flag_based""" flag_dict = { # Tab complete food items after -f and --food flags in command line @@ -60,7 +56,7 @@ def do_index_based(self, cmd: Cmd, statement: Statement): """Tab completes first 3 arguments using index_based_complete""" self._cmd.poutput("Args: {}".format(statement.args)) - def complete_index_based(self, cmd: Cmd, text: str, line: str, begidx: int, endidx: int) -> List[str]: + def complete_index_based(self, cmd: Cmd, text: str, line: str, begidx: int, endidx: int) -> list[str]: """Completion function for do_index_based""" index_dict = { 1: self.food_item_strs, # Tab complete food items at index 1 in command line @@ -74,14 +70,14 @@ def do_delimiter_complete(self, cmd: Cmd, statement: Statement): """Tab completes files from a list using delimiter_complete""" self._cmd.poutput("Args: {}".format(statement.args)) - def complete_delimiter_complete(self, cmd: Cmd, text: str, line: str, begidx: int, endidx: int) -> List[str]: + def complete_delimiter_complete(self, cmd: Cmd, text: str, line: str, begidx: int, endidx: int) -> list[str]: return cmd.delimiter_complete(text, line, begidx, endidx, match_against=self.file_strs, delimiter='/') def do_raise_error(self, cmd: Cmd, statement: Statement): """Demonstrates effect of raising CompletionError""" self._cmd.poutput("Args: {}".format(statement.args)) - def complete_raise_error(self, cmd: Cmd, text: str, line: str, begidx: int, endidx: int) -> List[str]: + def complete_raise_error(self, cmd: Cmd, text: str, line: str, begidx: int, endidx: int) -> list[str]: """ CompletionErrors can be raised if an error occurs while tab completing. diff --git a/examples/modular_commands/commandset_complex.py b/examples/modular_commands/commandset_complex.py index 7ab84ac3..e89884d7 100644 --- a/examples/modular_commands/commandset_complex.py +++ b/examples/modular_commands/commandset_complex.py @@ -5,9 +5,6 @@ """ import argparse -from typing import ( - List, -) import cmd2 @@ -25,7 +22,7 @@ def do_banana(self, statement: cmd2.Statement): 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]): + def do_cranberry(self, ns: argparse.Namespace, unknown: list[str]): self._cmd.poutput('Cranberry {}!!'.format(ns.arg1)) if unknown and len(unknown): self._cmd.poutput('Unknown: ' + ', '.join(['{}'] * len(unknown)).format(*unknown)) @@ -36,12 +33,12 @@ def help_cranberry(self): @cmd2.with_argument_list @cmd2.with_category('Also Alone') - def do_durian(self, args: List[str]): + def do_durian(self, args: list[str]): """Durian Command""" self._cmd.poutput('{} Arguments: '.format(len(args))) self._cmd.poutput(', '.join(['{}'] * len(args)).format(*args)) - def complete_durian(self, text: str, line: str, begidx: int, endidx: int) -> List[str]: + 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']) elderberry_parser = cmd2.Cmd2ArgumentParser() diff --git a/examples/modular_commands_main.py b/examples/modular_commands_main.py index 74483987..00f5bce9 100755 --- a/examples/modular_commands_main.py +++ b/examples/modular_commands_main.py @@ -8,7 +8,6 @@ import argparse from typing import ( Iterable, - List, Optional, ) @@ -35,7 +34,7 @@ def __init__(self, command_sets: Optional[Iterable[CommandSet]] = None): super().__init__(command_sets=command_sets) self.sport_item_strs = ['Bat', 'Basket', 'Basketball', 'Football', 'Space Ball'] - def choices_provider(self) -> List[str]: + def choices_provider(self) -> list[str]: """A choices provider is useful when the choice list is based on instance data of your application""" return self.sport_item_strs diff --git a/examples/paged_output.py b/examples/paged_output.py index 0f7173b2..4f4f8a5a 100755 --- a/examples/paged_output.py +++ b/examples/paged_output.py @@ -3,9 +3,6 @@ """A simple example demonstrating the using paged output via the ppaged() method.""" import os -from typing import ( - List, -) import cmd2 @@ -27,7 +24,7 @@ def page_file(self, file_path: str, chop: bool = False): self.pexcept('Error reading {!r}: {}'.format(filename, ex)) @cmd2.with_argument_list - def do_page_wrap(self, args: List[str]): + def do_page_wrap(self, args: list[str]): """Read in a text file and display its output in a pager, wrapping long lines if they don't fit. Usage: page_wrap @@ -40,7 +37,7 @@ def do_page_wrap(self, args: List[str]): complete_page_wrap = cmd2.Cmd.path_complete @cmd2.with_argument_list - def do_page_truncate(self, args: List[str]): + def do_page_truncate(self, args: list[str]): """Read in a text file and display its output in a pager, truncating long lines if they don't fit. Truncated lines can still be accessed by scrolling to the right using the arrow keys. diff --git a/examples/read_input.py b/examples/read_input.py index bfc43380..87f4719f 100755 --- a/examples/read_input.py +++ b/examples/read_input.py @@ -4,10 +4,6 @@ A simple example demonstrating the various ways to call cmd2.Cmd.read_input() for input history and tab completion """ -from typing import ( - List, -) - import cmd2 EXAMPLE_COMMANDS = "Example Commands" @@ -64,7 +60,7 @@ def do_custom_choices(self, _) -> None: else: self.custom_history.append(input_str) - def choices_provider(self) -> List[str]: + def choices_provider(self) -> list[str]: """Example choices provider function""" return ["from_provider_1", "from_provider_2", "from_provider_3"] diff --git a/examples/scripts/save_help_text.py b/examples/scripts/save_help_text.py index b8ba9624..3590dd32 100644 --- a/examples/scripts/save_help_text.py +++ b/examples/scripts/save_help_text.py @@ -8,15 +8,12 @@ import argparse import os import sys -from typing import ( - List, - TextIO, -) +from typing import TextIO ASTERISKS = "********************************************************" -def get_sub_commands(parser: argparse.ArgumentParser) -> List[str]: +def get_sub_commands(parser: argparse.ArgumentParser) -> list[str]: """Get a list of subcommands for an ArgumentParser""" sub_cmds = [] diff --git a/examples/table_creation.py b/examples/table_creation.py index 852f2d84..485c4c05 100755 --- a/examples/table_creation.py +++ b/examples/table_creation.py @@ -4,10 +4,7 @@ import functools import sys -from typing import ( - Any, - List, -) +from typing import Any from cmd2 import ( EightBitBg, @@ -63,8 +60,8 @@ def __init__(self, name: str, birthday: str, place_of_birth: str) -> None: self.name = name self.birthday = birthday self.place_of_birth = place_of_birth - self.books: List[Book] = [] - self.relatives: List[Relative] = [] + self.books: list[Book] = [] + self.relatives: list[Relative] = [] def ansi_print(text): @@ -76,7 +73,7 @@ def basic_tables(): """Demonstrates basic examples of the table classes""" # Table data which demonstrates handling of wrapping and text styles - data_list: List[List[Any]] = list() + data_list: list[list[Any]] = list() data_list.append(["Billy Smith", "123 Sesame St.\n" "Fake Town, USA 33445", DollarFormatter(100333.03)]) data_list.append( [ @@ -96,7 +93,7 @@ def basic_tables(): data_list.append(["John Jones", "9235 Highway 32\n" + green("Greenville") + ", SC 29604", DollarFormatter(82987.71)]) # Table Columns (width does not account for any borders or padding which may be added) - columns: List[Column] = list() + columns: list[Column] = list() columns.append(Column("Name", width=20)) columns.append(Column("Address", width=38)) columns.append( @@ -123,7 +120,7 @@ def nested_tables(): """ # Create data for this example - author_data: List[Author] = [] + author_data: list[Author] = [] author_1 = Author("Frank Herbert", "10/08/1920", "Tacoma, Washington") author_1.books.append(Book("Dune", "1965")) author_1.books.append(Book("Dune Messiah", "1969")) @@ -159,7 +156,7 @@ def nested_tables(): # Define table which presents Author data fields vertically with no header. # This will be nested in the parent table's first column. - author_columns: List[Column] = list() + author_columns: list[Column] = list() author_columns.append(Column("", width=14)) author_columns.append(Column("", width=20)) @@ -174,7 +171,7 @@ def nested_tables(): # Define AlternatingTable for books checked out by people in the first table. # This will be nested in the parent table's second column. - books_columns: List[Column] = list() + books_columns: list[Column] = list() books_columns.append(Column(ansi.style("Title", bold=True), width=25)) books_columns.append( Column( @@ -196,7 +193,7 @@ def nested_tables(): # Define BorderedTable for relatives of the author # This will be nested in the parent table's third column. - relative_columns: List[Column] = list() + relative_columns: list[Column] = list() relative_columns.append(Column(ansi.style("Name", bold=True), width=25)) relative_columns.append(Column(ansi.style("Relationship", bold=True), width=12)) @@ -220,7 +217,7 @@ def nested_tables(): ) # Define parent AlternatingTable which contains Author and Book tables - parent_tbl_columns: List[Column] = list() + parent_tbl_columns: list[Column] = list() # All of the nested tables already have background colors. Set style_data_text # to False so the parent AlternatingTable does not apply background color to them. @@ -242,7 +239,7 @@ def nested_tables(): ) # Construct the tables - parent_table_data: List[List[Any]] = [] + parent_table_data: list[list[Any]] = [] for row, author in enumerate(author_data, start=1): # First build the author table and color it based on row number author_tbl = even_author_tbl if row % 2 == 0 else odd_author_tbl diff --git a/pyproject.toml b/pyproject.toml index 163f4293..3007f8dc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -205,7 +205,7 @@ select = [ "FA", # flake8-future-annotations # "FBT", # flake8-boolean-trap "G", # flake8-logging-format - # "I", # isort + "I", # isort "ICN", # flake8-import-conventions # "INP", # flake8-no-pep420 "INT", # flake8-gettext diff --git a/tests/conftest.py b/tests/conftest.py index 20b9ebd3..aae8a3bd 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -10,7 +10,6 @@ redirect_stdout, ) from typing import ( - List, Optional, Union, ) @@ -32,7 +31,7 @@ def verify_help_text( - cmd2_app: cmd2.Cmd, help_output: Union[str, List[str]], verbose_strings: Optional[List[str]] = None + cmd2_app: cmd2.Cmd, help_output: Union[str, list[str]], verbose_strings: Optional[list[str]] = None ) -> None: """This function verifies that all expected commands are present in the help text. @@ -195,7 +194,7 @@ def get_endidx(): return app.complete(text, 0) -def find_subcommand(action: argparse.ArgumentParser, subcmd_names: List[str]) -> argparse.ArgumentParser: +def find_subcommand(action: argparse.ArgumentParser, subcmd_names: list[str]) -> argparse.ArgumentParser: if not subcmd_names: return action cur_subcmd = subcmd_names.pop(0) diff --git a/tests/test_argparse_completer.py b/tests/test_argparse_completer.py index 1f9178f8..e06b53a8 100644 --- a/tests/test_argparse_completer.py +++ b/tests/test_argparse_completer.py @@ -6,11 +6,7 @@ import argparse import numbers -from typing import ( - Dict, - List, - cast, -) +from typing import cast import pytest @@ -39,11 +35,11 @@ standalone_completions = ['standalone', 'completer'] -def standalone_choice_provider(cli: cmd2.Cmd) -> List[str]: +def standalone_choice_provider(cli: cmd2.Cmd) -> list[str]: return standalone_choices -def standalone_completer(cli: cmd2.Cmd, text: str, line: str, begidx: int, endidx: int) -> List[str]: +def standalone_completer(cli: cmd2.Cmd, text: str, line: str, begidx: int, endidx: int) -> list[str]: return cli.basic_complete(text, line, begidx, endidx, standalone_completions) @@ -125,11 +121,11 @@ def do_pos_and_flag(self, args: argparse.Namespace) -> None: # This tests that CompletionItems created with numerical values are sorted as numbers. num_completion_items = [CompletionItem(5, "Five"), CompletionItem(1.5, "One.Five"), CompletionItem(2, "Five")] - def choices_provider(self) -> List[str]: + def choices_provider(self) -> list[str]: """Method that provides choices""" return self.choices_from_provider - def completion_item_method(self) -> List[CompletionItem]: + def completion_item_method(self) -> list[CompletionItem]: """Choices method that returns CompletionItems""" items = [] for i in range(0, 10): @@ -191,13 +187,13 @@ def do_choices(self, args: argparse.Namespace) -> None: completions_for_pos_1 = ['completions', 'positional_1', 'probably', 'missed', 'spot'] completions_for_pos_2 = ['completions', 'positional_2', 'probably', 'missed', 'me'] - def flag_completer(self, text: str, line: str, begidx: int, endidx: int) -> List[str]: + def flag_completer(self, text: str, line: str, begidx: int, endidx: int) -> list[str]: return self.basic_complete(text, line, begidx, endidx, self.completions_for_flag) - def pos_1_completer(self, text: str, line: str, begidx: int, endidx: int) -> List[str]: + def pos_1_completer(self, text: str, line: str, begidx: int, endidx: int) -> list[str]: return self.basic_complete(text, line, begidx, endidx, self.completions_for_pos_1) - def pos_2_completer(self, text: str, line: str, begidx: int, endidx: int) -> List[str]: + def pos_2_completer(self, text: str, line: str, begidx: int, endidx: int) -> list[str]: return self.basic_complete(text, line, begidx, endidx, self.completions_for_pos_2) completer_parser = Cmd2ArgumentParser() @@ -265,11 +261,11 @@ def do_hint(self, args: argparse.Namespace) -> None: ############################################################################################################ # Begin code related to CompletionError ############################################################################################################ - def completer_raise_error(self, text: str, line: str, begidx: int, endidx: int) -> List[str]: + def completer_raise_error(self, text: str, line: str, begidx: int, endidx: int) -> list[str]: """Raises CompletionError""" raise CompletionError('completer broke something') - def choice_raise_error(self) -> List[str]: + def choice_raise_error(self) -> list[str]: """Raises CompletionError""" raise CompletionError('choice broke something') @@ -284,13 +280,13 @@ def do_raise_completion_error(self, args: argparse.Namespace) -> None: ############################################################################################################ # Begin code related to receiving arg_tokens ############################################################################################################ - def choices_takes_arg_tokens(self, arg_tokens: Dict[str, List[str]]) -> List[str]: + def choices_takes_arg_tokens(self, arg_tokens: dict[str, list[str]]) -> list[str]: """Choices function that receives arg_tokens from ArgparseCompleter""" return [arg_tokens['parent_arg'][0], arg_tokens['subcommand'][0]] def completer_takes_arg_tokens( - self, text: str, line: str, begidx: int, endidx: int, arg_tokens: Dict[str, List[str]] - ) -> List[str]: + self, text: str, line: str, begidx: int, endidx: int, arg_tokens: dict[str, list[str]] + ) -> list[str]: """Completer function that receives arg_tokens from ArgparseCompleter""" match_against = [arg_tokens['parent_arg'][0], arg_tokens['subcommand'][0]] return self.basic_complete(text, line, begidx, endidx, match_against) @@ -1204,7 +1200,7 @@ def test_complete_standalone(ac_app, flag, completions): # Custom ArgparseCompleter-based class class CustomCompleter(argparse_completer.ArgparseCompleter): - def _complete_flags(self, text: str, line: str, begidx: int, endidx: int, matched_flags: List[str]) -> List[str]: + def _complete_flags(self, text: str, line: str, begidx: int, endidx: int, matched_flags: list[str]) -> list[str]: """Override so flags with 'complete_when_ready' set to True will complete only when app is ready""" # Find flags which should not be completed and place them in matched_flags diff --git a/tests/test_utils.py b/tests/test_utils.py index a173f7f4..665261ab 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -336,10 +336,6 @@ def test_context_flag_exit_err(context_flag): def test_remove_overridden_styles(): - from typing import ( - List, - ) - from cmd2 import ( Bg, EightBitBg, @@ -350,7 +346,7 @@ def test_remove_overridden_styles(): TextStyle, ) - def make_strs(styles_list: List[ansi.AnsiSequence]) -> List[str]: + def make_strs(styles_list: list[ansi.AnsiSequence]) -> list[str]: return [str(s) for s in styles_list] # Test Reset All diff --git a/tests_isolated/test_commandset/conftest.py b/tests_isolated/test_commandset/conftest.py index e70185a8..423523b5 100644 --- a/tests_isolated/test_commandset/conftest.py +++ b/tests_isolated/test_commandset/conftest.py @@ -9,7 +9,6 @@ redirect_stdout, ) from typing import ( - List, Optional, Union, ) @@ -34,7 +33,7 @@ def verify_help_text( - cmd2_app: cmd2.Cmd, help_output: Union[str, List[str]], verbose_strings: Optional[List[str]] = None + cmd2_app: cmd2.Cmd, help_output: Union[str, list[str]], verbose_strings: Optional[list[str]] = None ) -> None: """This function verifies that all expected commands are present in the help text. diff --git a/tests_isolated/test_commandset/test_commandset.py b/tests_isolated/test_commandset/test_commandset.py index 0b488952..18c3d74a 100644 --- a/tests_isolated/test_commandset/test_commandset.py +++ b/tests_isolated/test_commandset/test_commandset.py @@ -6,9 +6,6 @@ import argparse import signal -from typing import ( - List, -) import pytest @@ -61,7 +58,7 @@ def do_banana(self, statement: cmd2.Statement): 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]): + def do_cranberry(self, ns: argparse.Namespace, unknown: list[str]): self._cmd.poutput('Cranberry {}!!'.format(ns.arg1)) if unknown and len(unknown): self._cmd.poutput('Unknown: ' + ', '.join(['{}'] * len(unknown)).format(*unknown)) @@ -72,13 +69,13 @@ def help_cranberry(self): @cmd2.with_argument_list @cmd2.with_category('Also Alone') - def do_durian(self, args: List[str]): + def do_durian(self, args: list[str]): """Durian Command""" self._cmd.poutput('{} Arguments: '.format(len(args))) 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]: + 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']) elderberry_parser = cmd2.Cmd2ArgumentParser() @@ -450,7 +447,7 @@ def __init__(self, dummy): def do_arugula(self, _: cmd2.Statement): self._cmd.poutput('Arugula') - def complete_style_arg(self, text: str, line: str, begidx: int, endidx: int) -> List[str]: + def complete_style_arg(self, text: str, line: str, begidx: int, endidx: int) -> list[str]: return ['quartered', 'diced'] bokchoy_parser = cmd2.Cmd2ArgumentParser() @@ -688,7 +685,7 @@ def cut_banana(self, ns: argparse.Namespace): """Cut banana""" self.poutput('cutting banana: ' + ns.direction) - def complete_style_arg(self, text: str, line: str, begidx: int, endidx: int) -> List[str]: + def complete_style_arg(self, text: str, line: str, begidx: int, endidx: int) -> list[str]: return ['quartered', 'diced'] bokchoy_parser = cmd2.Cmd2ArgumentParser() @@ -743,7 +740,7 @@ def __init__(self, dummy): """dummy variable prevents this from being autoloaded in other tests""" super(SupportFuncProvider, self).__init__() - def complete_states(self, text: str, line: str, begidx: int, endidx: int) -> List[str]: + def complete_states(self, text: str, line: str, begidx: int, endidx: int) -> list[str]: assert self is complete_states_expected_self return self._cmd.basic_complete(text, line, begidx, endidx, self.states)