diff --git a/lib/cli_command_parser/context.py b/lib/cli_command_parser/context.py index 0c0facfa..f728eedc 100644 --- a/lib/cli_command_parser/context.py +++ b/lib/cli_command_parser/context.py @@ -46,7 +46,7 @@ class Context(AbstractContextManager): # Extending AbstractContextManager to ma config: CommandConfig prog: OptStr = None allow_argv_prog: Bool = True - _command_obj: CommandObj = None + _command_obj: CommandObj | None = None _terminal_width: int | None _provided: dict[ParamOrGroup, int] @@ -56,8 +56,8 @@ def __init__( command_cls: CommandType | None = None, *, parent: Context | None = None, - config: AnyConfig = None, - terminal_width: int = None, + config: AnyConfig | None = None, + terminal_width: int | None = None, allow_argv_prog: Bool = None, command: CommandObj | None = None, **kwargs, @@ -100,7 +100,7 @@ def _set_argv(self, prog: OptStr, argv: Argv): self.remaining = list(self.argv) def _sub_context( - self, command_cls: CommandType, argv: Argv = None, command: CommandObj = None, **kwargs + self, command_cls: CommandType, argv: Argv = None, command: CommandObj | None = None, **kwargs ) -> Context: return self.__class__( self.remaining if argv is None else argv, @@ -151,7 +151,7 @@ def terminal_width(self) -> int: def get_parsed( self, - command: Command = None, + command: Command | None = None, *, exclude: Collection[Parameter] = (), recursive: Bool = True, @@ -443,7 +443,7 @@ def get_current_context(silent: bool = False) -> Context | None: def get_or_create_context( - command_cls: CommandType, argv: Argv = None, *, command: CommandObj = None, **kwargs + command_cls: CommandType, argv: Argv = None, *, command: CommandObj | None = None, **kwargs ) -> Context: """ Used internally by Commands to re-use an existing user-activated Context, or to create a new Context if there was @@ -469,7 +469,7 @@ def get_context(command: Command) -> Context: def get_parsed( - command: Command, to_call: Callable = None, default: Any = None, include_defaults: Bool = True + command: Command, to_call: Callable | None = None, default: Any = None, include_defaults: Bool = True ) -> dict[str, Any]: """ Provides a way to obtain all of the arguments that were parsed for the given Command as a dictionary. diff --git a/lib/cli_command_parser/conversion/argparse_ast.py b/lib/cli_command_parser/conversion/argparse_ast.py index 6c7b26b2..8128dcbe 100644 --- a/lib/cli_command_parser/conversion/argparse_ast.py +++ b/lib/cli_command_parser/conversion/argparse_ast.py @@ -35,7 +35,7 @@ class Script: _parser_classes = {} path: Path | None - def __init__(self, src_text: str, smart_loop_handling: bool = True, path: PathLike = None): + def __init__(self, src_text: str, smart_loop_handling: bool = True, path: PathLike | None = None): self.smart_loop_handling = smart_loop_handling self._parsers = [] self.path = Path(path) if path else None @@ -143,13 +143,15 @@ def _add_visit_func(cls, name: str) -> bool: cls.visit_funcs.add(name) return True - def __init_subclass__(cls, represents: RepresentedCallable = None, **kwargs): + def __init_subclass__(cls, represents: RepresentedCallable | None = None, **kwargs): super().__init_subclass__(**kwargs) if represents: cls.represents = represents cls._sig = None - def __init__(self, node: InitNode, parent: AstCallable | Script, tracked_refs: TrackedRefMap, call: Call = None): + def __init__( + self, node: InitNode, parent: AstCallable | Script, tracked_refs: TrackedRefMap, call: Call | None = None + ): self.init_node = node if not call: call = node.value if isinstance(node, Assign) else node # type: Call @@ -253,7 +255,9 @@ def __init_subclass__(cls, children: Collection[str] = (), **kwargs): if children: cls._children = (*cls._children, *children) - def __init__(self, node: InitNode, parent: AstCallable | Script, tracked_refs: TrackedRefMap, call: Call = None): + def __init__( + self, node: InitNode, parent: AstCallable | Script, tracked_refs: TrackedRefMap, call: Call | None = None + ): super().__init__(node, parent, tracked_refs, call) self.args = [] self.groups = [] @@ -314,7 +318,9 @@ class AstArgumentParser(ArgCollection, represents=ArgumentParser, children=('sub sub_parsers: list[SubParser] add_subparsers = AddVisitedChild(SubparsersAction, '_subparsers_actions') - def __init__(self, node: InitNode, parent: AstCallable | Script, tracked_refs: TrackedRefMap, call: Call = None): + def __init__( + self, node: InitNode, parent: AstCallable | Script, tracked_refs: TrackedRefMap, call: Call | None = None + ): super().__init__(node, parent, tracked_refs, call) self._subparsers_actions = [] # Note: sub_parsers aren't included in grouped_children since they need different handling during conversion @@ -324,7 +330,9 @@ def __repr__(self) -> str: sub_parsers = len(self.sub_parsers) return f'<{self.__class__.__name__}[{sub_parsers=}]: ``{self.init_call_repr()}``>' - def _add_subparser(self, node: InitNode, call: Call, tracked_refs: TrackedRefMap, sub_parser_cls: ParserCls = None): + def _add_subparser( + self, node: InitNode, call: Call, tracked_refs: TrackedRefMap, sub_parser_cls: ParserCls | None = None + ): # Using default of None since the class hasn't been defined at the time it would need to be set as default return self._add_child(sub_parser_cls or SubParser, self.sub_parsers, node, call, tracked_refs) diff --git a/lib/cli_command_parser/conversion/command_builder.py b/lib/cli_command_parser/conversion/command_builder.py index 8880532d..491de05b 100644 --- a/lib/cli_command_parser/conversion/command_builder.py +++ b/lib/cli_command_parser/conversion/command_builder.py @@ -33,11 +33,11 @@ def convert_script(script: Script, add_methods: bool = False) -> str: class Converter(Generic[AC], ABC): - converts: Type[AC] = None + converts: Type[AC] | None = None newline_between_members: bool = False _ac_converter_map = {} - def __init_subclass__(cls, converts: Type[AC] = None, newline_between_members: bool = None, **kwargs): + def __init_subclass__(cls, converts: Type[AC] | None = None, newline_between_members: bool | None = None, **kwargs): super().__init_subclass__(**kwargs) if converts: cls.converts = converts @@ -165,8 +165,8 @@ class ParserConverter(CollectionConverter[AstArgumentParser], converts=AstArgume def __init__( self, parser: AstArgumentParser, - parent: ParserConverter = None, - counter: count = None, + parent: ParserConverter | None = None, + counter: count | None = None, *, add_methods: bool = False, ): diff --git a/lib/cli_command_parser/documentation.py b/lib/cli_command_parser/documentation.py index c985c539..2693d531 100644 --- a/lib/cli_command_parser/documentation.py +++ b/lib/cli_command_parser/documentation.py @@ -22,7 +22,7 @@ from .formatting.restructured_text import MODULE_TEMPLATE, rst_header, rst_toc_tree if TYPE_CHECKING: - from .typing import Bool, CommandCls, PathLike, Strings + from .typing import Bool, CommandCls, OptStr, PathLike, Strings Commands = dict[str, CommandCls] @@ -34,14 +34,14 @@ def render_script_rst( - path: PathLike, top_only: Bool = True, fix_name: Bool = True, fix_name_func: NameFunc = None + path: PathLike, top_only: Bool = True, fix_name: Bool = True, fix_name_func: NameFunc | None = None ) -> str: """Load all Commands from the file with the given path, and generate a single RST string based on those Commands""" commands = load_commands(path, top_only) return _render_commands_rst(commands, fix_name, fix_name_func) -def render_command_rst(command: CommandCls, fix_name: Bool = True, fix_name_func: NameFunc = None) -> str: +def render_command_rst(command: CommandCls, fix_name: Bool = True, fix_name_func: NameFunc | None = None) -> str: """ :param command: The :class:`.Command` to document :param fix_name: Whether the file name should be re-formatted from CamelCase / snake_case to separate Title Case @@ -53,7 +53,7 @@ def render_command_rst(command: CommandCls, fix_name: Bool = True, fix_name_func return get_formatter(command).format_rst(fix_name, fix_name_func) -def _render_commands_rst(commands: Commands, fix_name: Bool = True, fix_name_func: NameFunc = None) -> str: +def _render_commands_rst(commands: Commands, fix_name: Bool = True, fix_name_func: NameFunc | None = None) -> str: # This could be better, but it's relatively unlikely to have multiple top level commands in a script... # For the same reason that main() does not try to pick one, this will just combine all of them. parts = [] @@ -199,7 +199,7 @@ def __init__( newline: str = '\n', ext: str = '.rst', module_template: str = MODULE_TEMPLATE, - skip_modules: Strings = None, + skip_modules: Strings | None = None, ): self.output_dir = Path(output_dir) self.dry_run = dry_run @@ -212,9 +212,9 @@ def __init__( def document_script( self, path: Path, - subdir: str = None, - name: str = None, - replacements: Mapping[str, str] = None, + subdir: OptStr = None, + name: OptStr = None, + replacements: Mapping[str, str] | None = None, top_only: Bool = True, **kwargs, ) -> str: @@ -253,13 +253,13 @@ def document_script( def document_scripts( self, paths: Iterable[Path], - subdir: str = None, + subdir: OptStr = None, top_only: Bool = True, *, - index_name: str = None, - index_header: str = None, - index_subdir: str = None, - caption: str = None, + index_name: OptStr = None, + index_header: OptStr = None, + index_subdir: OptStr = None, + caption: OptStr = None, **kwargs, ): names = [self.document_script(path, subdir, top_only=top_only, **kwargs) for path in paths] @@ -269,7 +269,7 @@ def document_scripts( name, index_header or name.title(), names, content_subdir=subdir, caption=caption, subdir=index_subdir ) - def document_module(self, module: str, subdir: str = None): + def document_module(self, module: str, subdir: OptStr = None): """ Generate an RST file to document a Python module. @@ -285,13 +285,13 @@ def document_package( self, pkg_name: str, pkg_path: Path, - subdir: str = None, + subdir: OptStr = None, *, - name: str = None, - header: str = None, + name: OptStr = None, + header: OptStr = None, index: Bool = True, empty: Bool = False, - caption: str = None, + caption: OptStr = None, max_depth: int = 4, ) -> list[str]: """ @@ -331,7 +331,9 @@ def document_package( ) return contents - def _generate_code_rsts(self, pkg_name: str, pkg_path: Path, subdir: str = None, max_depth: int = 4) -> list[str]: + def _generate_code_rsts( + self, pkg_name: str, pkg_path: Path, subdir: OptStr = None, max_depth: int = 4 + ) -> list[str]: contents = [] for path in pkg_path.iterdir(): if path.is_dir(): @@ -355,9 +357,9 @@ def write_index( header: str, contents: Strings, *, - content_subdir: str = None, - subdir: str = None, - caption: str = None, + content_subdir: OptStr = None, + subdir: OptStr = None, + caption: OptStr = None, max_depth: int = 4, **kwargs, ): @@ -379,7 +381,7 @@ def write_index( rendered = rst_toc_tree(header, content_fmt, contents, caption=caption, max_depth=max_depth, **kwargs) self.write_rst(name, rendered, subdir) - def write_rst(self, name: str, content: str, subdir: str = None): + def write_rst(self, name: str, content: str, subdir: OptStr = None): target_dir = self.output_dir.joinpath(subdir) if subdir else self.output_dir if not self.dry_run and not target_dir.exists(): target_dir.mkdir(parents=True) diff --git a/lib/cli_command_parser/formatting/commands.py b/lib/cli_command_parser/formatting/commands.py index 43a8372b..57230031 100644 --- a/lib/cli_command_parser/formatting/commands.py +++ b/lib/cli_command_parser/formatting/commands.py @@ -24,7 +24,7 @@ from ..core import CommandMeta from ..metadata import ProgramMetadata from ..parameters import BaseOption, BasePositional, Parameter, PassThru, SubCommand - from ..typing import Bool, CommandAny, CommandCls, CommandType + from ..typing import Bool, CommandAny, CommandCls, CommandType, OptStr __all__ = ['CommandHelpFormatter', 'get_formatter'] @@ -78,7 +78,7 @@ def _iter_params(self) -> Iterator[BasePositional | BaseOption | PassThru]: if params.pass_thru is not None: yield params.pass_thru - def _usage_parts(self, sub_cmd_choice: str = None, allow_sys_argv: Bool = True) -> Iterator[str]: + def _usage_parts(self, sub_cmd_choice: OptStr = None, allow_sys_argv: Bool = True) -> Iterator[str]: yield 'usage:' yield self._meta.get_prog(allow_sys_argv) if sub_cmd_choice: @@ -91,7 +91,7 @@ def _usage_parts(self, sub_cmd_choice: str = None, allow_sys_argv: Bool = True) def format_usage( self, delim: str = ' ', - sub_cmd_choice: str = None, + sub_cmd_choice: OptStr = None, allow_sys_argv: Bool = True, cont_indent: int = 4, ) -> str: @@ -149,7 +149,7 @@ def _format_rst( def _cmd_rst_lines( self, config: CommandConfig, - sub_cmd_choice: str = None, + sub_cmd_choice: OptStr = None, allow_sys_argv: Bool = False, include_epilog: Bool = False, ) -> Iterator[str]: @@ -184,7 +184,7 @@ def _sub_cmds_rst_lines( config: CommandConfig, sub_command: SubCommand, level: int, - choice_base: str = None, + choice_base: OptStr = None, depth: int = 0, allow_sys_argv: Bool = False, ): @@ -228,12 +228,15 @@ def get_formatter(command: CommandAny) -> CommandHelpFormatter: def get_usage_sub_cmds(command: CommandCls): cmd_mcs: Type[CommandMeta] = command.__class__ # Using metaclass to avoid potentially overwritten attrs - if not (parent := cmd_mcs.parent(command, False)): # type: CommandType + + parent: CommandMeta + if not (parent := cmd_mcs.parent(command, False)): return yield from get_usage_sub_cmds(parent) - if not (sub_cmd_param := cmd_mcs.params(parent).sub_command): # type: SubCommand + sub_cmd_param: SubCommand + if not (sub_cmd_param := cmd_mcs.params(parent).sub_command): return try: diff --git a/lib/cli_command_parser/formatting/params.py b/lib/cli_command_parser/formatting/params.py index d3e0f17a..9e43624f 100644 --- a/lib/cli_command_parser/formatting/params.py +++ b/lib/cli_command_parser/formatting/params.py @@ -120,7 +120,7 @@ def iter_usage_parts(self, include_meta: Bool = False, full: Bool = False) -> It """Format the Parameter for use in the list of Parameters with their ``help='...'`` descriptions""" yield self.format_usage(include_meta=include_meta, full=full) - def format_description(self, rst: Bool = False, description: str = None) -> str: + def format_description(self, rst: Bool = False, description: OptStr = None) -> str: param = self.param if description is None: description = param.help or '' @@ -170,7 +170,7 @@ def iter_usage_parts(self, include_meta: Bool = False, full: Bool = False) -> It metavar = self._format_usage_metavar() yield from (f'{opt} {metavar}' for opt in opts.option_strs()) - def format_description(self, rst: Bool = False, description: str = None) -> str: + def format_description(self, rst: Bool = False, description: OptStr = None) -> str: description = super().format_description(rst, description) param: BaseOption = self.param if param.env_var and (param.show_env_var or (param.show_env_var is None and ctx.config.show_env_vars)): @@ -436,7 +436,7 @@ def format_usage(self, include_meta: Bool = False, full: Bool = False, delim: st members = (mem.formatter.format_usage(include_meta, full, delim) for mem in self.param.members) return self.maybe_wrap_usage(self._get_choice_delim().join(members)) - def format_description(self, rst: Bool = False, description: str = None) -> str: + def format_description(self, rst: Bool = False, description: OptStr = None) -> str: if description: return description group = self.param diff --git a/lib/cli_command_parser/formatting/restructured_text.py b/lib/cli_command_parser/formatting/restructured_text.py index bf849c1f..d0ca22bd 100644 --- a/lib/cli_command_parser/formatting/restructured_text.py +++ b/lib/cli_command_parser/formatting/restructured_text.py @@ -59,7 +59,7 @@ def spaced_rst_header(text: str, level: int = 1, before: bool = True) -> Iterato def _rst_directive( - directive: str, args: str = None, options: dict[str, Any] = None, indent: int = 4, check: Bool = False + directive: str, args: OptStr = None, options: dict[str, Any] | None = None, indent: int = 4, check: Bool = False ) -> Iterator[str]: yield f'.. {directive}:: {args}' if args else f'.. {directive}::' if options: @@ -70,7 +70,7 @@ def _rst_directive( def rst_directive( - directive: str, args: str = None, options: dict[str, Any] = None, indent: int = 4, check: Bool = False + directive: str, args: OptStr = None, options: dict[str, Any] | None = None, indent: int = 4, check: Bool = False ) -> str: return '\n'.join(_rst_directive(directive, args, options, indent, check)) @@ -120,9 +120,9 @@ class RstTable: def __init__( self, - title: str = None, - subtitle: str = None, - headers: Sequence[str] = None, + title: OptStr = None, + subtitle: OptStr = None, + headers: Sequence[str] | None = None, *, show_title: Bool = True, use_table_directive: Bool = True, @@ -138,7 +138,9 @@ def __init__( self.add_row(*headers, header=True) @classmethod - def from_dicts(cls, rows: RowMaps, columns: Sequence[T] = None, auto_headers: Bool = False, **kwargs) -> RstTable: + def from_dicts( + cls, rows: RowMaps, columns: Sequence[T] | None = None, auto_headers: Bool = False, **kwargs + ) -> RstTable: """ Initialize a RstTable using the given keyword arguments, and populate its rows using the given dicts and :meth:`.add_dict_rows`. @@ -168,7 +170,7 @@ def widths(self) -> tuple[int, ...]: self._updated = False return self._widths - def add_dict_rows(self, rows: RowMaps, columns: Sequence[T] = None, add_header: Bool = False): + def add_dict_rows(self, rows: RowMaps, columns: Sequence[T] | None = None, add_header: Bool = False): """Add a row for each dict in the given sequence of rows, where the keys represent the columns.""" if not columns: columns = list(rows[0]) @@ -191,7 +193,7 @@ def _add_rows(self, rows: Iterable[Row]): self._rows.extend(rows) self._updated = True - def add_row(self, *columns: OptStr, index: int = None, header: bool = False): + def add_row(self, *columns: OptStr, index: int | None = None, header: bool = False): """ Add a row to the table. @@ -202,7 +204,7 @@ def add_row(self, *columns: OptStr, index: int = None, header: bool = False): """ self._add_row(Row([Cell(c or '') for c in columns], header), index) - def _add_row(self, row: Row, index: int = None): + def _add_row(self, row: Row, index: int | None = None): if index is None: self._rows.append(row) else: diff --git a/lib/cli_command_parser/inputs/choices.py b/lib/cli_command_parser/inputs/choices.py index 076481e3..89f231be 100644 --- a/lib/cli_command_parser/inputs/choices.py +++ b/lib/cli_command_parser/inputs/choices.py @@ -59,7 +59,7 @@ def _normalize(self, value: str) -> T: raise InvalidChoiceError(value, self.choices) from e return value - def _iter_normalized(self, value: Any, choices: Collection = None) -> Iterator[T]: + def _iter_normalized(self, value: Any, choices: Collection | None = None) -> Iterator[T]: yield value if not self.case_sensitive and (choices is None or isinstance(choices, (set, Mapping))): yield value.lower() @@ -94,7 +94,7 @@ class Choices(_ChoicesBase[T]): __slots__ = () - def __init__(self, choices: Collection[T], type: TypeFunc = None, case_sensitive: Bool = True): # noqa + def __init__(self, choices: Collection[T], type: TypeFunc | None = None, case_sensitive: Bool = True): # noqa if not case_sensitive and not all(isinstance(c, str) for c in choices): raise TypeError(f'Cannot combine case_sensitive=False with non-str {choices=}') elif isinstance(type, EnumChoices) and not any(isinstance(c, type.type) for c in choices): diff --git a/lib/cli_command_parser/inputs/files.py b/lib/cli_command_parser/inputs/files.py index 01bd2c3a..0dfc4f2f 100644 --- a/lib/cli_command_parser/inputs/files.py +++ b/lib/cli_command_parser/inputs/files.py @@ -10,7 +10,7 @@ from abc import ABC from pathlib import Path as _Path -from ..typing import FP, Bool, Converter, PathLike, T +from ..typing import FP, Bool, Converter, OptStr, PathLike, T from .base import InputType from .exceptions import InputValidationError from .utils import FileWrapper, InputParam, StatMode, allows_write, fix_windows_path @@ -143,8 +143,8 @@ def __init__( self, mode: str = 'r', *, - encoding: str = None, - errors: str = None, + encoding: OptStr = None, + errors: OptStr = None, lazy: Bool = True, parents: Bool = False, **kwargs, diff --git a/lib/cli_command_parser/inputs/numeric.py b/lib/cli_command_parser/inputs/numeric.py index 924720ea..314fa709 100644 --- a/lib/cli_command_parser/inputs/numeric.py +++ b/lib/cli_command_parser/inputs/numeric.py @@ -125,7 +125,7 @@ class NumRange(RangeMixin, _RangeInput[NT]): def __init__( self, - type: NumType = None, # noqa + type: NumType | None = None, # noqa snap: Bool = False, *, min: Number = None, # noqa diff --git a/lib/cli_command_parser/inputs/time.py b/lib/cli_command_parser/inputs/time.py index 9568ea25..511b33f1 100644 --- a/lib/cli_command_parser/inputs/time.py +++ b/lib/cli_command_parser/inputs/time.py @@ -24,7 +24,7 @@ from threading import RLock from typing import Collection, Iterator, Literal, Sequence, Type, TypeVar, overload -from ..typing import Bool, Locale, Number, T, TimeBound +from ..typing import Bool, Locale, Number, OptStr, T, TimeBound from ..utils import MissingMixin from .base import InputType, _FixedInputType from .exceptions import InputValidationError, InvalidChoiceError @@ -75,10 +75,10 @@ def __exit__(self, exc_type, exc_val, exc_tb): class DTInput(_FixedInputType[T], ABC): __slots__ = ('locale',) - dt_type: str + dt_type: OptStr locale: Locale | None - def __init_subclass__(cls, dt_type: str = None, **kwargs): + def __init_subclass__(cls, dt_type: OptStr = None, **kwargs): """ :param dt_type: Used in InvalidChoiceError / ValueError messages """ @@ -133,9 +133,9 @@ def __init__( full: Bool = True, abbreviation: Bool = True, numeric: Bool = False, - locale: Locale = None, + locale: Locale | None = None, out_format: str | DTFormatMode = DTFormatMode.FULL, - out_locale: Locale = None, + out_locale: Locale | None = None, fix_default: Bool = True, ): if not (full or abbreviation or numeric): @@ -250,9 +250,9 @@ def __init__( abbreviation: Bool = True, numeric: Bool = False, iso: Bool = False, - locale: Locale = None, + locale: Locale | None = None, out_format: str | DTFormatMode = DTFormatMode.FULL, - out_locale: Locale = None, + out_locale: Locale | None = None, fix_default: Bool = True, ): ... @@ -305,9 +305,9 @@ def __init__( full: Bool = True, abbreviation: Bool = True, numeric: Bool = True, - locale: Locale = None, + locale: Locale | None = None, out_format: str | DTFormatMode = DTFormatMode.FULL, - out_locale: Locale = None, + out_locale: Locale | None = None, fix_default: Bool = True, ): ... @@ -408,7 +408,7 @@ def __init_subclass__(cls, type: Type[DT], **kwargs): # noqa def __init__( self, formats: Collection[str], - locale: Locale = None, + locale: Locale | None = None, earliest: TimeBound = None, latest: TimeBound = None, fix_default: Bool = True, @@ -513,7 +513,7 @@ class DateTime(DateTimeInput[datetime], type=datetime): def __init__( self, *formats: str, - locale: Locale = None, + locale: Locale | None = None, earliest: TimeBound = None, latest: TimeBound = None, fix_default: Bool = True, @@ -539,7 +539,7 @@ class Date(DateTimeInput[date], type=date): def __init__( self, *formats: str, - locale: Locale = None, + locale: Locale | None = None, earliest: TimeBound = None, latest: TimeBound = None, fix_default: Bool = True, @@ -565,7 +565,7 @@ class Time(DateTimeInput[time], type=time): def __init__( self, *formats: str, - locale: Locale = None, + locale: Locale | None = None, earliest: TimeBound = None, latest: TimeBound = None, fix_default: Bool = True, @@ -586,7 +586,7 @@ def dt_repr(dt: datetime | date | time, use_repr: bool = True) -> str: return repr(dt_str) if use_repr else dt_str -def normalize_dt(value: TimeBound, now: datetime = None) -> datetime | None: +def normalize_dt(value: TimeBound, now: datetime | None = None) -> datetime | None: match value: case None | datetime(): return value diff --git a/lib/cli_command_parser/inputs/utils.py b/lib/cli_command_parser/inputs/utils.py index 39d7ce58..b4868ffb 100644 --- a/lib/cli_command_parser/inputs/utils.py +++ b/lib/cli_command_parser/inputs/utils.py @@ -18,7 +18,7 @@ from .exceptions import InputValidationError if TYPE_CHECKING: - from ..typing import FP, Bool, Converter, Number + from ..typing import FP, Bool, Converter, Number, OptStr __all__ = ['InputParam', 'StatMode', 'FileWrapper', 'fix_windows_path', 'range_str', 'RangeMixin'] @@ -46,7 +46,7 @@ def __set__(self, instance, value: Any): class StatMode(FixedFlag): - def __new__(cls, mode, friendly_name: str = None): + def __new__(cls, mode, friendly_name: OptStr = None): # Defined __new__ to avoid juggling dicts for the stat mode values and names obj = object.__new__(cls) if friendly_name: @@ -90,9 +90,9 @@ def __init__( self, path: Path, mode: str = 'r', - encoding: str = None, - errors: str = None, - converter: Converter = None, + encoding: OptStr = None, + errors: OptStr = None, + converter: Converter | None = None, pass_file: Bool = False, parents: Bool = False, ): diff --git a/lib/cli_command_parser/parameters/actions.py b/lib/cli_command_parser/parameters/actions.py index 318d4379..7c16a121 100644 --- a/lib/cli_command_parser/parameters/actions.py +++ b/lib/cli_command_parser/parameters/actions.py @@ -16,7 +16,7 @@ if TYPE_CHECKING: from .base import Parameter # noqa - from ..typing import Bool, CommandObj, T_co + from ..typing import Bool, CommandObj, OptStr __all__ = [ 'ParamAction', @@ -44,7 +44,9 @@ class ParamAction(ABC, Generic[Param]): accepts_values: bool = False accepts_consts: bool = False - def __init_subclass__(cls, default=_PANotSet, accepts_values: bool = None, accepts_consts: bool = None, **kwargs): + def __init_subclass__( + cls, default=_PANotSet, accepts_values: bool | None = None, accepts_consts: bool | None = None, **kwargs + ): super().__init_subclass__(**kwargs) cls.name = camel_to_snake_case(cls.__name__) if default is not _PANotSet: @@ -72,7 +74,7 @@ def default_nargs(self) -> Nargs: # region Add Parsed Value / Constant Methods @abstractmethod - def add_value(self, value: str, *, combo: bool = False, joined: bool = False, env_var: str = None) -> Found: + def add_value(self, value: str, *, combo: bool = False, joined: bool = False, env_var: OptStr = None) -> Found: """ Execute this action for the given Parameter and value. @@ -96,7 +98,7 @@ def add_env_value(self, value: str, env_var: str) -> Found: # added += self.add_value(value, combo=combo) # return added - def add_const(self, *, opt: str = None, combo: bool = False) -> Found: # noqa + def add_const(self, *, opt: OptStr = None, combo: bool = False) -> Found: # noqa ctx.record_action(self.param) raise MissingArgument(self.param) @@ -261,7 +263,7 @@ class Store(ValueMixin, ParamAction, default=None, accepts_values=True): # region Add Parsed Value / Constant Methods - def add_value(self, value: str, *, combo: bool = False, joined: Bool = False, env_var: str = None) -> Found: + def add_value(self, value: str, *, combo: bool = False, joined: Bool = False, env_var: OptStr = None) -> Found: ctx.record_action(self.param) value = self.param.prepare_value(value, combo, env_var) self.param.validate(value, joined) @@ -297,7 +299,7 @@ class Append(ValueMixin, ParamAction, accepts_values=True): # region Add Parsed Value / Constant Methods - def add_value(self, value: str, *, combo: bool = False, joined: Bool = False, env_var: str = None) -> Found: + def add_value(self, value: str, *, combo: bool = False, joined: Bool = False, env_var: OptStr = None) -> Found: ctx.record_action(self.param) value = self.param.prepare_value(value, combo, env_var) self.param.validate(value) @@ -392,7 +394,7 @@ class BasicConstAction(ConstMixin, ParamAction, ABC, accepts_consts=True): # region Add Parsed Value / Constant Methods - def add_value(self, value: str, *, combo: bool = False, joined: Bool = False, env_var: str = None) -> Found: # noqa + def add_value(self, value: str, *, combo: bool = False, joined: Bool = False, env_var: OptStr = None) -> Found: # noqa ctx.record_action(self.param) raise BadArgument(self.param, f'does not accept values, but {value=} was provided') @@ -414,7 +416,7 @@ class StoreConst(BasicConstAction, default=None): # region Add Parsed Value / Constant Methods - def add_const(self, *, opt: str = None, combo: bool = False) -> Found: + def add_const(self, *, opt: OptStr = None, combo: bool = False) -> Found: ctx.record_action(self.param) self.set_const(self.param.get_const(opt)) return 1 @@ -427,7 +429,7 @@ class AppendConst(BasicConstAction, append=True): # region Add Parsed Value / Constant Methods - def add_const(self, *, opt: str = None, combo: bool = False) -> Found: + def add_const(self, *, opt: OptStr = None, combo: bool = False) -> Found: ctx.record_action(self.param) # TODO: Fix nargs consistency for overall vs per-arg self.append_const(self.param.get_const(opt)) @@ -447,7 +449,7 @@ def get_default(self, command: CommandObj | None = None, missing_default=_NotSet # __slots__ = () # default_nargs = Nargs('?') # -# def add_const(self, *, opt: str = None, combo: bool = False) -> Found: +# def add_const(self, *, opt: OptStr = None, combo: bool = False) -> Found: # ctx.record_action(self.param) # self.set_const(self.param.get_const(opt)) # return 1 @@ -457,7 +459,7 @@ def get_default(self, command: CommandObj | None = None, missing_default=_NotSet # __slots__ = () # default_nargs = Nargs('?') # -# def add_const(self, *, opt: str = None, combo: bool = False) -> Found: +# def add_const(self, *, opt: OptStr = None, combo: bool = False) -> Found: # ctx.record_action(self.param) # self.append_const(self.param.get_const(opt)) # return 1 @@ -476,12 +478,12 @@ def _add(self, value: int): # region Add Parsed Value / Constant Methods - def add_const(self, *, opt: str = None, combo: bool = False) -> Found: + def add_const(self, *, opt: OptStr = None, combo: bool = False) -> Found: ctx.record_action(self.param) self._add(self.param.get_const(opt)) return 1 - def add_value(self, value: str, *, combo: bool = False, joined: Bool = False, env_var: str = None) -> Found: + def add_value(self, value: str, *, combo: bool = False, joined: Bool = False, env_var: OptStr = None) -> Found: ctx.record_action(self.param) value = self.param.prepare_value(value, combo, env_var) self.param.validate(value, joined) @@ -496,7 +498,7 @@ class Concatenate(Append): # region Add Parsed Value / Constant Methods - def add_value(self, value: str, *, combo: bool = False, joined: Bool = False, env_var: str = None) -> Found: + def add_value(self, value: str, *, combo: bool = False, joined: Bool = False, env_var: OptStr = None) -> Found: param = self.param values = value.split() if not param.is_valid_arg(' '.join(values)): diff --git a/lib/cli_command_parser/parameters/base.py b/lib/cli_command_parser/parameters/base.py index 3f7cac02..041c02f0 100644 --- a/lib/cli_command_parser/parameters/base.py +++ b/lib/cli_command_parser/parameters/base.py @@ -33,6 +33,7 @@ from .option_strings import OptionStrings if TYPE_CHECKING: + from ..core import CommandMeta from ..formatting.params import ParamHelpFormatter from ..typing import Bool, CommandAny, CommandCls, CommandObj, LeadingDash, OptStr, OptStrs, Strings from .actions import ParamAction @@ -58,22 +59,22 @@ class ParamBase(ABC): # fmt: off # Class Attributes - missing_hint: str = None #: Hint to provide if this param/group is missing + missing_hint: OptStr = None #: Hint to provide if this param/group is missing # Instance Attributes - _attr_name: str = None #: Always the name of the attr that points to this object - _name: str = None #: An explicitly provided name, or the name of the attr that points to this object - group: ParamGroup = None #: The group this object is a member of, if any - command: CommandCls = None #: The :class:`.Command` this object is a member of - required: Bool #: Whether this param/group is required - help: str #: The description for this param/group that will appear in ``--help`` text - hide: Bool #: Whether this param/group should be hidden in ``--help`` text + _attr_name: OptStr = None #: Always the name of the attr that points to this object + _name: OptStr = None #: An explicitly provided name, or the name of the attr that points to this obj + group: ParamGroup | None = None #: The group this object is a member of, if any + command: CommandMeta | None = None #: The :class:`.Command` this object is a member of + required: Bool #: Whether this param/group is required + help: str #: The description for this param/group that will appear in ``--help`` text + hide: Bool #: Whether this param/group should be hidden in ``--help`` text # fmt: on def __init__( self, - name: str | None = None, + name: OptStr = None, required: Bool = False, - help: str | None = None, # noqa + help: OptStr = None, # noqa hide: Bool = False, ): self.__doc__ = help # Prevent this class's docstring from showing up for params in generated documentation @@ -94,7 +95,7 @@ def name(self) -> str: return self._default_name() @name.setter - def name(self, value: str | None): + def name(self, value: OptStr): if value is not None: self._name = value @@ -202,7 +203,7 @@ class Parameter(ParamBase, Generic[T_co], ABC): _action_map: dict[str, Type[ParamAction]] = {} _repr_attrs: Strings | None = None #: Attributes to include in ``repr()`` output # Instance attributes with class defaults - metavar: str = None + metavar: OptStr = None nargs: Nargs # Expected to be set in subclasses type: Callable[[str], T_co] | None = None # Expected to be set in subclasses allow_leading_dash: AllowLeadingDash = AllowLeadingDash.NUMERIC # Set in some subclasses @@ -231,13 +232,13 @@ def __init__( # pylint: disable=R0913 self, action: str, *, - help: str | None = None, # noqa + help: OptStr = None, # noqa hide: Bool = False, - metavar: str | None = None, - name: str | None = None, + metavar: OptStr = None, + name: OptStr = None, required: Bool = False, default: Any = _NotSet, - default_cb: DefaultFunc = None, + default_cb: DefaultFunc | None = None, cb_with_cmd: Bool = False, show_default: Bool = None, strict_default: Bool = False, @@ -348,7 +349,7 @@ def get_const(self, opt_str: OptStr = None): def get_env_const(self, value: str, env_var: str) -> tuple[T_co, bool]: return _NotSet, False - def prepare_value(self, value: str, short_combo: Bool = False, env_var: str = None) -> T_co: + def prepare_value(self, value: str, short_combo: Bool = False, env_var: OptStr = None) -> T_co: if self.type is None: return value try: @@ -463,7 +464,7 @@ class BasePositional(Parameter[T_co], ABC): _default_ok: bool = False - def __init_subclass__(cls, default_ok: bool = None, **kwargs): # pylint: disable=W0222 + def __init_subclass__(cls, default_ok: bool | None = None, **kwargs): # pylint: disable=W0222 """ :param default_ok: Whether default values are supported for this Parameter type :param kwargs: Additional keyword arguments to pass to :meth:`.Parameter.__init_subclass__`. @@ -529,7 +530,7 @@ def __init__( self, *option_strs: str, action: str, - name_mode: OptionNameMode | str | None = _NotSet, + name_mode: OptionNameMode | OptStr = _NotSet, env_var: OptStrs = None, strict_env: bool = True, use_env_value: Bool = None, diff --git a/lib/cli_command_parser/parameters/choice_map.py b/lib/cli_command_parser/parameters/choice_map.py index ecad4fad..41f38b9a 100644 --- a/lib/cli_command_parser/parameters/choice_map.py +++ b/lib/cli_command_parser/parameters/choice_map.py @@ -96,7 +96,7 @@ class ChoiceMap(BasePositional[str], Generic[T], actions=(Concatenate,)): formatter: ChoiceMapHelpFormatter def __init_subclass__( # pylint: disable=W0222 - cls, title: str = None, choice_validation_exc: Type[Exception] = None, **kwargs + cls, title: OptStr = None, choice_validation_exc: Type[Exception] = None, **kwargs ): """ :param title: Default title to use for help text sections containing the choices for this parameter. @@ -109,7 +109,7 @@ def __init_subclass__( # pylint: disable=W0222 if choice_validation_exc is not None: cls._choice_validation_exc = choice_validation_exc - def __init__(self, *, action: str = 'concatenate', title: str = None, description: str = None, **kwargs): + def __init__(self, *, action: str = 'concatenate', title: OptStr = None, description: OptStr = None, **kwargs): super().__init__(action=action, **kwargs) self.title = title self.description = description @@ -143,15 +143,15 @@ def _validate_positional(cls, value: str): if bad := {c for c in value if (c in whitespace and c != ' ') or c not in printable}: raise cls._choice_validation_exc(f'Invalid {cls.__name__} choice={value!r} - invalid characters: {bad}') - def register_choice(self, choice: str, target: T = _NotSet, help: str = None): # noqa + def register_choice(self, choice: str, target: T = _NotSet, help: OptStr = None): # noqa self._validate_positional(choice) self._register_choice(choice, target, help) def _register_choice( self, choice: OptStr, - target: T | None = _NotSet, - help: str = None, # noqa + target: T | None | _NotSetType = _NotSet, + help: OptStr = None, # noqa local: bool = False, ): try: @@ -223,7 +223,7 @@ def __init__( self, *, required: Bool = True, - default_help: str = None, + default_help: OptStr = None, local_choices: Mapping[str, str] | Collection[str] | None = None, **kwargs, ): @@ -288,8 +288,8 @@ def register( self, command_or_choice: str | CommandCls | None = None, *, - choice: str = None, - help: str = None, # noqa + choice: OptStr = None, + help: OptStr = None, # noqa ) -> Callable[[CommandCls], CommandCls]: """ Class decorator version of :meth:`.register_command`. Registers the wrapped :class:`.Command` as the @@ -336,7 +336,7 @@ def register_action( self, choice: OptStr, method: MethodType, - help: str = None, # noqa + help: OptStr = None, # noqa default: Bool = False, ) -> MethodType: if help is None: @@ -362,8 +362,8 @@ def register( self, method_or_choice: str | MethodType | None = None, *, - choice: str = None, - help: str = None, # noqa + choice: OptStr = None, + help: OptStr = None, # noqa default: Bool = False, ) -> MethodType | Callable[[MethodType], MethodType]: """ diff --git a/lib/cli_command_parser/parameters/options.py b/lib/cli_command_parser/parameters/options.py index 5e94f275..bd5488ea 100644 --- a/lib/cli_command_parser/parameters/options.py +++ b/lib/cli_command_parser/parameters/options.py @@ -77,13 +77,13 @@ class Option(BaseOption[T_co | TD], actions=(Store, Append)): def __init__( self, *option_strs: str, - nargs: NargsValue = None, - action: Literal['store', 'append'] = None, + nargs: NargsValue | None = None, + action: Literal['store', 'append'] | None = None, default: TD = _NotSet, required: Bool = False, type: InputTypeFunc = None, # noqa choices: ChoicesType = None, - allow_leading_dash: LeadingDash = None, + allow_leading_dash: LeadingDash | None = None, **kwargs, ): if nargs_provided := nargs is not None: @@ -162,7 +162,7 @@ def __init__( default: TD = _NotSet, default_cb=_NotSet, const: TC = _NotSet, - type: TypeFunc = None, # noqa + type: TypeFunc | None = None, # noqa **kwargs, ): if const is _NotSet: @@ -246,14 +246,14 @@ def __init__( self, *option_strs: str, consts: tuple[TC, TA] = (True, False), - alt_prefix: str = None, - alt_long: str = None, - alt_short: str = None, - alt_help: str = None, + alt_prefix: OptStr = None, + alt_long: OptStr = None, + alt_short: OptStr = None, + alt_help: OptStr = None, action: ConstAct = 'store_const', default: TD = _NotSet, - default_cb: Callable[[], TD] = None, - type: TypeFunc = None, # noqa + default_cb: Callable[[], TD] | None = None, + type: TypeFunc | None = None, # noqa **kwargs, ): if alt_short and '-' in alt_short[1:]: @@ -342,7 +342,7 @@ def __init__( self, *option_strs: str, order: int | float = 1, - func: Callable = None, + func: Callable | None = None, before_main: Bool = True, # noqa # pylint: disable=W0621 always_available: Bool = False, **kwargs, @@ -423,12 +423,12 @@ def __reduce__(self): action_flag = ActionFlag # pylint: disable=C0103 -def before_main(*option_strs: str, order: int | float = 1, func: Callable = None, **kwargs) -> ActionFlag: +def before_main(*option_strs: str, order: int | float = 1, func: Callable | None = None, **kwargs) -> ActionFlag: """An ActionFlag that will be executed before :meth:`.Command.main`""" return ActionFlag(*option_strs, order=order, func=func, before_main=True, **kwargs) -def after_main(*option_strs: str, order: int | float = 1, func: Callable = None, **kwargs) -> ActionFlag: +def after_main(*option_strs: str, order: int | float = 1, func: Callable | None = None, **kwargs) -> ActionFlag: """An ActionFlag that will be executed after :meth:`.Command.main`""" return ActionFlag(*option_strs, order=order, func=func, before_main=False, **kwargs) @@ -477,7 +477,7 @@ def __init__( init: int = 0, const: int = 1, default: int = _NotSet, - default_cb: Callable[[], int] = None, + default_cb: Callable[[], int] | None = None, required: bool = False, **kwargs, ): @@ -499,7 +499,7 @@ def register_default_cb(self, method: CommandMethod) -> CommandMethod: self.default_cb = None return super().register_default_cb(method) - def prepare_value(self, value: str | None, short_combo: bool = False, env_var: str = None) -> int: + def prepare_value(self, value: str | None, short_combo: bool = False, env_var: OptStr = None) -> int: try: return self.type(value) except (ValueError, TypeError) as e: diff --git a/lib/cli_command_parser/parameters/positionals.py b/lib/cli_command_parser/parameters/positionals.py index e525bd8d..76b7b0df 100644 --- a/lib/cli_command_parser/parameters/positionals.py +++ b/lib/cli_command_parser/parameters/positionals.py @@ -54,14 +54,14 @@ class Positional(BasePositional, default_ok=True, actions=(Store, Append)): def __init__( self, - nargs: NargsValue = None, - action: Literal['store', 'append'] = None, + nargs: NargsValue | None = None, + action: Literal['store', 'append'] | None = None, type: InputTypeFunc = None, # noqa default: Any = _NotSet, *, - default_cb: DefaultFunc = None, + default_cb: DefaultFunc | None = None, choices: ChoicesType = None, - allow_leading_dash: LeadingDash = None, + allow_leading_dash: LeadingDash | None = None, **kwargs, ): if nargs_provided := nargs is not None: diff --git a/lib/cli_command_parser/testing.py b/lib/cli_command_parser/testing.py index 844dd09c..58c86ed0 100644 --- a/lib/cli_command_parser/testing.py +++ b/lib/cli_command_parser/testing.py @@ -91,34 +91,34 @@ class ParserTest(TestCase): # print() # return super().subTest(*args, **kwargs) - def assert_dict_equal(self, d1, d2, msg: str = None): + def assert_dict_equal(self, d1, d2, msg: OptStr = None): self.assertIsInstance(d1, dict, 'First argument is not a dictionary') self.assertIsInstance(d2, dict, 'Second argument is not a dictionary') if d1 != d2: self.fail(self._formatMessage(msg, f'{d1} != {d2}\n{format_dict_diff(d1, d2)}')) - def assert_raises_contains_str(self, expected_exc: Type[BaseException], expected_text: str, msg: str = None): + def assert_raises_contains_str(self, expected_exc: Type[BaseException], expected_text: str, msg: OptStr = None): return AssertRaisesWithStringContext(self, expected_exc, expected_text, msg) - def assert_parse_results(self, cmd_cls: CommandCls, argv: Argv, expected: Expected, msg: str = None) -> Command: + def assert_parse_results(self, cmd_cls: CommandCls, argv: Argv, expected: Expected, msg: OptStr = None) -> Command: cmd = cmd_cls.parse(argv) parsed = cmd.ctx.get_parsed(cmd, exclude=EXCLUDE_ACTIONS) if expected != parsed: self.fail(msg or f'Expected results ({expected}) != {parsed=}\n{format_dict_diff(expected, parsed)}') return cmd - def assert_parse_results_cases(self, cmd_cls: CommandCls, cases: Iterable[Case], msg: str = None): + def assert_parse_results_cases(self, cmd_cls: CommandCls, cases: Iterable[Case], msg: OptStr = None): for argv, expected in cases: with self.subTest(expected='results', argv=argv): self.assert_parse_results(cmd_cls, argv, expected, msg) def assert_env_parse_results( - self, cmd_cls: CommandCls, argv: Argv, env: Env, expected: Expected, msg: str = None + self, cmd_cls: CommandCls, argv: Argv, env: Env, expected: Expected, msg: OptStr = None ) -> Command: with patch(OPT_ENV_MOD, env): return self.assert_parse_results(cmd_cls, argv, expected, msg) - def assert_env_parse_results_cases(self, cmd_cls: CommandCls, cases: Iterable[EnvCase], msg: str = None): + def assert_env_parse_results_cases(self, cmd_cls: CommandCls, cases: Iterable[EnvCase], msg: OptStr = None): for argv, env, expected in cases: with self.subTest(expected='results', argv=argv, env=env): self.assert_env_parse_results(cmd_cls, argv, env, expected, msg) @@ -128,8 +128,8 @@ def assert_parse_fails( cmd_cls: CommandCls, argv: Argv, expected_exc: ExcType = UsageError, - expected_pattern: str = None, - msg: str = None, + expected_pattern: OptStr = None, + msg: OptStr = None, regex: bool = False, ): if expected_pattern and regex: @@ -139,14 +139,14 @@ def assert_parse_fails( with AssertRaisesWithStringContext(self, expected_exc, expected_pattern, msg): cmd_cls.parse(argv) - def assert_parse_fails_cases(self, cmd_cls: CommandCls, cases: ExcCases, exc: ExcType = None, msg: str = None): + def assert_parse_fails_cases(self, cmd_cls: CommandCls, cases: ExcCases, exc: ExcType = None, msg: OptStr = None): for argv, exc, pat in _iter_exc_cases(cases, exc): with self.subTest(expected='exception', argv=argv): with AssertRaisesWithStringContext(self, exc, pat, msg): cmd_cls.parse(argv) def assert_argv_parse_fails_cases( - self, cmd_cls: CommandCls, cases: Iterable[Argv], exc: ExcType = UsageError, msg: str = None + self, cmd_cls: CommandCls, cases: Iterable[Argv], exc: ExcType = UsageError, msg: OptStr = None ): """Convenience method for calling :meth:`.assert_parse_fails_cases` with a default exception type.""" self.assert_parse_fails_cases(cmd_cls, cases, exc, msg) @@ -156,20 +156,20 @@ def assert_call_fails( func: Callable, kwargs: Kwargs, exc: ExcType = Exception, - expected_exc_msg: str = None, - msg: str = None, + expected_exc_msg: OptStr = None, + msg: OptStr = None, ): with AssertRaisesWithStringContext(self, exc, expected_exc_msg, msg): func(**kwargs) - def assert_call_fails_cases(self, func: Callable, cases: Iterable[CallExceptionCase], msg: str = None): + def assert_call_fails_cases(self, func: Callable, cases: Iterable[CallExceptionCase], msg: OptStr = None): for kwargs, exc, pat in _iter_exc_cases(cases): with self.subTest(expected='exception', kwargs=kwargs): with AssertRaisesWithStringContext(self, exc, pat, msg): func(**kwargs) def assert_strings_equal( - self, expected: str, actual: str, message: str = None, diff_lines: int = 3, trim: bool = False + self, expected: str, actual: str, message: OptStr = None, diff_lines: int = 3, trim: bool = False ): if trim: expected = expected.rstrip() @@ -198,7 +198,7 @@ def env_vars(self, case: str, **env_vars): yield -def _iter_exc_cases(cases: ExcCases | CallExceptionCases, exc: ExcType = None): +def _iter_exc_cases(cases: ExcCases | CallExceptionCases, exc: ExcType | None = None): if exc is not None: for args in cases: yield args, exc, None diff --git a/lib/cli_command_parser/typing.py b/lib/cli_command_parser/typing.py index 993d7903..beaf9cfe 100644 --- a/lib/cli_command_parser/typing.py +++ b/lib/cli_command_parser/typing.py @@ -18,7 +18,6 @@ Pattern, Sequence, TextIO, - Tuple, Type, TypeVar, Union, @@ -57,7 +56,7 @@ Strings = Collection[str] PathLike = Union[str, 'Path'] -Locale = Union[str, Tuple[Union[str, None], Union[str, None]]] +Locale = str | tuple[OptStr, OptStr] TimeBound = Union['datetime', 'date', 'time', 'timedelta', None] FP = Union[TextIO, BinaryIO] diff --git a/tests/test_conversion/test_convert_argparse.py b/tests/test_conversion/test_convert_argparse.py index 1230ee25..a08a3268 100755 --- a/tests/test_conversion/test_convert_argparse.py +++ b/tests/test_conversion/test_convert_argparse.py @@ -64,7 +64,9 @@ def prep_cmd(*members: str, name: str = CMD0, base: str = 'Command', suffix: str return cls_def_line -def prep_group(*add_args: str, title: str = None, description: str = None, parser: str = 'p', var: str = 'g') -> str: +def prep_group( + *add_args: str, title: str | None = None, description: str | None = None, parser: str = 'p', var: str = 'g' +) -> str: group_arg_str = ', '.join(f'{k}={v!r}' for k, v in {'title': title, 'description': description}.items() if v) add_arg_iter = (f'{var}.add_argument({arg})' for arg in add_args) return '\n'.join((f'{var} = {parser}.add_argument_group({group_arg_str})', *add_arg_iter)) diff --git a/tests/test_documentation/test_help_text.py b/tests/test_documentation/test_help_text.py index 15717109..a44092a2 100755 --- a/tests/test_documentation/test_help_text.py +++ b/tests/test_documentation/test_help_text.py @@ -493,9 +493,9 @@ def assert_help_and_rst_match( mode: str, param_help_map: dict[str, str], help_header: str, - cmd_mode: str = None, - sc_kwargs: dict[str, Any] = None, - cmd_kwargs: dict[str, Any] = None, + cmd_mode: str | None = None, + sc_kwargs: dict[str, Any] | None = None, + cmd_kwargs: dict[str, Any] | None = None, ) -> Iterator[CommandCls]: if not cmd_kwargs: cmd_kwargs = {} @@ -674,7 +674,9 @@ def prep_expected_rst(table_fmt_str: str, param_help_map: dict[str, str]) -> str class ShowDefaultsTest(TestCase): - def assert_default_x_in_help_text(self, defaults: Iterable[Any], expect_in: bool, check_str: str = None, **kwargs): + def assert_default_x_in_help_text( + self, defaults: Iterable[Any], expect_in: bool, check_str: str | None = None, **kwargs + ): for default in defaults: with self.subTest(default=default, expect_in=expect_in): default_str = check_str or f'(default: {default!r})' diff --git a/tests/test_documentation/test_user_docs.py b/tests/test_documentation/test_user_docs.py index cd0f3d5e..831288bc 100755 --- a/tests/test_documentation/test_user_docs.py +++ b/tests/test_documentation/test_user_docs.py @@ -3,7 +3,7 @@ import re from inspect import Signature from pathlib import Path -from typing import Callable, Collection, Dict, Set +from typing import Callable, Collection from unittest import TestCase, main from cli_command_parser.config import CommandConfig @@ -12,7 +12,7 @@ DOCS_SRC = Path(__file__).resolve().parents[2].joinpath('docs', '_src') -def get_doc_params(rst_name: str, section_start: str, section_end: str = None) -> Dict[str, str]: +def get_doc_params(rst_name: str, section_start: str, section_end: str | None = None) -> dict[str, str]: data = DOCS_SRC.joinpath(rst_name).read_text('utf-8') start = data.index(section_start) if section_end: @@ -44,7 +44,7 @@ def get_doc_params(rst_name: str, section_start: str, section_end: str = None) - return params -def get_func_params(func: Callable, skip: Collection[str] = None) -> Set[str]: +def get_func_params(func: Callable, skip: Collection[str] | None = None) -> set[str]: sig = Signature.from_callable(func) params = set(sig.parameters) if skip: diff --git a/tests/test_examples/test_examples_via_subprocess.py b/tests/test_examples/test_examples_via_subprocess.py index 61ba029d..201f962e 100755 --- a/tests/test_examples/test_examples_via_subprocess.py +++ b/tests/test_examples/test_examples_via_subprocess.py @@ -5,7 +5,6 @@ from operator import xor from pathlib import Path from subprocess import PIPE, Popen -from typing import Tuple from unittest import TestCase, TestSuite as _TestSuite, main EXAMPLES_DIR = Path(__file__).resolve().parents[2].joinpath('examples') @@ -57,13 +56,13 @@ def __eq__(self, other): class ExampleScriptTest(TestCase): - _path: str = None + _path: str | None = None def __init_subclass__(cls, file: str, **kwargs): # noqa super().__init_subclass__(**kwargs) cls._path = EXAMPLES_DIR.joinpath(file).as_posix() - def call_script(self, *args) -> Tuple[int, str, str]: + def call_script(self, *args) -> tuple[int, str, str]: proc = Popen([sys.executable, self._path, *args], text=True, stdout=PIPE, stderr=PIPE) stdout = proc.stdout.read() proc.stdout.close() diff --git a/tests/test_inputs/test_file_inputs.py b/tests/test_inputs/test_file_inputs.py index 902d8db6..9fb45355 100755 --- a/tests/test_inputs/test_file_inputs.py +++ b/tests/test_inputs/test_file_inputs.py @@ -36,7 +36,7 @@ def temp_chdir(path: Path): @contextmanager -def temp_path(file: str = None, touch: bool = False) -> Iterator[Path]: +def temp_path(file: str | None = None, touch: bool = False) -> Iterator[Path]: with TemporaryDirectory() as tmp_dir: d = Path(tmp_dir) if file: