pyclean.debris.recursive_delete_debris()   A
last analyzed

Complexity

Conditions 5

Size

Total Lines 21
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 12
dl 0
loc 21
rs 9.3333
c 0
b 0
f 0
cc 5
nop 2
1
# SPDX-FileCopyrightText: 2020 Peter Bittner <[email protected]>
2
#
3
# SPDX-License-Identifier: GPL-3.0-or-later
4
5
"""Tool-specific artifact cleanup and debris detection (to suggest option usage)."""
6
7
import logging
8
import os
9
from pathlib import Path
10
11
from .erase import delete_filesystem_objects
12
from .runner import Runner
13
from .traversal import should_ignore
14
15
log = logging.getLogger(__name__)
16
17
DEBRIS_TOPICS = {
18
    'cache': [
19
        '.cache/**/*',
20
        '.cache/',
21
    ],
22
    'complexipy': [
23
        '.complexipy_cache/**/*',
24
        '.complexipy_cache/',
25
    ],
26
    'coverage': [
27
        '.coverage',
28
        'coverage.json',
29
        'coverage.lcov',
30
        'coverage.xml',
31
        'htmlcov/**/*',
32
        'htmlcov/',
33
    ],
34
    'jupyter': [
35
        '.ipynb_checkpoints/**/*',
36
        '.ipynb_checkpoints/',
37
    ],
38
    'mypy': [
39
        '.mypy_cache/**/*',
40
        '.mypy_cache/',
41
    ],
42
    'package': [
43
        'build/bdist.*/**/*',
44
        'build/bdist.*/',
45
        'build/lib/**/*',
46
        'build/lib/',
47
        'build/',
48
        'dist/**/*',
49
        'dist/',
50
        'sdist/**/*',
51
        'sdist/',
52
        '*.egg-info/**/*',
53
        '*.egg-info/',
54
    ],
55
    'pyright': [
56
        '.pyright-app-cache-*/**/*',
57
        '.pyright-app-cache-*/',
58
        '.pyright-stubs-*/**/*',
59
        '.pyright-stubs-*/',
60
        '.pyright/',
61
    ],
62
    'pytest': [
63
        '.pytest_cache/**/*',
64
        '.pytest_cache/',
65
        'pytestdebug.log',
66
    ],
67
    'ruff': [
68
        '.ruff_cache/**/*',
69
        '.ruff_cache/',
70
    ],
71
    'tox': [
72
        '.tox/**/*',
73
        '.tox/',
74
    ],
75
}
76
77
78
def remove_debris_for(topic, directory):
79
    """
80
    Clean up debris for a specific topic.
81
    """
82
    log.debug('Scanning for debris of %s ...', topic.title())
83
84
    patterns = DEBRIS_TOPICS[topic]
85
    recursive_delete_debris(directory, patterns)
86
87
88
def recursive_delete_debris(directory: Path, patterns: list[str]):
89
    """
90
    Recursively delete debris matching any of the given patterns.
91
92
    This function walks the directory tree once and applies all patterns
93
    at each level, avoiding redundant directory scans.
94
    """
95
    for pattern in patterns:
96
        delete_filesystem_objects(directory, pattern)
97
98
    try:
99
        subdirs = [entry for entry in os.scandir(directory) if entry.is_dir()]
100
    except (OSError, PermissionError) as err:
101
        log.warning('Cannot access directory %s: %s', directory, err)
102
        return
103
104
    for subdir in subdirs:
105
        if should_ignore(subdir.path, Runner.ignore):
106
            log.debug('Skipping %s', subdir.name)
107
        else:
108
            recursive_delete_debris(Path(subdir.path), patterns)
109
110
111
def detect_debris_in_directory(directory):
112
    """
113
    Scan a directory for debris artifacts and return a list of detected topics.
114
    """
115
    detected_topics = []
116
117
    for topic, patterns in DEBRIS_TOPICS.items():
118
        for pattern in patterns:
119
            if '**' in pattern:
120
                continue
121
            matches = list(directory.glob(pattern))
122
            if matches:
123
                detected_topics.append(topic)
124
                break
125
126
    return detected_topics
127
128
129
def suggest_debris_option(args):
130
    """
131
    Suggest using the --debris option when it wasn't used.
132
    Optionally provide targeted suggestions based on detected artifacts.
133
    """
134
    all_detected = set()
135
    for dir_name in args.directory:
136
        dir_path = Path(dir_name)
137
        if dir_path.exists():
138
            detected = detect_debris_in_directory(dir_path)
139
            all_detected.update(detected)
140
141
    if all_detected:
142
        topics_str = ' '.join(sorted(all_detected))
143
        log.info(
144
            'Hint: Use --debris to also clean up build artifacts. Detected: %s',
145
            topics_str,
146
        )
147
    else:
148
        log.info(
149
            'Hint: Use --debris to also clean up build artifacts '
150
            'from common Python development tools.',
151
        )
152