diff --git a/CHANGES b/CHANGES index 8909f66e24..d9c7ec5bb2 100644 --- a/CHANGES +++ b/CHANGES @@ -19,6 +19,16 @@ $ pipx install --suffix=@next 'tmuxp' --pip-args '\--pre' --force +### Development + +- Aggressive automated lint fixes via `ruff` (#953) + + via ruff v0.8.4, all automated lint fixes, including unsafe and previews were applied for Python 3.9: + + ```sh + ruff check --select ALL . --fix --unsafe-fixes --preview --show-fixes; ruff format . + ``` + ## tmuxp 1.49.0 (2024-11-26) _Maintenance only, no bug fixes or new features_ diff --git a/conftest.py b/conftest.py index 16899a7a2c..54d0b18bb0 100644 --- a/conftest.py +++ b/conftest.py @@ -84,7 +84,7 @@ def monkeypatch_plugin_test_packages(monkeypatch: pytest.MonkeyPatch) -> None: @pytest.fixture -def session_params(session_params: t.Dict[str, t.Any]) -> t.Dict[str, t.Any]: +def session_params(session_params: dict[str, t.Any]) -> dict[str, t.Any]: """Terminal-friendly tmuxp session_params for dimensions.""" session_params.update({"x": 800, "y": 600}) return session_params @@ -99,7 +99,7 @@ def socket_name(request: pytest.FixtureRequest) -> str: @pytest.fixture(autouse=True) def add_doctest_fixtures( request: pytest.FixtureRequest, - doctest_namespace: t.Dict[str, t.Any], + doctest_namespace: dict[str, t.Any], tmp_path: pathlib.Path, ) -> None: """Harness pytest fixtures to doctests namespace.""" diff --git a/docs/_ext/aafig.py b/docs/_ext/aafig.py index 88b12c78d1..987cc39825 100644 --- a/docs/_ext/aafig.py +++ b/docs/_ext/aafig.py @@ -40,9 +40,9 @@ def merge_dict( - dst: t.Dict[str, t.Optional[str]], - src: t.Dict[str, t.Optional[str]], -) -> t.Dict[str, t.Optional[str]]: + dst: dict[str, t.Optional[str]], + src: dict[str, t.Optional[str]], +) -> dict[str, t.Optional[str]]: for k, v in src.items(): if k not in dst: dst[k] = v @@ -51,15 +51,15 @@ def merge_dict( def get_basename( text: str, - options: t.Dict[str, str], + options: dict[str, str], prefix: t.Optional[str] = "aafig", ) -> str: options = options.copy() if "format" in options: del options["format"] hashkey = text + str(options) - _id = sha(hashkey.encode("utf-8")).hexdigest() - return f"{prefix}-{_id}" + id_ = sha(hashkey.encode("utf-8")).hexdigest() + return f"{prefix}-{id_}" class AafigError(SphinxError): @@ -83,7 +83,7 @@ class AafigDirective(images.Image): # type:ignore option_spec = images.Image.option_spec.copy() option_spec.update(own_option_spec) - def run(self) -> t.List[nodes.Node]: + def run(self) -> list[nodes.Node]: aafig_options = {} own_options_keys = [self.own_option_spec.keys(), "scale"] for k, v in self.options.items(): @@ -120,13 +120,13 @@ def render_aafig_images(app: "Sphinx", doctree: nodes.Node) -> None: continue options = img.aafig["options"] text = img.aafig["text"] - _format = app.builder.format + format_ = app.builder.format merge_dict(options, app.builder.config.aafig_default_options) - if _format in format_map: - options["format"] = format_map[_format] + if format_ in format_map: + options["format"] = format_map[format_] else: logger.warning( - f'unsupported builder format "{_format}", please ' + f'unsupported builder format "{format_}", please ' "add a custom entry in aafig_format config " "option for this builder", ) @@ -159,8 +159,8 @@ def __init__(self, *args: object, **kwargs: object) -> None: def render_aafigure( app: "Sphinx", text: str, - options: t.Dict[str, str], -) -> t.Tuple[str, str, t.Optional[str], t.Optional[str]]: + options: dict[str, str], +) -> tuple[str, str, t.Optional[str], t.Optional[str]]: """Render an ASCII art figure into the requested format output file.""" if aafigure is None: raise AafigureNotInstalled diff --git a/docs/conf.py b/docs/conf.py index 6f232aa41c..980b848890 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -22,7 +22,7 @@ sys.path.insert(0, str(cwd / "_ext")) # package data -about: t.Dict[str, str] = {} +about: dict[str, str] = {} with (src_root / "tmuxp" / "__about__.py").open() as fp: exec(fp.read(), about) @@ -73,8 +73,8 @@ html_static_path = ["_static"] html_favicon = "_static/favicon.ico" html_theme = "furo" -html_theme_path: t.List[str] = [] -html_theme_options: t.Dict[str, t.Union[str, t.List[t.Dict[str, str]]]] = { +html_theme_path: list[str] = [] +html_theme_options: dict[str, t.Union[str, list[dict[str, str]]]] = { "light_logo": "img/tmuxp.svg", "dark_logo": "img/tmuxp.svg", "footer_icons": [ @@ -143,7 +143,7 @@ } -def linkcode_resolve(domain: str, info: t.Dict[str, str]) -> t.Union[None, str]: +def linkcode_resolve(domain: str, info: dict[str, str]) -> t.Union[None, str]: """ Determine the URL corresponding to Python object. diff --git a/pyproject.toml b/pyproject.toml index 953a65175e..d7caaef55f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -153,6 +153,7 @@ exclude_lines = [ [tool.mypy] strict = true +python_version = "3.9" files = [ "src/", "tests/", @@ -171,7 +172,7 @@ module = [ ignore_missing_imports = true [tool.ruff] -target-version = "py38" +target-version = "py39" [tool.ruff.lint] select = [ diff --git a/src/tmuxp/_internal/config_reader.py b/src/tmuxp/_internal/config_reader.py index 4ca6aa82a3..6cd862edb9 100644 --- a/src/tmuxp/_internal/config_reader.py +++ b/src/tmuxp/_internal/config_reader.py @@ -11,7 +11,7 @@ FormatLiteral = t.Literal["json", "yaml"] - RawConfigData: TypeAlias = t.Dict[t.Any, t.Any] + RawConfigData: TypeAlias = dict[t.Any, t.Any] class ConfigReader: @@ -28,7 +28,7 @@ def __init__(self, content: "RawConfigData") -> None: self.content = content @staticmethod - def _load(fmt: "FormatLiteral", content: str) -> t.Dict[str, t.Any]: + def _load(fmt: "FormatLiteral", content: str) -> dict[str, t.Any]: """Load raw config data and directly return it. >>> ConfigReader._load("json", '{ "session_name": "my session" }') @@ -39,14 +39,14 @@ def _load(fmt: "FormatLiteral", content: str) -> t.Dict[str, t.Any]: """ if fmt == "yaml": return t.cast( - t.Dict[str, t.Any], + "dict[str, t.Any]", yaml.load( content, Loader=yaml.SafeLoader, ), ) if fmt == "json": - return t.cast(t.Dict[str, t.Any], json.loads(content)) + return t.cast("dict[str, t.Any]", json.loads(content)) msg = f"{fmt} not supported in configuration" raise NotImplementedError(msg) @@ -74,7 +74,7 @@ def load(cls, fmt: "FormatLiteral", content: str) -> "ConfigReader": ) @classmethod - def _from_file(cls, path: pathlib.Path) -> t.Dict[str, t.Any]: + def _from_file(cls, path: pathlib.Path) -> dict[str, t.Any]: r"""Load data from file path directly to dictionary. **YAML file** diff --git a/src/tmuxp/_internal/types.py b/src/tmuxp/_internal/types.py index 42b55a82f7..aa2d9444db 100644 --- a/src/tmuxp/_internal/types.py +++ b/src/tmuxp/_internal/types.py @@ -10,8 +10,6 @@ ... """ -import typing as t - from typing_extensions import NotRequired, TypedDict @@ -19,10 +17,10 @@ class PluginConfigSchema(TypedDict): plugin_name: NotRequired[str] tmux_min_version: NotRequired[str] tmux_max_version: NotRequired[str] - tmux_version_incompatible: NotRequired[t.List[str]] + tmux_version_incompatible: NotRequired[list[str]] libtmux_min_version: NotRequired[str] libtmux_max_version: NotRequired[str] - libtmux_version_incompatible: NotRequired[t.List[str]] + libtmux_version_incompatible: NotRequired[list[str]] tmuxp_min_version: NotRequired[str] tmuxp_max_version: NotRequired[str] - tmuxp_version_incompatible: NotRequired[t.List[str]] + tmuxp_version_incompatible: NotRequired[list[str]] diff --git a/src/tmuxp/cli/__init__.py b/src/tmuxp/cli/__init__.py index e1ee765a77..f285cb7d9c 100644 --- a/src/tmuxp/cli/__init__.py +++ b/src/tmuxp/cli/__init__.py @@ -118,7 +118,7 @@ class CLINamespace(argparse.Namespace): ns = CLINamespace() -def cli(_args: t.Optional[t.List[str]] = None) -> None: +def cli(_args: t.Optional[list[str]] = None) -> None: """Manage tmux sessions. Pass the "--help" argument to any command to see detailed help. diff --git a/src/tmuxp/cli/debug_info.py b/src/tmuxp/cli/debug_info.py index 2ce20a2279..f8908a5ba2 100644 --- a/src/tmuxp/cli/debug_info.py +++ b/src/tmuxp/cli/debug_info.py @@ -31,7 +31,7 @@ def command_debug_info( ) -> None: """Entrypoint for ``tmuxp debug-info`` to print debug info to submit with issues.""" - def prepend_tab(strings: t.List[str]) -> t.List[str]: + def prepend_tab(strings: list[str]) -> list[str]: """Prepend tab to strings in list.""" return [f"\t{x}" for x in strings] diff --git a/src/tmuxp/cli/freeze.py b/src/tmuxp/cli/freeze.py index 2feece59fd..b6d4e27617 100644 --- a/src/tmuxp/cli/freeze.py +++ b/src/tmuxp/cli/freeze.py @@ -165,7 +165,7 @@ def command_freeze( dest = os.path.abspath(os.path.relpath(os.path.expanduser(dest))) workspace_format = args.workspace_format - valid_workspace_formats: t.List[CLIOutputFormatLiteral] = ["json", "yaml"] + valid_workspace_formats: list[CLIOutputFormatLiteral] = ["json", "yaml"] def is_valid_ext(stem: t.Optional[str]) -> "TypeGuard[CLIOutputFormatLiteral]": return stem in valid_workspace_formats @@ -184,15 +184,15 @@ def extract_workspace_format( workspace_format = extract_workspace_format(dest) if not is_valid_ext(workspace_format): - _workspace_format = prompt_choices( + workspace_format_ = prompt_choices( "Couldn't ascertain one of [{}] from file name. Convert to".format( ", ".join(valid_workspace_formats), ), - choices=t.cast(t.List[str], valid_workspace_formats), + choices=t.cast("list[str]", valid_workspace_formats), default="yaml", ) - assert is_valid_ext(_workspace_format) - workspace_format = _workspace_format + assert is_valid_ext(workspace_format_) + workspace_format = workspace_format_ if workspace_format == "yaml": workspace = configparser.dump( diff --git a/src/tmuxp/cli/import_config.py b/src/tmuxp/cli/import_config.py index 3420177f43..1f4127b61d 100644 --- a/src/tmuxp/cli/import_config.py +++ b/src/tmuxp/cli/import_config.py @@ -128,7 +128,7 @@ def create_import_subparser( class ImportConfigFn(t.Protocol): """Typing for import configuration callback function.""" - def __call__(self, workspace_dict: t.Dict[str, t.Any]) -> t.Dict[str, t.Any]: + def __call__(self, workspace_dict: dict[str, t.Any]) -> dict[str, t.Any]: """Execute tmuxp import function.""" ... diff --git a/src/tmuxp/cli/load.py b/src/tmuxp/cli/load.py index e6a5c5aad9..551d9ecf29 100644 --- a/src/tmuxp/cli/load.py +++ b/src/tmuxp/cli/load.py @@ -36,7 +36,7 @@ class OptionOverrides(TypedDict): class CLILoadNamespace(argparse.Namespace): """Typed :class:`argparse.Namespace` for tmuxp load command.""" - workspace_files: t.List[str] + workspace_files: list[str] socket_name: t.Optional[str] socket_path: t.Optional[str] tmux_config_file: t.Optional[str] @@ -47,7 +47,7 @@ class CLILoadNamespace(argparse.Namespace): log_file: t.Optional[str] -def load_plugins(session_config: t.Dict[str, t.Any]) -> t.List[t.Any]: +def load_plugins(session_config: dict[str, t.Any]) -> list[t.Any]: """Load and return plugins in workspace.""" plugins = [] if "plugins" in session_config: diff --git a/src/tmuxp/cli/utils.py b/src/tmuxp/cli/utils.py index 6c364bf2bc..83dbaa7085 100644 --- a/src/tmuxp/cli/utils.py +++ b/src/tmuxp/cli/utils.py @@ -9,7 +9,7 @@ if t.TYPE_CHECKING: from typing_extensions import TypeAlias - CLIColour: TypeAlias = t.Union[int, t.Tuple[int, int, int], str] + CLIColour: TypeAlias = t.Union[int, tuple[int, int, int], str] logger = logging.getLogger(__name__) @@ -56,10 +56,10 @@ def prompt( `flask-script `_. See the `flask-script license `_. """ - _prompt = name + ((default and f" [{default}]") or "") - _prompt += (name.endswith("?") and " ") or ": " + prompt_ = name + ((default and f" [{default}]") or "") + prompt_ += (name.endswith("?") and " ") or ": " while True: - rv = input(_prompt) or default + rv = input(prompt_) or default try: if value_proc is not None and callable(value_proc): assert isinstance(rv, str) @@ -106,11 +106,11 @@ def prompt_bool( else: prompt_choice = "y/N" - _prompt = name + f" [{prompt_choice}]" - _prompt += (name.endswith("?") and " ") or ": " + prompt_ = name + f" [{prompt_choice}]" + prompt_ += (name.endswith("?") and " ") or ": " while True: - rv = input(_prompt) + rv = input(prompt_) if not rv: return default if rv.lower() in yes_choices: @@ -126,7 +126,7 @@ def prompt_yes_no(name: str, default: bool = True) -> bool: def prompt_choices( name: str, - choices: t.Union[t.List[str], t.Tuple[str, str]], + choices: t.Union[list[str], tuple[str, str]], default: t.Optional[str] = None, no_choice: t.Sequence[str] = ("none",), ) -> t.Optional[str]: @@ -148,8 +148,8 @@ def prompt_choices( ------- str """ - _choices: t.List[str] = [] - options: t.List[str] = [] + choices_: list[str] = [] + options: list[str] = [] for choice in choices: if isinstance(choice, str): @@ -157,7 +157,7 @@ def prompt_choices( elif isinstance(choice, tuple): options.append(f"{choice} [{choice[0]}]") choice = choice[0] - _choices.append(choice) + choices_.append(choice) while True: rv = prompt(name + " - ({})".format(", ".join(options)), default=default) @@ -166,7 +166,7 @@ def prompt_choices( rv = rv.lower() if rv in no_choice: return None - if rv in _choices: + if rv in choices_: return rv @@ -201,7 +201,7 @@ def strip_ansi(value: str) -> str: def _interpret_color( - color: t.Union[int, t.Tuple[int, int, int], str], + color: t.Union[int, tuple[int, int, int], str], offset: int = 0, ) -> str: if isinstance(color, int): diff --git a/src/tmuxp/log.py b/src/tmuxp/log.py index b841d340ad..57855dcabb 100644 --- a/src/tmuxp/log.py +++ b/src/tmuxp/log.py @@ -135,7 +135,7 @@ def format(self, record: logging.LogRecord) -> str: def debug_log_template( - self: t.Type[logging.Formatter], + self: type[logging.Formatter], record: logging.LogRecord, stylized: t.Optional[bool] = False, **kwargs: t.Any, diff --git a/src/tmuxp/plugin.py b/src/tmuxp/plugin.py index adb0ac363d..9920e873f4 100644 --- a/src/tmuxp/plugin.py +++ b/src/tmuxp/plugin.py @@ -41,7 +41,7 @@ class VersionConstraints(TypedDict): version: t.Union[Version, str] vmin: str vmax: t.Optional[str] - incompatible: t.List[t.Union[t.Any, str]] + incompatible: list[t.Union[t.Any, str]] class TmuxpPluginVersionConstraints(TypedDict): """Version constraints for a tmuxp plugin.""" @@ -57,13 +57,13 @@ class Config(t.TypedDict): plugin_name: str tmux_min_version: str tmux_max_version: t.Optional[str] - tmux_version_incompatible: t.Optional[t.List[str]] + tmux_version_incompatible: t.Optional[list[str]] libtmux_min_version: str libtmux_max_version: t.Optional[str] - libtmux_version_incompatible: t.Optional[t.List[str]] + libtmux_version_incompatible: t.Optional[list[str]] tmuxp_min_version: str tmuxp_max_version: t.Optional[str] - tmuxp_version_incompatible: t.Optional[t.List[str]] + tmuxp_version_incompatible: t.Optional[list[str]] DEFAULT_CONFIG: "Config" = { @@ -200,7 +200,7 @@ def _pass_version_check( version: t.Union[str, Version], vmin: str, vmax: t.Optional[str], - incompatible: t.List[t.Union[t.Any, str]], + incompatible: list[t.Union[t.Any, str]], ) -> bool: """Provide affirmative if version compatibility is correct.""" if vmin and version < Version(vmin): diff --git a/src/tmuxp/shell.py b/src/tmuxp/shell.py index 9000779564..d7f9094b32 100644 --- a/src/tmuxp/shell.py +++ b/src/tmuxp/shell.py @@ -39,10 +39,10 @@ class LaunchImports(t.TypedDict): """tmuxp shell launch import mapping.""" libtmux: ModuleType - Server: t.Type[Server] - Session: t.Type[Session] - Window: t.Type[Window] - Pane: t.Type[Pane] + Server: type[Server] + Session: type[Session] + Window: type[Window] + Pane: type[Pane] server: t.Optional["Server"] session: t.Optional["Session"] window: t.Optional["Window"] @@ -114,7 +114,7 @@ def detect_best_shell() -> "CLIShellLiteral": def get_bpython( options: "LaunchOptionalImports", - extra_args: t.Optional[t.Dict[str, t.Any]] = None, + extra_args: t.Optional[dict[str, t.Any]] = None, ) -> t.Callable[[], None]: """Return bpython shell.""" if extra_args is None: @@ -132,7 +132,7 @@ def launch_bpython() -> None: return launch_bpython -def get_ipython_arguments() -> t.List[str]: +def get_ipython_arguments() -> list[str]: """Return ipython shell args via ``IPYTHON_ARGUMENTS`` environment variables.""" ipython_args = "IPYTHON_ARGUMENTS" return os.environ.get(ipython_args, "").split() @@ -140,7 +140,7 @@ def get_ipython_arguments() -> t.List[str]: def get_ipython( options: "LaunchOptionalImports", - **extra_args: t.Dict[str, t.Any], + **extra_args: dict[str, t.Any], ) -> t.Any: """Return ipython shell.""" try: diff --git a/src/tmuxp/workspace/builder.py b/src/tmuxp/workspace/builder.py index fa4f8330f2..4623ed4b85 100644 --- a/src/tmuxp/workspace/builder.py +++ b/src/tmuxp/workspace/builder.py @@ -149,9 +149,9 @@ class WorkspaceBuilder: def __init__( self, - session_config: t.Dict[str, t.Any], + session_config: dict[str, t.Any], server: Server, - plugins: t.Optional[t.List[t.Any]] = None, + plugins: t.Optional[list[t.Any]] = None, ) -> None: """Initialize workspace loading. @@ -443,7 +443,7 @@ def iter_create_windows( def iter_create_panes( self, window: Window, - window_config: t.Dict[str, t.Any], + window_config: dict[str, t.Any], ) -> t.Iterator[t.Any]: """Return :class:`libtmux.Pane` iterating through window config dict. @@ -479,8 +479,8 @@ def iter_create_panes( else: def get_pane_start_directory( - pane_config: t.Dict[str, str], - window_config: t.Dict[str, str], + pane_config: dict[str, str], + window_config: dict[str, str], ) -> t.Optional[str]: if "start_directory" in pane_config: return pane_config["start_directory"] @@ -489,8 +489,8 @@ def get_pane_start_directory( return None def get_pane_shell( - pane_config: t.Dict[str, str], - window_config: t.Dict[str, str], + pane_config: dict[str, str], + window_config: dict[str, str], ) -> t.Optional[str]: if "shell" in pane_config: return pane_config["shell"] @@ -565,7 +565,7 @@ def get_pane_shell( def config_after_window( self, window: Window, - window_config: t.Dict[str, t.Any], + window_config: dict[str, t.Any], ) -> None: """Actions to apply to window after window and pane finished. diff --git a/src/tmuxp/workspace/finders.py b/src/tmuxp/workspace/finders.py index 9e441e75ae..2c202bb0fe 100644 --- a/src/tmuxp/workspace/finders.py +++ b/src/tmuxp/workspace/finders.py @@ -21,7 +21,7 @@ def is_workspace_file( filename: str, - extensions: t.Union["ValidExtensions", t.List["ValidExtensions"], None] = None, + extensions: t.Union["ValidExtensions", list["ValidExtensions"], None] = None, ) -> bool: """ Return True if file has a valid workspace file type. @@ -45,8 +45,8 @@ def is_workspace_file( def in_dir( workspace_dir: t.Union[pathlib.Path, str, None] = None, - extensions: t.Optional[t.List["ValidExtensions"]] = None, -) -> t.List[str]: + extensions: t.Optional[list["ValidExtensions"]] = None, +) -> list[str]: """ Return a list of workspace_files in ``workspace_dir``. @@ -74,7 +74,7 @@ def in_dir( ] -def in_cwd() -> t.List[str]: +def in_cwd() -> list[str]: """ Return list of workspace_files in current working directory. @@ -187,7 +187,7 @@ def find_workspace_file( ] if exists(x) ] - if not len(candidates): + if not candidates: file_error = ( "workspace-file not found " + f"in workspace dir (yaml/yml/json) {workspace_dir} for name" diff --git a/src/tmuxp/workspace/freezer.py b/src/tmuxp/workspace/freezer.py index e782ccec2c..fba5103a73 100644 --- a/src/tmuxp/workspace/freezer.py +++ b/src/tmuxp/workspace/freezer.py @@ -9,7 +9,7 @@ from libtmux.window import Window -def inline(workspace_dict: t.Dict[str, t.Any]) -> t.Any: +def inline(workspace_dict: dict[str, t.Any]) -> t.Any: """Return workspace with inlined shorthands. Opposite of :meth:`loader.expand`. Parameters @@ -50,7 +50,7 @@ def inline(workspace_dict: t.Dict[str, t.Any]) -> t.Any: return workspace_dict -def freeze(session: Session) -> t.Dict[str, t.Any]: +def freeze(session: Session) -> dict[str, t.Any]: """Freeze live tmux session into a tmuxp workspacee. Parameters @@ -63,13 +63,13 @@ def freeze(session: Session) -> t.Dict[str, t.Any]: dict tmuxp compatible workspace """ - session_config: t.Dict[str, t.Any] = { + session_config: dict[str, t.Any] = { "session_name": session.session_name, "windows": [], } for window in session.windows: - window_config: t.Dict[str, t.Any] = { + window_config: dict[str, t.Any] = { "options": window.show_window_options(), "window_name": window.name, "layout": window.window_layout, @@ -88,7 +88,7 @@ def pane_has_same_path(window: "Window", pane: Pane) -> bool: window_config["start_directory"] = window.panes[0].pane_current_path for pane in window.panes: - pane_config: t.Union[str, t.Dict[str, t.Any]] = {"shell_command": []} + pane_config: t.Union[str, dict[str, t.Any]] = {"shell_command": []} assert isinstance(pane_config, dict) if "start_directory" not in window_config and pane.pane_current_path: diff --git a/src/tmuxp/workspace/importers.py b/src/tmuxp/workspace/importers.py index 9f4b1e4b16..2ee9c7880f 100644 --- a/src/tmuxp/workspace/importers.py +++ b/src/tmuxp/workspace/importers.py @@ -3,7 +3,7 @@ import typing as t -def import_tmuxinator(workspace_dict: t.Dict[str, t.Any]) -> t.Dict[str, t.Any]: +def import_tmuxinator(workspace_dict: dict[str, t.Any]) -> dict[str, t.Any]: """Return tmuxp workspace from a `tmuxinator`_ yaml workspace. .. _tmuxinator: https://github.com/aziz/tmuxinator @@ -100,7 +100,7 @@ def import_tmuxinator(workspace_dict: t.Dict[str, t.Any]) -> t.Dict[str, t.Any]: return tmuxp_workspace -def import_teamocil(workspace_dict: t.Dict[str, t.Any]) -> t.Dict[str, t.Any]: +def import_teamocil(workspace_dict: dict[str, t.Any]) -> dict[str, t.Any]: """Return tmuxp workspace from a `teamocil`_ yaml workspace. .. _teamocil: https://github.com/remiprev/teamocil diff --git a/src/tmuxp/workspace/loader.py b/src/tmuxp/workspace/loader.py index 58da0ee91a..bf9c7c098a 100644 --- a/src/tmuxp/workspace/loader.py +++ b/src/tmuxp/workspace/loader.py @@ -26,7 +26,7 @@ def expandshell(value: str) -> str: return os.path.expandvars(os.path.expanduser(value)) # NOQA: PTH111 -def expand_cmd(p: t.Dict[str, t.Any]) -> t.Dict[str, t.Any]: +def expand_cmd(p: dict[str, t.Any]) -> dict[str, t.Any]: """Resolve shell variables and expand shorthands in a tmuxp config mapping.""" if isinstance(p, str): p = {"shell_command": [p]} @@ -64,10 +64,10 @@ def expand_cmd(p: t.Dict[str, t.Any]) -> t.Dict[str, t.Any]: def expand( - workspace_dict: t.Dict[str, t.Any], + workspace_dict: dict[str, t.Any], cwd: t.Optional[t.Union[pathlib.Path, str]] = None, parent: t.Optional[t.Any] = None, -) -> t.Dict[str, t.Any]: +) -> dict[str, t.Any]: """Resolve workspace variables and expand shorthand style / inline properties. This is necessary to keep the code in the :class:`WorkspaceBuilder` clean @@ -186,7 +186,7 @@ def expand( return workspace_dict -def trickle(workspace_dict: t.Dict[str, t.Any]) -> t.Dict[str, t.Any]: +def trickle(workspace_dict: dict[str, t.Any]) -> dict[str, t.Any]: """Return a dict with "trickled down" / inherited workspace values. This will only work if workspace has been expanded to full form with diff --git a/tests/cli/test_cli.py b/tests/cli/test_cli.py index 4ad5d28204..2c271d39e2 100644 --- a/tests/cli/test_cli.py +++ b/tests/cli/test_cli.py @@ -37,7 +37,7 @@ def test_creates_config_dir_not_exists(tmp_path: pathlib.Path) -> None: ], ) def test_help( - cli_args: t.List[str], + cli_args: list[str], tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str], diff --git a/tests/cli/test_convert.py b/tests/cli/test_convert.py index 5e792a473e..d9de79ede8 100644 --- a/tests/cli/test_convert.py +++ b/tests/cli/test_convert.py @@ -4,7 +4,6 @@ import io import json import pathlib -import typing as t import pytest @@ -22,7 +21,7 @@ ], ) def test_convert( - cli_args: t.List[str], + cli_args: list[str], tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch, ) -> None: @@ -62,7 +61,7 @@ def test_convert( ], ) def test_convert_json( - cli_args: t.List[str], + cli_args: list[str], tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch, ) -> None: diff --git a/tests/cli/test_freeze.py b/tests/cli/test_freeze.py index 0dc56c48e6..0d2d184ba6 100644 --- a/tests/cli/test_freeze.py +++ b/tests/cli/test_freeze.py @@ -31,8 +31,8 @@ ) def test_freeze( server: "Server", - cli_args: t.List[str], - inputs: t.List[str], + cli_args: list[str], + inputs: list[str], tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch, ) -> None: @@ -83,8 +83,8 @@ def test_freeze( ) def test_freeze_overwrite( server: "Server", - cli_args: t.List[str], - inputs: t.List[str], + cli_args: list[str], + inputs: list[str], tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch, ) -> None: diff --git a/tests/cli/test_import.py b/tests/cli/test_import.py index 1e94d4e2ff..2e1e1497f9 100644 --- a/tests/cli/test_import.py +++ b/tests/cli/test_import.py @@ -3,7 +3,6 @@ import contextlib import io import pathlib -import typing as t import pytest @@ -13,7 +12,7 @@ @pytest.mark.parametrize("cli_args", [(["import"])]) def test_import( - cli_args: t.List[str], + cli_args: list[str], tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str], @@ -43,8 +42,8 @@ def test_import( ], ) def test_import_teamocil( - cli_args: t.List[str], - inputs: t.List[str], + cli_args: list[str], + inputs: list[str], tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch, ) -> None: @@ -90,8 +89,8 @@ def test_import_teamocil( ], ) def test_import_tmuxinator( - cli_args: t.List[str], - inputs: t.List[str], + cli_args: list[str], + inputs: list[str], tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch, ) -> None: diff --git a/tests/cli/test_load.py b/tests/cli/test_load.py index 0a1f0dd05d..fd04f48762 100644 --- a/tests/cli/test_load.py +++ b/tests/cli/test_load.py @@ -187,7 +187,7 @@ def test_load_symlinked_workspace( if t.TYPE_CHECKING: from typing_extensions import TypeAlias - ExpectedOutput: TypeAlias = t.Optional[t.Union[str, t.List[str]]] + ExpectedOutput: TypeAlias = t.Optional[t.Union[str, list[str]]] class CLILoadFixture(t.NamedTuple): @@ -197,9 +197,9 @@ class CLILoadFixture(t.NamedTuple): test_id: str # test params - cli_args: t.List[t.Union[str, t.List[str]]] - config_paths: t.List[str] - session_names: t.List[str] + cli_args: list[t.Union[str, list[str]]] + config_paths: list[str] + session_names: list[str] expected_exit_code: int expected_in_out: "ExpectedOutput" = None expected_not_in_out: "ExpectedOutput" = None @@ -207,7 +207,7 @@ class CLILoadFixture(t.NamedTuple): expected_not_in_err: "ExpectedOutput" = None -TEST_LOAD_FIXTURES: t.List[CLILoadFixture] = [ +TEST_LOAD_FIXTURES: list[CLILoadFixture] = [ CLILoadFixture( test_id="dir-relative-dot-samedir", cli_args=["load", "."], @@ -294,9 +294,9 @@ def test_load( capsys: pytest.CaptureFixture[str], monkeypatch: pytest.MonkeyPatch, test_id: str, - cli_args: t.List[str], - config_paths: t.List[str], - session_names: t.List[str], + cli_args: list[str], + config_paths: list[str], + session_names: list[str], expected_exit_code: int, expected_in_out: "ExpectedOutput", expected_not_in_out: "ExpectedOutput", @@ -362,7 +362,7 @@ def test_regression_00132_session_name_with_dots( [["load", ".", "-d"], ["load", ".tmuxp.yaml", "-d"]], ) def test_load_zsh_autotitle_warning( - cli_args: t.List[str], + cli_args: list[str], tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str], @@ -422,7 +422,7 @@ def test_load_zsh_autotitle_warning( ], ) def test_load_log_file( - cli_args: t.List[str], + cli_args: list[str], tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str], @@ -488,8 +488,8 @@ def test_load_plugins( ) def test_load_plugins_version_fail_skip( monkeypatch_plugin_test_packages: None, - cli_args: t.List[str], - inputs: t.List[str], + cli_args: list[str], + inputs: list[str], capsys: pytest.CaptureFixture[str], ) -> None: """Test tmuxp load with plugins failing version constraints can continue.""" @@ -512,8 +512,8 @@ def test_load_plugins_version_fail_skip( ) def test_load_plugins_version_fail_no_skip( monkeypatch_plugin_test_packages: None, - cli_args: t.List[str], - inputs: t.List[str], + cli_args: list[str], + inputs: list[str], monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str], ) -> None: @@ -534,7 +534,7 @@ def test_load_plugins_version_fail_no_skip( ) def test_load_plugins_plugin_missing( monkeypatch_plugin_test_packages: None, - cli_args: t.List[str], + cli_args: list[str], capsys: pytest.CaptureFixture[str], ) -> None: """Test tmuxp load with plugins missing raise an error.""" diff --git a/tests/cli/test_shell.py b/tests/cli/test_shell.py index 39f7e7d694..6690b2a333 100644 --- a/tests/cli/test_shell.py +++ b/tests/cli/test_shell.py @@ -22,13 +22,13 @@ class CLIShellFixture(t.NamedTuple): test_id: str # test params - cli_args: t.List[str] - inputs: t.List[t.Any] - env: t.Dict[str, str] + cli_args: list[str] + inputs: list[t.Any] + env: dict[str, str] expected_output: str -TEST_SHELL_FIXTURES: t.List[CLIShellFixture] = [ +TEST_SHELL_FIXTURES: list[CLIShellFixture] = [ CLIShellFixture( test_id="print-socket-name", cli_args=["-L{SOCKET_NAME}", "-c", "print(str(server.socket_name))"], @@ -108,11 +108,11 @@ class CLIShellFixture(t.NamedTuple): ids=[test.test_id for test in TEST_SHELL_FIXTURES], ) def test_shell( - cli_cmd: t.List[str], + cli_cmd: list[str], test_id: str, - cli_args: t.List[str], - inputs: t.List[t.Any], - env: t.Dict[str, str], + cli_args: list[str], + inputs: list[t.Any], + env: dict[str, str], expected_output: str, server: "Server", session: Session, @@ -196,14 +196,14 @@ def test_shell( ], ) def test_shell_target_missing( - cli_cmd: t.List[str], - cli_args: t.List[str], - inputs: t.List[t.Any], - env: t.Dict[t.Any, t.Any], - template_ctx: t.Dict[str, str], + cli_cmd: list[str], + cli_args: list[str], + inputs: list[t.Any], + env: dict[t.Any, t.Any], + template_ctx: dict[str, str], exception: t.Union[ - t.Type[exc.TmuxpException], - t.Type[subprocess.CalledProcessError], + type[exc.TmuxpException], + type[subprocess.CalledProcessError], ], message: str, socket_name: str, @@ -279,10 +279,10 @@ def test_shell_target_missing( ], ) def test_shell_interactive( - cli_cmd: t.List[str], - cli_args: t.List[str], - inputs: t.List[t.Any], - env: t.Dict[str, str], + cli_cmd: list[str], + cli_args: list[str], + inputs: list[t.Any], + env: dict[str, str], message: str, server: "Server", session: Session, diff --git a/tests/fixtures/pluginsystem/partials/_types.py b/tests/fixtures/pluginsystem/partials/_types.py index d275ad8982..77d2328cd5 100644 --- a/tests/fixtures/pluginsystem/partials/_types.py +++ b/tests/fixtures/pluginsystem/partials/_types.py @@ -10,8 +10,6 @@ ... """ -import typing as t - from typing_extensions import NotRequired, TypedDict @@ -26,10 +24,10 @@ class PluginTestConfigSchema(TypedDict): plugin_name: NotRequired[str] tmux_min_version: NotRequired[str] tmux_max_version: NotRequired[str] - tmux_version_incompatible: NotRequired[t.List[str]] + tmux_version_incompatible: NotRequired[list[str]] libtmux_min_version: NotRequired[str] libtmux_max_version: NotRequired[str] - libtmux_version_incompatible: NotRequired[t.List[str]] + libtmux_version_incompatible: NotRequired[list[str]] tmuxp_min_version: NotRequired[str] tmuxp_max_version: NotRequired[str] - tmuxp_version_incompatible: NotRequired[t.List[str]] + tmuxp_version_incompatible: NotRequired[list[str]] diff --git a/tests/fixtures/utils.py b/tests/fixtures/utils.py index 9e6b454bf6..81fd07cf89 100644 --- a/tests/fixtures/utils.py +++ b/tests/fixtures/utils.py @@ -7,23 +7,23 @@ def get_workspace_file( - _file: t.Union[str, pathlib.Path], + file: t.Union[str, pathlib.Path], ) -> pathlib.Path: """Return fixture data, relative to __file__.""" - if isinstance(_file, str): - _file = pathlib.Path(_file) + if isinstance(file, str): + file = pathlib.Path(file) - return FIXTURE_PATH / _file + return FIXTURE_PATH / file def read_workspace_file( - _file: t.Union[pathlib.Path, str], + file: t.Union[pathlib.Path, str], ) -> str: """Return fixture data, relative to __file__.""" - if isinstance(_file, str): - _file = pathlib.Path(_file) + if isinstance(file, str): + file = pathlib.Path(file) - return get_workspace_file(_file).open().read() + return get_workspace_file(file).open().read() def write_config( diff --git a/tests/fixtures/workspace/expand1.py b/tests/fixtures/workspace/expand1.py index 75db0e7f48..75d5d06c00 100644 --- a/tests/fixtures/workspace/expand1.py +++ b/tests/fixtures/workspace/expand1.py @@ -33,7 +33,7 @@ } -def after_workspace() -> t.Dict[str, t.Any]: +def after_workspace() -> dict[str, t.Any]: """After expansion of shorthand style.""" return { "session_name": "sample workspace", diff --git a/tests/fixtures/workspace/shell_command_before.py b/tests/fixtures/workspace/shell_command_before.py index a0f80510c5..10b74518dd 100644 --- a/tests/fixtures/workspace/shell_command_before.py +++ b/tests/fixtures/workspace/shell_command_before.py @@ -40,7 +40,7 @@ } -def config_expanded() -> t.Dict[str, t.Any]: +def config_expanded() -> dict[str, t.Any]: """Return expanded configuration for shell_command_before example.""" return { # shell_command_before is string in some areas "session_name": "sample workspace", @@ -93,7 +93,7 @@ def config_expanded() -> t.Dict[str, t.Any]: } -def config_after() -> t.Dict[str, t.Any]: +def config_after() -> dict[str, t.Any]: """Return expected configuration for shell_command_before example.""" return { # shell_command_before is string in some areas "session_name": "sample workspace", diff --git a/tests/workspace/test_builder.py b/tests/workspace/test_builder.py index 801b462d5a..21ba3c8e59 100644 --- a/tests/workspace/test_builder.py +++ b/tests/workspace/test_builder.py @@ -94,12 +94,12 @@ def test_focus_pane_index(session: Session) -> None: assert session.active_window.name == "focused window" - _pane_base_index = session.active_window.show_window_option( + pane_base_index_ = session.active_window.show_window_option( "pane-base-index", g=True, ) - assert isinstance(_pane_base_index, int) - pane_base_index = int(_pane_base_index) + assert isinstance(pane_base_index_, int) + pane_base_index = int(pane_base_index_) pane_base_index = 0 if not pane_base_index else int(pane_base_index) @@ -215,9 +215,9 @@ def f(p: Pane, buffer_name: str, assertCase: AssertCallbackProtocol) -> bool: return assertCase(sent_cmd, history_cmd) - _f = functools.partial(f, p=p, buffer_name=buffer_name, assertCase=assertCase) + f_ = functools.partial(f, p=p, buffer_name=buffer_name, assertCase=assertCase) - assert retry_until(_f), f"Unknown sent command: [{sent_cmd}] in {assertCase}" + assert retry_until(f_), f"Unknown sent command: [{sent_cmd}] in {assertCase}" def test_session_options(session: Session) -> None: @@ -230,13 +230,13 @@ def test_session_options(session: Session) -> None: builder = WorkspaceBuilder(session_config=workspace, server=session.server) builder.build(session=session) - _default_shell = session.show_option("default-shell") - assert isinstance(_default_shell, str) - assert "/bin/sh" in _default_shell + default_shell = session.show_option("default-shell") + assert isinstance(default_shell, str) + assert "/bin/sh" in default_shell - _default_command = session.show_option("default-command") - assert isinstance(_default_command, str) - assert "/bin/sh" in _default_command + default_command = session.show_option("default-command") + assert isinstance(default_command, str) + assert "/bin/sh" in default_command def test_global_options(session: Session) -> None: @@ -249,9 +249,9 @@ def test_global_options(session: Session) -> None: builder = WorkspaceBuilder(session_config=workspace, server=session.server) builder.build(session=session) - _status_position = session.show_option("status-position", _global=True) - assert isinstance(_status_position, str) - assert "top" in _status_position + status_position = session.show_option("status-position", _global=True) + assert isinstance(status_position, str) + assert "top" in status_position assert session.show_option("repeat-time", _global=True) == 493 @@ -275,9 +275,9 @@ def test_global_session_env_options( builder = WorkspaceBuilder(session_config=workspace, server=session.server) builder.build(session=session) - _visual_silence = session.show_option("visual-silence", _global=True) - assert isinstance(_visual_silence, str) - assert visual_silence in _visual_silence + visual_silence_ = session.show_option("visual-silence", _global=True) + assert isinstance(visual_silence_, str) + assert visual_silence in visual_silence_ assert repeat_time == session.show_option("repeat-time") assert main_pane_height == session.active_window.show_window_option( "main-pane-height", @@ -376,9 +376,9 @@ def test_window_shell( def f(w: Window) -> bool: return w.window_name != "top" - _f = functools.partial(f, w=w) + f_ = functools.partial(f, w=w) - retry_until(_f) + retry_until(f_) assert w.name != "top" @@ -592,10 +592,10 @@ def f(path: str, p: Pane) -> bool: pane_path is not None and path in pane_path ) or pane_path == path - _f = functools.partial(f, path=path, p=p) + f_ = functools.partial(f, path=path, p=p) # handle case with OS X adding /private/ to /tmp/ paths - assert retry_until(_f) + assert retry_until(f_) def test_start_directory_relative(session: Session, tmp_path: pathlib.Path) -> None: @@ -647,10 +647,10 @@ def f(path: str, p: Pane) -> bool: pane_path is not None and path in pane_path ) or pane_path == path - _f = functools.partial(f, path=path, p=p) + f_ = functools.partial(f, path=path, p=p) # handle case with OS X adding /private/ to /tmp/ paths - assert retry_until(_f) + assert retry_until(f_) @pytest.mark.skipif( @@ -726,9 +726,9 @@ def f(pane_path: str, p: Pane) -> bool: p.refresh() return p.pane_current_path == pane_path - _f = functools.partial(f, pane_path=pane_path, p=p) + f_ = functools.partial(f, pane_path=pane_path, p=p) - assert retry_until(_f) + assert retry_until(f_) def test_window_index( @@ -1406,10 +1406,10 @@ def f(path: str, p: Pane) -> bool: pane_path = p.pane_current_path return (pane_path is not None and path in pane_path) or pane_path == path - _f = functools.partial(f, path=path, p=p) + f_ = functools.partial(f, path=path, p=p) # handle case with OS X adding /private/ to /tmp/ paths - assert retry_until(_f) + assert retry_until(f_) @pytest.mark.skipif( @@ -1463,7 +1463,7 @@ class DefaultSizeNamespaceFixture(t.NamedTuple): # test params TMUXP_DEFAULT_SIZE: t.Optional[str] raises: bool - confoverrides: t.Dict[str, t.Any] + confoverrides: dict[str, t.Any] DEFAULT_SIZE_FIXTURES = [ @@ -1500,7 +1500,7 @@ def test_issue_800_default_size_many_windows( test_id: str, TMUXP_DEFAULT_SIZE: t.Optional[str], raises: bool, - confoverrides: t.Dict[str, t.Any], + confoverrides: dict[str, t.Any], ) -> None: """Recreate default-size issue. diff --git a/tests/workspace/test_config.py b/tests/workspace/test_config.py index 66adee78d6..f04cd1d93b 100644 --- a/tests/workspace/test_config.py +++ b/tests/workspace/test_config.py @@ -14,7 +14,7 @@ from tests.fixtures.structures import WorkspaceTestData -def load_workspace(path: t.Union[str, pathlib.Path]) -> t.Dict[str, t.Any]: +def load_workspace(path: t.Union[str, pathlib.Path]) -> dict[str, t.Any]: """Load tmuxp workspace configuration from file.""" return ConfigReader._from_file( pathlib.Path(path) if isinstance(path, str) else path, diff --git a/tests/workspace/test_import_teamocil.py b/tests/workspace/test_import_teamocil.py index 8ae913c61e..347fe26627 100644 --- a/tests/workspace/test_import_teamocil.py +++ b/tests/workspace/test_import_teamocil.py @@ -36,8 +36,8 @@ ) def test_config_to_dict( teamocil_yaml: str, - teamocil_dict: t.Dict[str, t.Any], - tmuxp_dict: t.Dict[str, t.Any], + teamocil_dict: dict[str, t.Any], + tmuxp_dict: dict[str, t.Any], ) -> None: """Test exporting teamocil configuration to dictionary.""" yaml_to_dict = config_reader.ConfigReader._load( @@ -53,9 +53,9 @@ def test_config_to_dict( @pytest.fixture(scope="module") def multisession_config() -> ( - t.Dict[ + dict[ str, - t.Dict[str, t.Any], + dict[str, t.Any], ] ): """Return loaded multisession teamocil config as a dictionary. @@ -65,7 +65,7 @@ def multisession_config() -> ( """ teamocil_yaml_file = fixtures.layouts.teamocil_yaml_file test_config = config_reader.ConfigReader._from_file(teamocil_yaml_file) - teamocil_dict: t.Dict[str, t.Any] = fixtures.layouts.teamocil_dict + teamocil_dict: dict[str, t.Any] = fixtures.layouts.teamocil_dict assert test_config == teamocil_dict return teamocil_dict @@ -88,8 +88,8 @@ def multisession_config() -> ( ) def test_multisession_config( session_name: str, - expected: t.Dict[str, t.Any], - multisession_config: t.Dict[str, t.Any], + expected: dict[str, t.Any], + multisession_config: dict[str, t.Any], ) -> None: """Test importing teamocil multisession configuration.""" # teamocil can fit multiple sessions in a config diff --git a/tests/workspace/test_import_tmuxinator.py b/tests/workspace/test_import_tmuxinator.py index d5dfe3ba2e..086c382f83 100644 --- a/tests/workspace/test_import_tmuxinator.py +++ b/tests/workspace/test_import_tmuxinator.py @@ -31,8 +31,8 @@ ) def test_config_to_dict( tmuxinator_yaml: str, - tmuxinator_dict: t.Dict[str, t.Any], - tmuxp_dict: t.Dict[str, t.Any], + tmuxinator_dict: dict[str, t.Any], + tmuxp_dict: dict[str, t.Any], ) -> None: """Test exporting tmuxinator configuration to dictionary.""" yaml_to_dict = ConfigReader._load(fmt="yaml", content=tmuxinator_yaml)