Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
env:
- lint
- format
- mypy
- types
- package
- license
steps:
Expand Down
37 changes: 24 additions & 13 deletions pyclean/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,36 @@

"""Cleanup runner with dry-run and file operations functionality."""

from __future__ import annotations

import logging
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from argparse import Namespace
from pathlib import Path

log = logging.getLogger(__name__)


def noop(_: Path) -> None:
"""No-op function for uninitialized runner."""


class CleanupRunner:
"""Execution engine with object counting, logging and optional dry-run."""

def __init__(self):
"""Cleanup runner with optional dry-run behavior."""
self.unlink = None
self.rmdir = None
self.ignore = None
self.unlink_count = None
self.unlink_failed = None
self.rmdir_count = None
self.rmdir_failed = None

def configure(self, args):
self.unlink = noop
self.rmdir = noop
self.ignore: list[str] = []
self.unlink_count = 0
self.unlink_failed = 0
self.rmdir_count = 0
self.rmdir_failed = 0

def configure(self, args: Namespace) -> None:
"""Set up runner according to command line options."""
self.unlink = print_filename if args.dry_run else remove_file
self.rmdir = print_dirname if args.dry_run else remove_directory
Expand All @@ -36,7 +47,7 @@ def configure(self, args):
Runner = CleanupRunner()


def remove_file(fileobj):
def remove_file(fileobj: Path) -> None:
"""Attempt to delete a file object for real."""
log.debug('Deleting file: %s', fileobj)
try:
Expand All @@ -47,7 +58,7 @@ def remove_file(fileobj):
Runner.unlink_failed += 1


def remove_directory(dirobj):
def remove_directory(dirobj: Path) -> None:
"""Attempt to remove a directory object for real."""
log.debug('Removing directory: %s', dirobj)
try:
Expand All @@ -58,13 +69,13 @@ def remove_directory(dirobj):
Runner.rmdir_failed += 1


def print_filename(fileobj):
def print_filename(fileobj: Path) -> None:
"""Only display the file name, used with --dry-run."""
log.debug('Would delete file: %s', fileobj)
Runner.unlink_count += 1


def print_dirname(dirobj):
def print_dirname(dirobj: Path) -> None:
"""Only display the directory name, used with --dry-run."""
log.debug('Would delete directory: %s', dirobj)
Runner.rmdir_count += 1
4 changes: 3 additions & 1 deletion pyclean/traversal.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

"""Directory traversal and ignore pattern matching."""

from __future__ import annotations

import logging
import os
from pathlib import Path
Expand All @@ -23,7 +25,7 @@ def normalize(path_pattern: str) -> str:
return path_pattern.replace(os.sep, os.altsep or os.sep)


def should_ignore(pathname: str, ignore_patterns: list[str]) -> bool:
def should_ignore(pathname: str, ignore_patterns: list[str] | None) -> bool:
"""
Check if a path should be ignored based on ignore patterns.

Expand Down
13 changes: 0 additions & 13 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,19 +58,6 @@ exclude_also = ["if __name__ == .__main__.:"]
show_missing = true
skip_covered = true

[tool.mypy]
exclude = [
"tests/",
"tests\\.py$",
"test_.*\\.py$",
".*_tests\\.py$",
]
ignore_missing_imports = true
pretty = true
warn_redundant_casts = true
warn_unreachable = true
warn_unused_ignores = true

[tool.pytest.ini_options]
addopts = "--color=yes --doctest-modules --junitxml=tests/junit-report.xml --verbose"

Expand Down
6 changes: 2 additions & 4 deletions tests/test_traversal.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,7 @@ def test_should_ignore(path_str, patterns, expected):
"""
Does should_ignore correctly match path patterns?
"""
path = Path(path_str)
result = should_ignore(path, patterns)
result = should_ignore(path_str, patterns)
assert result == expected


Expand All @@ -112,8 +111,7 @@ def test_should_ignore_windows_paths(path_str, patterns, expected):
Does should_ignore correctly handle Windows-style backslash patterns?
This test only runs on Windows where backslash is a path separator.
"""
path = Path(path_str)
result = should_ignore(path, patterns)
result = should_ignore(path_str, patterns)
assert result == expected


Expand Down
17 changes: 10 additions & 7 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
envlist =
lint
format
types
license
mypy
py3{9,10,11,12,13,14}
pypy3{9,10,11}
package
Expand Down Expand Up @@ -59,12 +59,6 @@ skip_install = true
deps = ruff
commands = ruff check {posargs:.}

[testenv:mypy]
description = Perform static type checking
skip_install = true
deps = mypy
commands = mypy {posargs:.}

[testenv:package]
description = Build package and check metadata (or upload package)
skip_install = true
Expand All @@ -78,3 +72,12 @@ passenv =
TWINE_USERNAME
TWINE_PASSWORD
TWINE_REPOSITORY_URL

[testenv:types]
description = Perform static type checking
skip_install = true
deps =
cli-test-helpers
pytest
ty
commands = ty check {posargs}