Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
3 changes: 3 additions & 0 deletions pyclean/erase.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from pathlib import Path

from .runner import Runner
from .traversal import path_is_ignored

log = logging.getLogger(__name__)

Expand Down Expand Up @@ -38,6 +39,8 @@ def delete_filesystem_objects(
are empty (for both files & directories) when we attempt to remove them.
"""
all_names = sorted(directory.glob(path_glob), reverse=True)
if Runner.ignore:
all_names = [n for n in all_names if not path_is_ignored(n, Runner.ignore)]
Comment thread
bittner marked this conversation as resolved.
Outdated
dirs = (name for name in all_names if name.is_dir() and not name.is_symlink())
files = (name for name in all_names if not name.is_dir() or name.is_symlink())

Expand Down
7 changes: 7 additions & 0 deletions pyclean/traversal.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ def should_ignore(pathname: str, ignore_patterns: list[str] | None) -> bool:
return False


def path_is_ignored(path: Path, ignore_patterns: list[str]) -> bool:
"""Check if a path or any of its ancestors matches an ignore pattern."""
if not isinstance(path, Path):
path = Path(str(path))
return any(should_ignore(str(p), ignore_patterns) for p in [path, *path.parents])


def descend_and_clean(directory, file_types, dir_names):
"""
Walk and descend a directory tree, cleaning up files of a certain type
Expand Down
76 changes: 75 additions & 1 deletion tests/test_erase.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@

import pyclean.cli
import pyclean.main
from pyclean.erase import confirm, delete_filesystem_objects, remove_freeform_targets
from pyclean.erase import (
confirm,
delete_filesystem_objects,
remove_freeform_targets,
)
from pyclean.traversal import path_is_ignored


@patch('pyclean.main.remove_freeform_targets')
Expand Down Expand Up @@ -213,3 +218,72 @@ def test_confirm_no(mock_input):
Does confirm return False for 'no' answer?
"""
assert confirm('Test message') is False


def test_path_is_ignored_for_dir_itself():
"""
Does path_is_ignored return True for an ignored directory itself?
"""
assert path_is_ignored(Path('allure-results'), ['allure-results'])


def test_path_is_ignored_for_file_in_ignored_dir():
"""
Does path_is_ignored return True for a file inside an ignored directory?
"""
assert path_is_ignored(Path('allure-results/foo.txt'), ['allure-results'])


def test_path_is_ignored_for_nested_path_in_ignored_dir():
"""
Does path_is_ignored return True for a deeply nested path inside an ignored directory?
"""
assert path_is_ignored(Path('allure-results/sub/deep/file.txt'), ['allure-results'])


def test_path_is_not_ignored_for_unrelated_path():
"""
Does path_is_ignored return False for a path not matching any ignore pattern?
"""
assert not path_is_ignored(Path('keep.txt'), ['allure-results'])
assert not path_is_ignored(Path('other/foo.txt'), ['allure-results'])


def test_delete_filesystem_objects_skips_ignored_dirs(tmp_path):
"""
Does delete_filesystem_objects skip files and directories in ignored paths?
"""
ignored_dir = tmp_path / 'allure-results'
ignored_dir.mkdir()
ignored_file = ignored_dir / 'foo.txt'
ignored_file.write_text('test')

args = Namespace(dry_run=False, ignore=['allure-results'])
pyclean.main.Runner.configure(args)

delete_filesystem_objects(tmp_path, 'allure-results/**/*', prompt=False)

assert ignored_file.exists(), 'File in ignored directory should not be deleted'


def test_delete_filesystem_objects_erases_non_ignored(tmp_path):
"""
Does delete_filesystem_objects still erase non-ignored paths when ignore is set?
"""
ignored_dir = tmp_path / 'allure-results'
ignored_dir.mkdir()
ignored_file = ignored_dir / 'foo.txt'
ignored_file.write_text('test')
non_ignored_file1 = tmp_path / 'keep.txt'
non_ignored_file1.write_text('keep')
non_ignored_file2 = tmp_path / 'erase.txt'
non_ignored_file2.write_text('erase')

args = Namespace(dry_run=False, ignore=['allure-results'])
pyclean.main.Runner.configure(args)

delete_filesystem_objects(tmp_path, '*.txt', prompt=False)

assert ignored_file.exists(), 'File in ignored directory should not be deleted'
assert not non_ignored_file1.exists(), 'Non-ignored file should be deleted'
assert not non_ignored_file2.exists(), 'Non-ignored file should be deleted'
Loading