pyclean.debris   A
last analyzed

Complexity

Total Complexity 15

Size/Duplication

Total Lines 146
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 15
eloc 95
dl 0
loc 146
rs 10
c 0
b 0
f 0

4 Functions

Rating   Name   Duplication   Size   Complexity  
A remove_debris_for() 0 8 1
A suggest_debris_option() 0 21 4
A detect_debris_in_directory() 0 16 5
A recursive_delete_debris() 0 21 5
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: Path, patterns: list[str]):
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 = [entry for entry in os.scandir(directory) if entry.is_dir()]
96
    except (OSError, PermissionError) as err:
97
        log.warning('Cannot access directory %s: %s', directory, err)
98
        return
99
100
    for subdir in subdirs:
101
        if should_ignore(subdir.path, Runner.ignore):
102
            log.debug('Skipping %s', subdir.name)
103
        else:
104
            recursive_delete_debris(Path(subdir.path), patterns)
105
106
107
def detect_debris_in_directory(directory):
108
    """
109
    Scan a directory for debris artifacts and return a list of detected topics.
110
    """
111
    detected_topics = []
112
113
    for topic, patterns in DEBRIS_TOPICS.items():
114
        for pattern in patterns:
115
            if '**' in pattern:
116
                continue
117
            matches = list(directory.glob(pattern))
118
            if matches:
119
                detected_topics.append(topic)
120
                break
121
122
    return detected_topics
123
124
125
def suggest_debris_option(args):
126
    """
127
    Suggest using the --debris option when it wasn't used.
128
    Optionally provide targeted suggestions based on detected artifacts.
129
    """
130
    all_detected = set()
131
    for dir_name in args.directory:
132
        dir_path = Path(dir_name)
133
        if dir_path.exists():
134
            detected = detect_debris_in_directory(dir_path)
135
            all_detected.update(detected)
136
137
    if all_detected:
138
        topics_str = ' '.join(sorted(all_detected))
139
        log.info(
140
            'Hint: Use --debris to also clean up build artifacts. Detected: %s',
141
            topics_str,
142
        )
143
    else:
144
        log.info(
145
            'Hint: Use --debris to also clean up build artifacts '
146
            'from common Python development tools.',
147
        )
148