Test Failed
Pull Request — master (#19)
by Peter
01:20
created

pyclean.modern.descend_and_clean_bytecode()   B

Complexity

Conditions 6

Size

Total Lines 16
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 10
nop 1
dl 0
loc 16
rs 8.6666
c 0
b 0
f 0
1
"""
2
Modern, cross-platform, pure-Python pyclean implementation.
3
"""
4
import logging
5
6
try:
7
    from pathlib import Path
8
except ImportError:  # Python 2.7, PyPy2
9
    from warnings import warn
10
    warn("Python 3 required for modern implementation. Python 2 is obsolete.")
11
12
log = logging.getLogger(__name__)
13
14
15
class Runner:  # pylint: disable=too-few-public-methods
16
    """Module-level configuration and value store."""
17
    rmdir_count = 0
18
    rmdir_failed = 0
19
    unlink_count = 0
20
    unlink_failed = 0
21
22
23
def remove_file(fileobj):
24
    """Attempt to delete a file object for real."""
25
    log.debug("Deleting file: %s", fileobj)
26
    try:
27
        fileobj.unlink()
28
        Runner.unlink_count += 1
29
    except OSError as err:
30
        log.debug("File not deleted. %s", err)
31
        Runner.unlink_failed += 1
32
33
34
def remove_directory(dirobj):
35
    """Attempt to remove a directory object for real."""
36
    log.debug("Removing directory: %s", dirobj)
37
    try:
38
        dirobj.rmdir()
39
        Runner.rmdir_count += 1
40
    except OSError as err:
41
        log.debug("Directory not removed. %s", err)
42
        Runner.rmdir_failed += 1
43
44
45
def print_filename(fileobj):
46
    """Only display the file name, used with --dry-run."""
47
    log.debug("Would delete file: %s", fileobj)
48
    Runner.unlink_count += 1
49
50
51
def print_dirname(dirobj):
52
    """Only display the directory name, used with --dry-run."""
53
    log.debug("Would delete directory: %s", dirobj)
54
    Runner.rmdir_count += 1
55
56
57
def pyclean(args):
58
    """Cross-platform cleaning of Python bytecode."""
59
    Runner.unlink = print_filename if args.dry_run else remove_file
60
    Runner.rmdir = print_dirname if args.dry_run else remove_directory
61
62
    log_level = logging.FATAL if args.quiet \
63
        else logging.DEBUG if args.verbose \
64
        else logging.INFO
65
    log_format = "%(message)s"
66
    logging.basicConfig(level=log_level, format=log_format)
67
68
    for directory in args.directory:
69
        log.info("Cleaning directory %s", directory)
70
        descend_and_clean_bytecode(Path(directory))
71
72
    log.info("Total %d files, %d directories %s.",
73
             Runner.unlink_count, Runner.rmdir_count,
74
             "would be removed" if args.dry_run else "removed")
75
76
    if Runner.unlink_failed or Runner.rmdir_failed:
77
        log.debug("%d files, %d directories could not be removed.",
78
                  Runner.unlink_failed, Runner.rmdir_failed)
79
80
81
def descend_and_clean_bytecode(directory):
82
    """
83
    Walk and descend a directory tree, cleaning up bytecode files along
84
    the way. Only delete bytecode folders if they are empty in the end.
85
    """
86
    for child in directory.iterdir():
87
        if child.is_file():
88
            if child.suffix in ['.pyc', '.pyo']:
89
                Runner.unlink(child)
90
        elif child.is_dir():
91
            descend_and_clean_bytecode(child)
92
93
            if child.name == '__pycache__':
94
                Runner.rmdir(child)
95
        else:
96
            log.debug("Ignoring %s", child)
97