diff --git a/pyclean/runner.py b/pyclean/runner.py index 6032579..4cd167e 100644 --- a/pyclean/runner.py +++ b/pyclean/runner.py @@ -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 @@ -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: @@ -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: @@ -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 diff --git a/pyclean/traversal.py b/pyclean/traversal.py index 2a984f0..ddf967d 100644 --- a/pyclean/traversal.py +++ b/pyclean/traversal.py @@ -4,6 +4,8 @@ """Directory traversal and ignore pattern matching.""" +from __future__ import annotations + import logging import os from pathlib import Path @@ -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. diff --git a/tests/test_traversal.py b/tests/test_traversal.py index 2fb6559..117c144 100644 --- a/tests/test_traversal.py +++ b/tests/test_traversal.py @@ -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 @@ -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