From 18dad912221634e25adb4a171b220baa8b50c90f Mon Sep 17 00:00:00 2001 From: Mathieu Kniewallner Date: Tue, 9 May 2023 20:14:40 +0200 Subject: [PATCH] chore: add more Ruff rules (#379) * chore(ruff): enable `flake8-logging-format` * chore: comply with `flake8-logging-format` * chore(ruff): enable `flake8-pytest-style` * chore(ruff): enable `flake8-use-pathlib` * chore: comply with `flake8-use-pathlib` * test: use `touch` to create files * chore(ruff): enable `flake8-blind-except` * chore(ruff): enable `pep8-naming` --- deptry/core.py | 2 +- deptry/dependency.py | 8 ++- deptry/dependency_getter/base.py | 6 +- deptry/dependency_getter/requirements_txt.py | 12 ++-- deptry/dependency_specification_detector.py | 12 ++-- deptry/exceptions.py | 7 +- deptry/imports/extract.py | 8 +-- deptry/imports/extractors/base.py | 2 +- .../extractors/notebook_import_extractor.py | 6 +- .../extractors/python_import_extractor.py | 6 +- deptry/issues_finder/misplaced_dev.py | 15 +++-- deptry/issues_finder/missing.py | 6 +- deptry/issues_finder/obsolete.py | 6 +- deptry/issues_finder/transitive.py | 6 +- deptry/reporters/json.py | 3 +- deptry/utils.py | 5 +- pyproject.toml | 10 +++ tests/functional/cli/test_cli.py | 3 +- tests/unit/dependency_getter/test_pdm.py | 4 +- tests/unit/dependency_getter/test_pep_621.py | 2 +- tests/unit/dependency_getter/test_poetry.py | 2 +- .../test_requirements_txt.py | 32 +++++---- tests/unit/imports/test_extract.py | 66 ++++++++++--------- tests/unit/reporters/test_json_reporter.py | 2 +- tests/unit/test_config.py | 28 +++++--- .../test_dependency_specification_detector.py | 42 +++++++----- tests/unit/test_python_file_finder.py | 2 +- tests/utils.py | 13 ++-- 28 files changed, 180 insertions(+), 136 deletions(-) diff --git a/deptry/core.py b/deptry/core.py index a0b0569f..dada2c94 100644 --- a/deptry/core.py +++ b/deptry/core.py @@ -179,7 +179,7 @@ def _get_stdlib_modules() -> frozenset[str]: def _log_config(self) -> None: logging.debug("Running with the following configuration:") for key, value in vars(self).items(): - logging.debug(f"{key}: {value}") + logging.debug("%s: %s", key, value) logging.debug("") @staticmethod diff --git a/deptry/dependency.py b/deptry/dependency.py index 8fb08b64..860002f0 100644 --- a/deptry/dependency.py +++ b/deptry/dependency.py @@ -73,8 +73,12 @@ def _get_top_levels( # we'll guess the name. module_name = name.replace("-", "_").lower() logging.warning( - f"Assuming the corresponding module name of package {self.name!r} is {module_name!r}. Install the package" - " or configure a package_module_name_map entry to override this behaviour." + ( + "Assuming the corresponding module name of package %r is %r. Install the package or configure a" + " package_module_name_map entry to override this behaviour." + ), + self.name, + module_name, ) return {module_name} diff --git a/deptry/dependency_getter/base.py b/deptry/dependency_getter/base.py index 34042af3..c0482636 100644 --- a/deptry/dependency_getter/base.py +++ b/deptry/dependency_getter/base.py @@ -39,7 +39,9 @@ def get(self) -> DependenciesExtract: @staticmethod def _log_dependencies(dependencies: list[Dependency], is_dev: bool = False) -> None: - logging.debug(f"The project contains the following {'dev ' if is_dev else ''}dependencies:") + logging.debug("The project contains the following %s:", "dev dependencies" if is_dev else "dependencies") + for dependency in dependencies: - logging.debug(str(dependency)) + logging.debug(dependency) + logging.debug("") diff --git a/deptry/dependency_getter/requirements_txt.py b/deptry/dependency_getter/requirements_txt.py index b4d500c1..97b74094 100644 --- a/deptry/dependency_getter/requirements_txt.py +++ b/deptry/dependency_getter/requirements_txt.py @@ -45,18 +45,20 @@ def _scan_for_dev_requirements_files(self) -> list[str]: """ dev_requirements_files = [file_name for file_name in self.requirements_txt_dev if file_name in os.listdir()] if dev_requirements_files: - logging.debug(f"Found files with development requirements! {dev_requirements_files}") + logging.debug("Found files with development requirements! %s", dev_requirements_files) return dev_requirements_files def _get_dependencies_from_requirements_file(self, file_name: str, is_dev: bool = False) -> list[Dependency]: - logging.debug(f"Scanning {file_name} for {'dev ' if is_dev else ''}dependencies") + logging.debug("Scanning %s for %s", file_name, "dev dependencies" if is_dev else "dependencies") dependencies = [] - with open(file_name) as f: + file_path = Path(file_name) + + with file_path.open() as f: data = f.readlines() for line in data: - dependency = self._extract_dependency_from_line(line, Path(file_name)) + dependency = self._extract_dependency_from_line(line, file_path) if dependency: dependencies.append(dependency) @@ -132,5 +134,5 @@ def _extract_name_from_url(line: str) -> str | None: if match: return match.group(1) - logging.warning(f"Could not parse dependency name from url {line}") + logging.warning("Could not parse dependency name from url %s", line) return None diff --git a/deptry/dependency_specification_detector.py b/deptry/dependency_specification_detector.py index 97edcc8b..92bb4839 100644 --- a/deptry/dependency_specification_detector.py +++ b/deptry/dependency_specification_detector.py @@ -1,16 +1,12 @@ from __future__ import annotations import logging -import os from enum import Enum -from typing import TYPE_CHECKING +from pathlib import Path from deptry.exceptions import DependencySpecificationNotFoundError from deptry.utils import load_pyproject_toml -if TYPE_CHECKING: - from pathlib import Path - class DependencyManagementFormat(Enum): PDM = "pdm" @@ -105,10 +101,10 @@ def _project_uses_pep_621(self) -> bool: return True def _project_uses_requirements_txt(self) -> bool: - check = any(os.path.isfile(requirements_txt) for requirements_txt in self.requirements_txt) + check = any(Path(requirements_txt).is_file() for requirements_txt in self.requirements_txt) if check: logging.debug( - f"Dependency specification found in '{', '.join(self.requirements_txt)}'. Will use this to determine" - " the project's dependencies.\n" + "Dependency specification found in '%s'. Will use this to determine the project's dependencies.\n", + ", ".join(self.requirements_txt), ) return check diff --git a/deptry/exceptions.py b/deptry/exceptions.py index 7c3b94a2..7685d054 100644 --- a/deptry/exceptions.py +++ b/deptry/exceptions.py @@ -1,5 +1,10 @@ from __future__ import annotations +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from pathlib import Path + class DependencySpecificationNotFoundError(FileNotFoundError): def __init__(self, requirements_txt: tuple[str, ...]) -> None: @@ -15,7 +20,7 @@ def __init__(self) -> None: class PyprojectFileNotFoundError(FileNotFoundError): - def __init__(self, directory: str) -> None: + def __init__(self, directory: Path) -> None: super().__init__(f"No file `pyproject.toml` found in directory {directory}") diff --git a/deptry/imports/extract.py b/deptry/imports/extract.py index 924a558c..d195ab37 100644 --- a/deptry/imports/extract.py +++ b/deptry/imports/extract.py @@ -14,7 +14,7 @@ def get_imported_modules_for_list_of_files(list_of_files: list[Path]) -> dict[str, list[Location]]: - logging.info(f"Scanning {len(list_of_files)} file{'s' if len(list_of_files)>1 else ''}...") + logging.info("Scanning %d %s...", len(list_of_files), "files" if len(list_of_files) > 1 else "file") modules: dict[str, list[Location]] = defaultdict(list) @@ -23,17 +23,17 @@ def get_imported_modules_for_list_of_files(list_of_files: list[Path]) -> dict[st for location in locations: modules[module].append(location) - logging.debug(f"All imported modules: {modules}\n") + logging.debug("All imported modules: %s\n", modules) return modules def get_imported_modules_from_file(path_to_file: Path) -> dict[str, list[Location]]: - logging.debug(f"Scanning {path_to_file}...") + logging.debug("Scanning %s...", path_to_file) modules = _get_extractor_class(path_to_file)(path_to_file).extract_imports() - logging.debug(f"Found the following imports in {str(path_to_file)}: {modules}") + logging.debug("Found the following imports in %s: %s", path_to_file, modules) return modules diff --git a/deptry/imports/extractors/base.py b/deptry/imports/extractors/base.py index a0bbd49e..7df4bdc9 100644 --- a/deptry/imports/extractors/base.py +++ b/deptry/imports/extractors/base.py @@ -51,5 +51,5 @@ def _extract_imports_from_ast(self, tree: ast.AST) -> dict[str, list[Location]]: @staticmethod def _get_file_encoding(file: Path) -> str: - with open(file, "rb") as f: + with file.open("rb") as f: return chardet.detect(f.read())["encoding"] diff --git a/deptry/imports/extractors/notebook_import_extractor.py b/deptry/imports/extractors/notebook_import_extractor.py index d4d776c3..71f7cf22 100644 --- a/deptry/imports/extractors/notebook_import_extractor.py +++ b/deptry/imports/extractors/notebook_import_extractor.py @@ -34,14 +34,14 @@ def extract_imports(self) -> dict[str, list[Location]]: @classmethod def _read_ipynb_file(cls, path_to_ipynb: Path) -> dict[str, Any] | None: try: - with open(path_to_ipynb) as ipynb_file: + with path_to_ipynb.open() as ipynb_file: notebook: dict[str, Any] = json.load(ipynb_file) except (UnicodeDecodeError, ValueError): try: - with open(path_to_ipynb, encoding=cls._get_file_encoding(path_to_ipynb)) as ipynb_file: + with path_to_ipynb.open(encoding=cls._get_file_encoding(path_to_ipynb)) as ipynb_file: notebook = json.load(ipynb_file, strict=False) except UnicodeDecodeError: - logging.warning(f"Warning: File {path_to_ipynb} could not be decoded. Skipping...") + logging.warning("Warning: File %s could not be decoded. Skipping...", path_to_ipynb) return None return notebook diff --git a/deptry/imports/extractors/python_import_extractor.py b/deptry/imports/extractors/python_import_extractor.py index 436b6303..78394682 100644 --- a/deptry/imports/extractors/python_import_extractor.py +++ b/deptry/imports/extractors/python_import_extractor.py @@ -18,14 +18,14 @@ class PythonImportExtractor(ImportExtractor): def extract_imports(self) -> dict[str, list[Location]]: """Extract all imported top-level modules from the Python file.""" try: - with open(self.file) as python_file: + with self.file.open() as python_file: tree = ast.parse(python_file.read(), str(self.file)) except (UnicodeDecodeError, ValueError): try: - with open(self.file, encoding=self._get_file_encoding(self.file)) as python_file: + with self.file.open(encoding=self._get_file_encoding(self.file)) as python_file: tree = ast.parse(python_file.read(), str(self.file)) except UnicodeDecodeError: - logging.warning(f"Warning: File {self.file} could not be decoded. Skipping...") + logging.warning("Warning: File %s could not be decoded. Skipping...", self.file) return {} return self._extract_imports_from_ast(tree) diff --git a/deptry/issues_finder/misplaced_dev.py b/deptry/issues_finder/misplaced_dev.py index 323cbf40..367cbfb3 100644 --- a/deptry/issues_finder/misplaced_dev.py +++ b/deptry/issues_finder/misplaced_dev.py @@ -33,7 +33,7 @@ def find(self) -> list[Violation]: for module_with_locations in self.imported_modules_with_locations: module = module_with_locations.module - logging.debug(f"Scanning module {module.name}...") + logging.debug("Scanning module %s...", module.name) corresponding_package_name = self._get_package_name(module) if corresponding_package_name and self._is_development_dependency(module, corresponding_package_name): @@ -50,12 +50,12 @@ def _is_development_dependency(self, module: Module, corresponding_package_name: if module.name in self.ignored_modules: logging.debug( - f"Dependency '{corresponding_package_name}' found to be a misplaced development dependency, but" - " ignoring." + "Dependency '%s' found to be a misplaced development dependency, but ignoring.", + corresponding_package_name, ) return False - logging.debug(f"Dependency '{corresponding_package_name}' marked as a misplaced development dependency.") + logging.debug("Dependency '%s' marked as a misplaced development dependency.", corresponding_package_name) return True def _get_package_name(self, module: Module) -> str | None: @@ -64,12 +64,13 @@ def _get_package_name(self, module: Module) -> str | None: if module.dev_top_levels: if len(module.dev_top_levels) > 1: logging.debug( - f"Module {module.name} is found in the top-level module names of multiple development dependencies." - " Skipping." + "Module %s is found in the top-level module names of multiple development dependencies. Skipping.", + module.name, ) elif len(module.dev_top_levels) == 0: logging.debug( - f"Module {module.name} has no metadata and it is not found in any top-level module names. Skipping." + "Module %s has no metadata and it is not found in any top-level module names. Skipping.", + module.name, ) else: return module.dev_top_levels[0] diff --git a/deptry/issues_finder/missing.py b/deptry/issues_finder/missing.py index a4cce912..610938b6 100644 --- a/deptry/issues_finder/missing.py +++ b/deptry/issues_finder/missing.py @@ -25,7 +25,7 @@ def find(self) -> list[Violation]: for module_with_locations in self.imported_modules_with_locations: module = module_with_locations.module - logging.debug(f"Scanning module {module.name}...") + logging.debug("Scanning module %s...", module.name) if self._is_missing(module): for location in module_with_locations.locations: @@ -45,8 +45,8 @@ def _is_missing(self, module: Module) -> bool: return False if module.name in self.ignored_modules: - logging.debug(f"Identified module '{module.name}' as a missing dependency, but ignoring.") + logging.debug("Identified module '%s' as a missing dependency, but ignoring.", module.name) return False - logging.debug(f"No package found to import module '{module.name}' from. Marked as a missing dependency.") + logging.debug("No package found to import module '%s' from. Marked as a missing dependency.", module.name) return True diff --git a/deptry/issues_finder/obsolete.py b/deptry/issues_finder/obsolete.py index 4841d569..24ced8ed 100644 --- a/deptry/issues_finder/obsolete.py +++ b/deptry/issues_finder/obsolete.py @@ -32,7 +32,7 @@ def find(self) -> list[Violation]: obsolete_dependencies: list[Violation] = [] for dependency in self.dependencies: - logging.debug(f"Scanning module {dependency.name}...") + logging.debug("Scanning module %s...", dependency.name) if self._is_obsolete(dependency): obsolete_dependencies.append( @@ -46,10 +46,10 @@ def _is_obsolete(self, dependency: Dependency) -> bool: return False if dependency.name in self.ignored_modules: - logging.debug(f"Dependency '{dependency.name}' found to be obsolete, but ignoring.") + logging.debug("Dependency '%s' found to be obsolete, but ignoring.", dependency.name) return False - logging.debug(f"Dependency '{dependency.name}' does not seem to be used.") + logging.debug("Dependency '%s' does not seem to be used.", dependency.name) return True def _dependency_found_in_imported_modules(self, dependency: Dependency) -> bool: diff --git a/deptry/issues_finder/transitive.py b/deptry/issues_finder/transitive.py index 9b21942c..984caed6 100644 --- a/deptry/issues_finder/transitive.py +++ b/deptry/issues_finder/transitive.py @@ -32,7 +32,7 @@ def find(self) -> list[Violation]: for module_with_locations in self.imported_modules_with_locations: module = module_with_locations.module - logging.debug(f"Scanning module {module.name}...") + logging.debug("Scanning module %s...", module.name) if self._is_transitive(module): # `self._is_transitive` only returns `True` if the package is not None. @@ -53,8 +53,8 @@ def _is_transitive(self, module: Module) -> bool: return False if module.name in self.ignored_modules: - logging.debug(f"Dependency '{module.package}' found to be a transitive dependency, but ignoring.") + logging.debug("Dependency '%s' found to be a transitive dependency, but ignoring.", module.package) return False - logging.debug(f"Dependency '{module.package}' marked as a transitive dependency.") + logging.debug("Dependency '%s' marked as a transitive dependency.", module.package) return True diff --git a/deptry/reporters/json.py b/deptry/reporters/json.py index c9ab579a..06e7b6a7 100644 --- a/deptry/reporters/json.py +++ b/deptry/reporters/json.py @@ -2,6 +2,7 @@ import json from dataclasses import dataclass +from pathlib import Path from typing import TYPE_CHECKING from deptry.reporters.base import Reporter @@ -33,5 +34,5 @@ def report(self) -> None: }, ) - with open(self.json_output, "w", encoding="utf-8") as f: + with Path(self.json_output).open("w", encoding="utf-8") as f: json.dump(output, f, ensure_ascii=False, indent=4) diff --git a/deptry/utils.py b/deptry/utils.py index db3d3242..cf46fa73 100644 --- a/deptry/utils.py +++ b/deptry/utils.py @@ -1,11 +1,10 @@ from __future__ import annotations -import os import sys +from pathlib import Path from typing import TYPE_CHECKING if TYPE_CHECKING: - from pathlib import Path from typing import Any @@ -22,4 +21,4 @@ def load_pyproject_toml(config: Path) -> dict[str, Any]: with config.open("rb") as pyproject_file: return tomllib.load(pyproject_file) except FileNotFoundError: - raise PyprojectFileNotFoundError(os.getcwd()) from None + raise PyprojectFileNotFoundError(Path.cwd()) from None diff --git a/pyproject.toml b/pyproject.toml index 9de43e6b..1f9edf9e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -126,6 +126,8 @@ select = [ "YTT", # flake8-bandit "S", + # flake8-blind-except + "BLE", # flake8-bugbear "B", # flake8-builtins @@ -134,16 +136,24 @@ select = [ "C4", # flake8-debugger "T10", + # flake8-logging-format + "G", # flake8-print "T20", + # flake8-pytest-style + "PT", # flake8-simplify "SIM", # flake8-type-checking "TCH", + # flake8-use-pathlib + "PTH", # isort "I", # mccabe "C90", + # pep8-naming + "N", # pycodestyle "E", "W", # pyflakes diff --git a/tests/functional/cli/test_cli.py b/tests/functional/cli/test_cli.py index 3060aff8..e967b79d 100644 --- a/tests/functional/cli/test_cli.py +++ b/tests/functional/cli/test_cli.py @@ -1,6 +1,5 @@ from __future__ import annotations -import os import shlex import subprocess from pathlib import Path @@ -480,7 +479,7 @@ def test_cli_with_no_ansi(project_builder: ToolSpecificProjectBuilder) -> None: def test_cli_with_not_json_output(project_builder: ToolSpecificProjectBuilder) -> None: with run_within_dir(project_builder("example_project", "poetry install --no-interaction --no-root")): # Remove previously generated `report.json`. - os.remove("report.json") + Path("report.json").unlink() result = subprocess.run(shlex.split("poetry run deptry ."), capture_output=True, text=True) diff --git a/tests/unit/dependency_getter/test_pdm.py b/tests/unit/dependency_getter/test_pdm.py index 7e67f853..f25eebd4 100644 --- a/tests/unit/dependency_getter/test_pdm.py +++ b/tests/unit/dependency_getter/test_pdm.py @@ -21,7 +21,7 @@ def test_dependency_getter(tmp_path: Path) -> None: """ with run_within_dir(tmp_path): - with open("pyproject.toml", "w") as f: + with Path("pyproject.toml").open("w") as f: f.write(fake_pyproject_toml) getter = PDMDependencyGetter( @@ -80,7 +80,7 @@ def test_dev_dependency_getter(tmp_path: Path) -> None: """ with run_within_dir(tmp_path): - with open("pyproject.toml", "w") as f: + with Path("pyproject.toml").open("w") as f: f.write(fake_pyproject_toml) dev_dependencies = PDMDependencyGetter(Path("pyproject.toml")).get().dev_dependencies diff --git a/tests/unit/dependency_getter/test_pep_621.py b/tests/unit/dependency_getter/test_pep_621.py index 5c1411a7..55c77708 100644 --- a/tests/unit/dependency_getter/test_pep_621.py +++ b/tests/unit/dependency_getter/test_pep_621.py @@ -29,7 +29,7 @@ def test_dependency_getter(tmp_path: Path) -> None: """ with run_within_dir(tmp_path): - with open("pyproject.toml", "w") as f: + with Path("pyproject.toml").open("w") as f: f.write(fake_pyproject_toml) getter = PEP621DependencyGetter( diff --git a/tests/unit/dependency_getter/test_poetry.py b/tests/unit/dependency_getter/test_poetry.py index 6777be10..1da321d0 100644 --- a/tests/unit/dependency_getter/test_poetry.py +++ b/tests/unit/dependency_getter/test_poetry.py @@ -18,7 +18,7 @@ def test_dependency_getter(tmp_path: Path) -> None: qux = { version = ">=2.5.1,<4.0.0", optional = true }""" with run_within_dir(tmp_path): - with open("pyproject.toml", "w") as f: + with Path("pyproject.toml").open("w") as f: f.write(fake_pyproject_toml) getter = PoetryDependencyGetter( diff --git a/tests/unit/dependency_getter/test_requirements_txt.py b/tests/unit/dependency_getter/test_requirements_txt.py index 479f69ca..6946425d 100644 --- a/tests/unit/dependency_getter/test_requirements_txt.py +++ b/tests/unit/dependency_getter/test_requirements_txt.py @@ -28,7 +28,7 @@ def test_parse_requirements_txt(tmp_path: Path) -> None: fox-python """ with run_within_dir(tmp_path): - with open("requirements.txt", "w") as f: + with Path("requirements.txt").open("w") as f: f.write(fake_requirements_txt) getter = RequirementsTxtDependencyGetter( @@ -70,7 +70,7 @@ def test_parse_requirements_txt_urls(tmp_path: Path) -> None: git+https://github.com/abc123/bar-foo@xyz789#egg=bar-fooo""" with run_within_dir(tmp_path): - with open("requirements.txt", "w") as f: + with Path("requirements.txt").open("w") as f: f.write(fake_requirements_txt) dependencies_extract = RequirementsTxtDependencyGetter(Path("pyproject.toml")).get() @@ -88,7 +88,7 @@ def test_parse_requirements_txt_urls(tmp_path: Path) -> None: def test_single(tmp_path: Path) -> None: with run_within_dir(tmp_path): - with open("req.txt", "w") as f: + with Path("req.txt").open("w") as f: f.write("click==8.1.3 #123asd\ncolorama==0.4.5") dependencies_extract = RequirementsTxtDependencyGetter( @@ -105,9 +105,10 @@ def test_single(tmp_path: Path) -> None: def test_multiple(tmp_path: Path) -> None: with run_within_dir(tmp_path): - with open("foo.txt", "w") as f: + with Path("foo.txt").open("w") as f: f.write("click==8.1.3 #123asd") - with open("bar.txt", "w") as f: + + with Path("bar.txt").open("w") as f: f.write("bar") dependencies_extract = RequirementsTxtDependencyGetter( @@ -124,9 +125,10 @@ def test_multiple(tmp_path: Path) -> None: def test_dev_single(tmp_path: Path) -> None: with run_within_dir(tmp_path): - with open("requirements.txt", "w") as f: + with Path("requirements.txt").open("w") as f: f.write("") - with open("requirements-dev.txt", "w") as f: + + with Path("requirements-dev.txt").open("w") as f: f.write("click==8.1.3 #123asd\ncolorama==0.4.5") dependencies_extract = RequirementsTxtDependencyGetter(Path("pyproject.toml")).get() @@ -143,11 +145,13 @@ def test_dev_single(tmp_path: Path) -> None: def test_dev_multiple(tmp_path: Path) -> None: with run_within_dir(tmp_path): - with open("requirements.txt", "w") as f: + with Path("requirements.txt").open("w") as f: f.write("") - with open("requirements-dev.txt", "w") as f: + + with Path("requirements-dev.txt").open("w") as f: f.write("click==8.1.3 #123asd") - with open("dev-requirements.txt", "w") as f: + + with Path("dev-requirements.txt").open("w") as f: f.write("bar") dependencies_extract = RequirementsTxtDependencyGetter(Path("pyproject.toml")).get() @@ -162,11 +166,13 @@ def test_dev_multiple(tmp_path: Path) -> None: def test_dev_multiple_with_arguments(tmp_path: Path) -> None: with run_within_dir(tmp_path): - with open("requirements.txt", "w") as f: + with Path("requirements.txt").open("w") as f: f.write("") - with open("foo.txt", "w") as f: + + with Path("foo.txt").open("w") as f: f.write("click==8.1.3 #123asd") - with open("bar.txt", "w") as f: + + with Path("bar.txt").open("w") as f: f.write("bar") dependencies_extract = RequirementsTxtDependencyGetter( diff --git a/tests/unit/imports/test_extract.py b/tests/unit/imports/test_extract.py index 9ce2b71d..55f9cc28 100644 --- a/tests/unit/imports/test_extract.py +++ b/tests/unit/imports/test_extract.py @@ -18,32 +18,36 @@ def test_import_parser_py() -> None: - assert get_imported_modules_from_file(Path("tests/data/some_imports.py")) == { - "barfoo": [Location(Path("tests/data/some_imports.py"), 20, 0)], - "baz": [Location(Path("tests/data/some_imports.py"), 16, 4)], - "click": [Location(Path("tests/data/some_imports.py"), 24, 4)], - "foobar": [Location(Path("tests/data/some_imports.py"), 18, 4)], - "httpx": [Location(Path("tests/data/some_imports.py"), 14, 4)], - "module_in_class": [Location(Path("tests/data/some_imports.py"), 35, 8)], - "module_in_func": [Location(Path("tests/data/some_imports.py"), 30, 4)], - "not_click": [Location(Path("tests/data/some_imports.py"), 26, 4)], + some_imports_path = Path("tests/data/some_imports.py") + + assert get_imported_modules_from_file(some_imports_path) == { + "barfoo": [Location(some_imports_path, 20, 0)], + "baz": [Location(some_imports_path, 16, 4)], + "click": [Location(some_imports_path, 24, 4)], + "foobar": [Location(some_imports_path, 18, 4)], + "httpx": [Location(some_imports_path, 14, 4)], + "module_in_class": [Location(some_imports_path, 35, 8)], + "module_in_func": [Location(some_imports_path, 30, 4)], + "not_click": [Location(some_imports_path, 26, 4)], "numpy": [ - Location(Path("tests/data/some_imports.py"), 5, 0), - Location(Path("tests/data/some_imports.py"), 7, 0), + Location(some_imports_path, 5, 0), + Location(some_imports_path, 7, 0), ], - "os": [Location(Path("tests/data/some_imports.py"), 1, 0)], - "pandas": [Location(Path("tests/data/some_imports.py"), 6, 0)], - "pathlib": [Location(Path("tests/data/some_imports.py"), 2, 0)], - "randomizer": [Location(Path("tests/data/some_imports.py"), 21, 0)], - "typing": [Location(Path("tests/data/some_imports.py"), 3, 0)], + "os": [Location(some_imports_path, 1, 0)], + "pandas": [Location(some_imports_path, 6, 0)], + "pathlib": [Location(some_imports_path, 2, 0)], + "randomizer": [Location(some_imports_path, 21, 0)], + "typing": [Location(some_imports_path, 3, 0)], } def test_import_parser_ipynb() -> None: - assert get_imported_modules_from_file(Path("tests/data/example_project/src/notebook.ipynb")) == { - "click": [Location(Path("tests/data/example_project/src/notebook.ipynb"), 1, 0)], - "toml": [Location(Path("tests/data/example_project/src/notebook.ipynb"), 5, 0)], - "urllib3": [Location(Path("tests/data/example_project/src/notebook.ipynb"), 3, 0)], + notebook_path = Path("tests/data/example_project/src/notebook.ipynb") + + assert get_imported_modules_from_file(notebook_path) == { + "click": [Location(notebook_path, 1, 0)], + "toml": [Location(notebook_path, 5, 0)], + "urllib3": [Location(notebook_path, 3, 0)], } @@ -69,15 +73,13 @@ def test_import_parser_ipynb() -> None: ], ) def test_import_parser_file_encodings(file_content: str, encoding: str | None, tmp_path: Path) -> None: - random_file_name = f"file_{uuid.uuid4()}.py" + random_file = Path(f"file_{uuid.uuid4()}.py") with run_within_dir(tmp_path): - with open(random_file_name, "w", encoding=encoding) as f: + with random_file.open("w", encoding=encoding) as f: f.write(file_content) - assert get_imported_modules_from_file(Path(random_file_name)) == { - "foo": [Location(Path(f"{random_file_name}"), 2, 0)] - } + assert get_imported_modules_from_file(random_file) == {"foo": [Location(random_file, 2, 0)]} @pytest.mark.parametrize( @@ -102,10 +104,10 @@ def test_import_parser_file_encodings(file_content: str, encoding: str | None, t ], ) def test_import_parser_file_encodings_ipynb(code_cell_content: list[str], encoding: str | None, tmp_path: Path) -> None: - random_file_name = f"file_{uuid.uuid4()}.ipynb" + random_file = Path(f"file_{uuid.uuid4()}.ipynb") with run_within_dir(tmp_path): - with open(random_file_name, "w", encoding=encoding) as f: + with random_file.open("w", encoding=encoding) as f: file_content = { "cells": [ { @@ -117,20 +119,20 @@ def test_import_parser_file_encodings_ipynb(code_cell_content: list[str], encodi } f.write(json.dumps(file_content)) - assert get_imported_modules_from_file(Path(random_file_name)) == { - "foo": [Location(Path(f"{random_file_name}"), 1, 0)] - } + assert get_imported_modules_from_file(random_file) == {"foo": [Location(random_file, 1, 0)]} def test_import_parser_file_encodings_warning(tmp_path: Path, caplog: LogCaptureFixture) -> None: + file_path = Path("file1.py") + with run_within_dir(tmp_path): - with open("file1.py", "w", encoding="utf-8") as f: + with file_path.open("w", encoding="utf-8") as f: f.write("print('this is a mock unparseable file')") with caplog.at_level(logging.WARNING), mock.patch( "deptry.imports.extractors.python_import_extractor.ast.parse", side_effect=UnicodeDecodeError("fakecodec", b"\x00\x00", 1, 2, "Fake reason!"), ): - assert get_imported_modules_from_file(Path("file1.py")) == {} + assert get_imported_modules_from_file(file_path) == {} assert "Warning: File file1.py could not be decoded. Skipping..." in caplog.text diff --git a/tests/unit/reporters/test_json_reporter.py b/tests/unit/reporters/test_json_reporter.py index e2c073fd..745abaf0 100644 --- a/tests/unit/reporters/test_json_reporter.py +++ b/tests/unit/reporters/test_json_reporter.py @@ -30,7 +30,7 @@ def test_simple(tmp_path: Path) -> None: "output.json", ).report() - with open("output.json") as f: + with Path("output.json").open() as f: data = json.load(f) assert data == [ diff --git a/tests/unit/test_config.py b/tests/unit/test_config.py index df3d1400..431569fb 100644 --- a/tests/unit/test_config.py +++ b/tests/unit/test_config.py @@ -51,12 +51,15 @@ def test_read_configuration_from_pyproject_toml_exists(tmp_path: Path) -> None: """ with run_within_dir(tmp_path): - with open("pyproject.toml", "w") as f: + pyproject_toml_path = Path("pyproject.toml") + + with pyproject_toml_path.open("w") as f: f.write(pyproject_toml_content) - assert read_configuration_from_pyproject_toml( - click_context, click.UNPROCESSED(None), Path("pyproject.toml") - ) == Path("pyproject.toml") + assert ( + read_configuration_from_pyproject_toml(click_context, click.UNPROCESSED(None), pyproject_toml_path) + == pyproject_toml_path + ) assert click_context.default_map == { "exclude": ["foo", "bar"], @@ -76,10 +79,15 @@ def test_read_configuration_from_pyproject_toml_exists(tmp_path: Path) -> None: def test_read_configuration_from_pyproject_toml_file_not_found(caplog: LogCaptureFixture) -> None: + pyproject_toml_path = Path("a_non_existent_pyproject.toml") + with caplog.at_level(logging.DEBUG): - assert read_configuration_from_pyproject_toml( - click.Context(click.Command("")), click.UNPROCESSED(None), Path("a_non_existent_pyproject.toml") - ) == Path("a_non_existent_pyproject.toml") + assert ( + read_configuration_from_pyproject_toml( + click.Context(click.Command("")), click.UNPROCESSED(None), pyproject_toml_path + ) + == pyproject_toml_path + ) assert "No pyproject.toml file to read configuration from." in caplog.text @@ -93,12 +101,14 @@ def test_read_configuration_from_pyproject_toml_file_without_deptry_section( """ with run_within_dir(tmp_path): - with open("pyproject.toml", "w") as f: + pyproject_toml_path = Path("pyproject.toml") + + with pyproject_toml_path.open("w") as f: f.write(pyproject_toml_content) with caplog.at_level(logging.DEBUG): assert read_configuration_from_pyproject_toml( - click.Context(click.Command("")), click.UNPROCESSED(None), Path("pyproject.toml") + click.Context(click.Command("")), click.UNPROCESSED(None), pyproject_toml_path ) == Path("pyproject.toml") assert "No configuration for deptry was found in pyproject.toml." in caplog.text diff --git a/tests/unit/test_dependency_specification_detector.py b/tests/unit/test_dependency_specification_detector.py index b1fd4a40..ae129c92 100644 --- a/tests/unit/test_dependency_specification_detector.py +++ b/tests/unit/test_dependency_specification_detector.py @@ -1,6 +1,5 @@ from __future__ import annotations -import os import re from pathlib import Path @@ -13,16 +12,18 @@ def test_poetry(tmp_path: Path) -> None: with run_within_dir(tmp_path): - with open("pyproject.toml", "w") as f: + pyproject_toml_path = Path("pyproject.toml") + + with pyproject_toml_path.open("w") as f: f.write('[tool.poetry.dependencies]\nfake = "10"') - spec = DependencySpecificationDetector(Path("pyproject.toml")).detect() + spec = DependencySpecificationDetector(pyproject_toml_path).detect() assert spec == DependencyManagementFormat.POETRY def test_requirements_txt(tmp_path: Path) -> None: with run_within_dir(tmp_path): - with open("requirements.txt", "w") as f: + with Path("requirements.txt").open("w") as f: f.write('foo >= "1.0"') spec = DependencySpecificationDetector(Path("pyproject.toml")).detect() @@ -31,31 +32,37 @@ def test_requirements_txt(tmp_path: Path) -> None: def test_pdm_with_dev_dependencies(tmp_path: Path) -> None: with run_within_dir(tmp_path): - with open("pyproject.toml", "w") as f: + pyproject_toml_path = Path("pyproject.toml") + + with pyproject_toml_path.open("w") as f: f.write( '[project]\ndependencies=["foo"]\n[tool.pdm]\nversion = {source =' ' "scm"}\n[tool.pdm.dev-dependencies]\ngroup=["bar"]' ) - spec = DependencySpecificationDetector(Path("pyproject.toml")).detect() + spec = DependencySpecificationDetector(pyproject_toml_path).detect() assert spec == DependencyManagementFormat.PDM def test_pdm_without_dev_dependencies(tmp_path: Path) -> None: with run_within_dir(tmp_path): - with open("pyproject.toml", "w") as f: + pyproject_toml_path = Path("pyproject.toml") + + with pyproject_toml_path.open("w") as f: f.write('[project]\ndependencies=["foo"]\n[tool.pdm]\nversion = {source = "scm"}') - spec = DependencySpecificationDetector(Path("pyproject.toml")).detect() + spec = DependencySpecificationDetector(pyproject_toml_path).detect() assert spec == DependencyManagementFormat.PEP_621 def test_pep_621(tmp_path: Path) -> None: with run_within_dir(tmp_path): - with open("pyproject.toml", "w") as f: + pyproject_toml_path = Path("pyproject.toml") + + with pyproject_toml_path.open("w") as f: f.write('[project]\ndependencies=["foo"]') - spec = DependencySpecificationDetector(Path("pyproject.toml")).detect() + spec = DependencySpecificationDetector(pyproject_toml_path).detect() assert spec == DependencyManagementFormat.PEP_621 @@ -65,19 +72,21 @@ def test_both(tmp_path: Path) -> None: """ with run_within_dir(tmp_path): - with open("pyproject.toml", "w") as f: + pyproject_toml_path = Path("pyproject.toml") + + with pyproject_toml_path.open("w") as f: f.write('[tool.poetry.dependencies]\nfake = "10"') - with open("requirements.txt", "w") as f: + with Path("requirements.txt").open("w") as f: f.write('foo >= "1.0"') - spec = DependencySpecificationDetector(Path("pyproject.toml")).detect() + spec = DependencySpecificationDetector(pyproject_toml_path).detect() assert spec == DependencyManagementFormat.POETRY def test_requirements_txt_with_argument(tmp_path: Path) -> None: with run_within_dir(tmp_path): - with open("req.txt", "w") as f: + with Path("req.txt").open("w") as f: f.write('foo >= "1.0"') spec = DependencySpecificationDetector(Path("pyproject.toml"), requirements_txt=("req.txt",)).detect() @@ -86,8 +95,9 @@ def test_requirements_txt_with_argument(tmp_path: Path) -> None: def test_requirements_txt_with_argument_not_root_directory(tmp_path: Path) -> None: with run_within_dir(tmp_path): - os.mkdir("req") - with open("req/req.txt", "w") as f: + Path("req").mkdir() + + with Path("req/req.txt").open("w") as f: f.write('foo >= "1.0"') spec = DependencySpecificationDetector(Path("pyproject.toml"), requirements_txt=("req/req.txt",)).detect() diff --git a/tests/unit/test_python_file_finder.py b/tests/unit/test_python_file_finder.py index 57adbc2f..2c96ba64 100644 --- a/tests/unit/test_python_file_finder.py +++ b/tests/unit/test_python_file_finder.py @@ -152,7 +152,7 @@ def test__generate_gitignore_pathspec_with_non_existing_gitignore(tmp_path: Path def test__generate_gitignore_pathspec_with_existing_gitignore(tmp_path: Path) -> None: with run_within_dir(tmp_path): - with open(Path(".gitignore"), "w") as gitignore: + with Path(".gitignore").open("w") as gitignore: gitignore.write("foo.py\nbar/") gitignore_pathspec = PythonFileFinder( diff --git a/tests/utils.py b/tests/utils.py index 353d3913..48c7b7fe 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -3,13 +3,11 @@ import json import os from contextlib import contextmanager -from typing import TYPE_CHECKING, Any, Generator +from pathlib import Path +from typing import Any, Generator from deptry.reporters.text import COLORS -if TYPE_CHECKING: - from pathlib import Path - @contextmanager def run_within_dir(path: Path) -> Generator[None, None, None]: @@ -24,7 +22,7 @@ def run_within_dir(path: Path) -> Generator[None, None, None]: ``` """ - oldpwd = os.getcwd() + oldpwd = Path.cwd() os.chdir(path) try: yield @@ -33,7 +31,7 @@ def run_within_dir(path: Path) -> Generator[None, None, None]: def get_issues_report(path: str = "report.json") -> list[dict[str, Any]]: - with open(path) as file: + with Path(path).open() as file: report: list[dict[str, Any]] = json.load(file) return report @@ -47,8 +45,7 @@ def create_files(paths: list[Path]) -> None: """ for path in paths: path.parent.mkdir(parents=True, exist_ok=True) - with open(path, "w"): - pass + path.touch() def stylize(text: str, **kwargs: Any) -> str: