Passed
Push — main ( 9b73b1...666399 )
by Peter
01:12
created

pyclean.debris.recursive_delete_debris()   A

Complexity

Conditions 5

Size

Total Lines 23
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 13
dl 0
loc 23
rs 9.2833
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
    'coverage': [
23
        '.coverage',
24
        'coverage.json',
25
        'coverage.lcov',
26
        'coverage.xml',
27
        'htmlcov/**/*',
28
        'htmlcov/',
29
    ],
30
    'jupyter': [
31
        '.ipynb_checkpoints/**/*',
32
        '.ipynb_checkpoints/',
33
    ],
34
    'mypy': [
35
        '.mypy_cache/**/*',
36
        '.mypy_cache/',
37
    ],
38
    'package': [
39
        'build/bdist.*/**/*',
40
        'build/bdist.*/',
41
        'build/lib/**/*',
42
        'build/lib/',
43
        'build/',
44
        'dist/**/*',
45
        'dist/',
46
        'sdist/**/*',
47
        'sdist/',
48
        '*.egg-info/**/*',
49
        '*.egg-info/',
50
    ],
51
    'pyright': [
52
        '.pyright-app-cache-*/**/*',
53
        '.pyright-app-cache-*/',
54
        '.pyright-stubs-*/**/*',
55
        '.pyright-stubs-*/',
56
        '.pyright/',
57
    ],
58
    'pytest': [
59
        '.pytest_cache/**/*',
60
        '.pytest_cache/',
61
        'pytestdebug.log',
62
    ],
63
    'ruff': [
64
        '.ruff_cache/**/*',
65
        '.ruff_cache/',
66
    ],
67
    'tox': [
68
        '.tox/**/*',
69
        '.tox/',
70
    ],
71
}
72
73
74
def remove_debris_for(topic, directory):
75
    """
76
    Clean up debris for a specific topic.
77
    """
78
    log.debug('Scanning for debris of %s ...', topic.title())
79
80
    patterns = DEBRIS_TOPICS[topic]
81
    recursive_delete_debris(directory, patterns)
82
83
84
def recursive_delete_debris(directory, patterns):
85
    """
86
    Recursively delete debris matching any of the given patterns.
87
88
    This function walks the directory tree once and applies all patterns
89
    at each level, avoiding redundant directory scans.
90
    """
91
    for pattern in patterns:
92
        delete_filesystem_objects(directory, pattern)
93
94
    try:
95
        subdirs = (
96
            Path(entry.path) for entry in os.scandir(directory) if entry.is_dir()
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable entry does not seem to be defined.
Loading history...
97
        )
98
    except (OSError, PermissionError) as err:
99
        log.warning('Cannot access directory %s: %s', directory, err)
100
        return
101
102
    for subdir in subdirs:
103
        if should_ignore(subdir, Runner.ignore):
104
            log.debug('Skipping %s', subdir)
105
        else:
106
            recursive_delete_debris(subdir, patterns)
107
108
109
def detect_debris_in_directory(directory):
110
    """
111
    Scan a directory for debris artifacts and return a list of detected topics.
112
    """
113
    detected_topics = []
114
115
    for topic, patterns in DEBRIS_TOPICS.items():
116
        for pattern in patterns:
117
            if '**' in pattern:
118
                continue
119
            matches = list(directory.glob(pattern))
120
            if matches:
121
                detected_topics.append(topic)
122
                break
123
124
    return detected_topics
125
126
127
def suggest_debris_option(args):
128
    """
129
    Suggest using the --debris option when it wasn't used.
130
    Optionally provide targeted suggestions based on detected artifacts.
131
    """
132
    all_detected = set()
133
    for dir_name in args.directory:
134
        dir_path = Path(dir_name)
135
        if dir_path.exists():
136
            detected = detect_debris_in_directory(dir_path)
137
            all_detected.update(detected)
138
139
    if all_detected:
140
        topics_str = ' '.join(sorted(all_detected))
141
        log.info(
142
            'Hint: Use --debris to also clean up build artifacts. Detected: %s',
143
            topics_str,
144
        )
145
    else:
146
        log.info(
147
            'Hint: Use --debris to also clean up build artifacts '
148
            'from common Python development tools.',
149
        )
150