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

pyclean.traversal.should_ignore()   B

Complexity

Conditions 8

Size

Total Lines 24
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 15
dl 0
loc 24
rs 7.3333
c 0
b 0
f 0
cc 8
nop 2
1
# SPDX-FileCopyrightText: 2020 Peter Bittner <[email protected]>
2
#
3
# SPDX-License-Identifier: GPL-3.0-or-later
4
5
"""Directory traversal and ignore pattern matching."""
6
7
import logging
8
import os
9
from pathlib import Path
10
11
from .runner import Runner
12
13
log = logging.getLogger(__name__)
14
15
16
def normalize(path_pattern: str) -> str:
17
    """
18
    Normalize path separators in a pattern for cross-platform support.
19
20
    On Windows, both forward slash and backslash are valid path separators.
21
    On Unix/Posix, only forward slash is valid (backslash can be part of filename).
22
    """
23
    return path_pattern.replace(os.sep, os.altsep or os.sep)
24
25
26
def should_ignore(path: Path, ignore_patterns: list[str]) -> bool:
27
    """
28
    Check if a path should be ignored based on ignore patterns.
29
30
    Patterns can be:
31
    - Simple names like 'bar': matches any directory with that name
32
    - Paths like 'foo/bar': matches 'bar' directory inside 'foo' directory
33
      and also ignores everything inside that directory
34
    """
35
    if not ignore_patterns:
36
        return False
37
38
    for pattern in ignore_patterns:
39
        pattern_parts = Path(normalize(pattern)).parts
40
        if len(pattern_parts) > 1:
41
            if len(path.parts) < len(pattern_parts):
42
                continue
43
            for i in range(len(path.parts) - len(pattern_parts) + 1):
44
                path_slice = path.parts[i : i + len(pattern_parts)]
45
                if path_slice == pattern_parts:
46
                    return True
47
        elif path.name == pattern:
48
            return True
49
    return False
50
51
52
def descend_and_clean(directory, file_types, dir_names):
53
    """
54
    Walk and descend a directory tree, cleaning up files of a certain type
55
    along the way. Only delete directories if they are empty, in the end.
56
    """
57
    for child in sorted(directory.iterdir()):
58
        if child.is_file():
59
            if child.suffix in file_types:
60
                Runner.unlink(child)
61
        elif child.is_dir():
62
            if should_ignore(child, Runner.ignore):
63
                log.debug('Skipping %s', child)
64
            else:
65
                descend_and_clean(child, file_types, dir_names)
66
67
            if child.name in dir_names:
68
                Runner.rmdir(child)
69
        else:
70
            log.debug('Ignoring %s (neither a file nor a folder)', child)
71