diff --git a/pyproject.toml b/pyproject.toml index 658a1b5b66..ac1b759670 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -76,6 +76,7 @@ lint.extend-select = [ "PLE", "PLW", "RSE", + "RUF012", "RUF100", "W", ] diff --git a/src/pipx/animate.py b/src/pipx/animate.py index 327911dbb6..ed1dcd6f30 100644 --- a/src/pipx/animate.py +++ b/src/pipx/animate.py @@ -21,7 +21,7 @@ import ctypes class _CursorInfo(ctypes.Structure): - _fields_ = [("size", ctypes.c_int), ("visible", ctypes.c_byte)] + _fields_ = (("size", ctypes.c_int), ("visible", ctypes.c_byte)) def _env_supports_animation() -> bool: diff --git a/src/pipx/pipx_metadata_file.py b/src/pipx/pipx_metadata_file.py index 7d04062af5..0c3796784c 100644 --- a/src/pipx/pipx_metadata_file.py +++ b/src/pipx/pipx_metadata_file.py @@ -1,7 +1,8 @@ import json import logging +from dataclasses import asdict, dataclass, field from pathlib import Path -from typing import Any, Dict, List, NamedTuple, Optional, Union +from typing import Any, Dict, List, Optional, Union from pipx.emojis import hazard from pipx.util import PipxError, pipx_wrap @@ -26,7 +27,8 @@ def _json_decoder_object_hook(json_dict: Dict[str, Any]) -> Union[Dict[str, Any] return json_dict -class PackageInfo(NamedTuple): +@dataclass(frozen=True) +class PackageInfo: package: Optional[str] package_or_url: Optional[str] pip_args: List[str] @@ -37,10 +39,10 @@ class PackageInfo(NamedTuple): apps_of_dependencies: List[str] app_paths_of_dependencies: Dict[str, List[Path]] package_version: str - man_pages: List[str] = [] - man_paths: List[Path] = [] - man_pages_of_dependencies: List[str] = [] - man_paths_of_dependencies: Dict[str, List[Path]] = {} + man_pages: List[str] = field(default_factory=list) + man_paths: List[Path] = field(default_factory=list) + man_pages_of_dependencies: List[str] = field(default_factory=list) + man_paths_of_dependencies: Dict[str, List[Path]] = field(default_factory=dict) suffix: str = "" @@ -85,11 +87,11 @@ def __init__(self, venv_dir: Path, read: bool = True): def to_dict(self) -> Dict[str, Any]: return { - "main_package": self.main_package._asdict(), + "main_package": asdict(self.main_package), "python_version": self.python_version, "source_interpreter": self.source_interpreter, "venv_args": self.venv_args, - "injected_packages": {name: data._asdict() for (name, data) in self.injected_packages.items()}, + "injected_packages": {name: asdict(data) for (name, data) in self.injected_packages.items()}, "pipx_metadata_version": self.__METADATA_VERSION__, } diff --git a/tests/helpers.py b/tests/helpers.py index c2d2cb875f..888f9d4559 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -2,6 +2,7 @@ import os import re import sys +from dataclasses import replace from pathlib import Path from typing import Any, Dict, List, Optional from unittest import mock @@ -195,13 +196,15 @@ def assert_package_metadata(test_metadata, ref_metadata): assert isinstance(test_metadata.apps, list) assert isinstance(test_metadata.app_paths, list) - test_metadata_replaced = test_metadata._replace( + test_metadata_replaced = replace( + test_metadata, apps=sorted(test_metadata.apps), app_paths=sorted(test_metadata.app_paths), apps_of_dependencies=sorted(test_metadata.apps_of_dependencies), app_paths_of_dependencies={key: sorted(value) for key, value in test_metadata.app_paths_of_dependencies.items()}, ) - ref_metadata_replaced = ref_metadata._replace( + ref_metadata_replaced = replace( + ref_metadata, apps=sorted(ref_metadata.apps), app_paths=sorted(ref_metadata.app_paths), apps_of_dependencies=sorted(ref_metadata.apps_of_dependencies), diff --git a/tests/test_pipx_metadata_file.py b/tests/test_pipx_metadata_file.py index 9e1cc78412..83f7579154 100644 --- a/tests/test_pipx_metadata_file.py +++ b/tests/test_pipx_metadata_file.py @@ -1,4 +1,5 @@ import sys +from dataclasses import replace from pathlib import Path import pytest # type: ignore @@ -68,9 +69,9 @@ def test_pipx_metadata_file_create(tmp_path): @pytest.mark.parametrize( "test_package", [ - TEST_PACKAGE1._replace(include_apps=False), - TEST_PACKAGE1._replace(package=None), - TEST_PACKAGE1._replace(package_or_url=None), + replace(TEST_PACKAGE1, include_apps=False), + replace(TEST_PACKAGE1, package=None), + replace(TEST_PACKAGE1, package_or_url=None), ], ) def test_pipx_metadata_file_validation(tmp_path, test_package):