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

from .runner import Runner
from .traversal import should_ignore

log = logging.getLogger(__name__)


def _path_is_ignored(path: Path, ignore_patterns: list[str]) -> bool:
"""Check if 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 confirm(message):
"""An interactive confirmation prompt."""
try:
Expand All @@ -38,6 +46,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
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 (
_path_is_ignored,
confirm,
delete_filesystem_objects,
remove_freeform_targets,
)


@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