Passed
Push — main ( 7d001e...ab258d )
by Peter
01:10
created

pyclean.cli   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 191
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 126
dl 0
loc 191
rs 10
c 0
b 0
f 0
wmc 19

6 Functions

Rating   Name   Duplication   Size   Complexity  
D parse_arguments() 0 129 9
A main() 0 15 4
A init_logging() 0 9 3
A py2clean() 0 5 1
A py3clean() 0 5 1
A pypyclean() 0 5 1
1
"""
2
Command line interface implementation for pyclean.
3
"""
4
import argparse
5
import logging
6
import sys
7
8
from . import __version__, compat, modern
9
10
log = logging.getLogger(__name__)
11
12
13
def parse_arguments():
14
    """
15
    Parse and handle CLI arguments
16
    """
17
    debris_default_topics = ['cache', 'coverage', 'package', 'pytest', 'ruff']
18
    debris_optional_topics = ['jupyter', 'mypy', 'tox']
19
    debris_choices = ['all'] + debris_default_topics + debris_optional_topics
20
    ignore_default_items = [
21
        '.git',
22
        '.hg',
23
        '.svn',
24
        '.tox',
25
        '.venv',
26
        'node_modules',
27
        'venv',
28
    ]
29
30
    parser = argparse.ArgumentParser(
31
        description='Remove byte-compiled files for a package or project.',
32
    )
33
34
    if sys.version_info < (3, 8):
35
        parser.register('action', 'extend', compat.ExtendAction)
36
37
    parser.add_argument('--version', action='version', version=__version__)
38
    parser.add_argument(
39
        '-V',
40
        metavar='VERSION',
41
        dest='version',
42
        help='specify Python version to clean',
43
    )
44
    parser.add_argument(
45
        '-p',
46
        '--package',
47
        metavar='PACKAGE',
48
        action='append',
49
        default=[],
50
        help='Debian package to byte-compile ' '(may be specified multiple times)',
51
    )
52
    parser.add_argument(
53
        'directory',
54
        nargs='*',
55
        help='directory tree to traverse for byte-code',
56
    )
57
    parser.add_argument(
58
        '-i',
59
        '--ignore',
60
        metavar='DIRECTORY',
61
        action='extend',
62
        nargs='+',
63
        default=ignore_default_items,
64
        help='directory that should be ignored (may be specified multiple times;'
65
        ' default: %s)' % ' '.join(ignore_default_items),
66
    )
67
    parser.add_argument(
68
        '-d',
69
        '--debris',
70
        metavar='TOPIC',
71
        action='extend',
72
        nargs='*',
73
        default=argparse.SUPPRESS,
74
        choices=debris_choices,
75
        help='remove leftovers from popular Python development tools'
76
        ' (may be specified multiple times; optional: all %s; default: %s)'
77
        % (
78
            ' '.join(debris_optional_topics),
79
            ' '.join(debris_default_topics),
80
        ),
81
    )
82
    parser.add_argument(
83
        '-e',
84
        '--erase',
85
        metavar='PATTERN',
86
        action='extend',
87
        nargs='+',
88
        default=[],
89
        help='delete files or folders matching a globbing pattern (may be specified'
90
        ' multiple times); this will be interactive unless --yes is used.',
91
    )
92
    parser.add_argument(
93
        '--legacy',
94
        action='store_true',
95
        help='use legacy Debian implementation (autodetect)',
96
    )
97
    parser.add_argument(
98
        '-n',
99
        '--dry-run',
100
        action='store_true',
101
        help='show what would be done',
102
    )
103
104
    verbosity = parser.add_mutually_exclusive_group()
105
    verbosity.add_argument('-q', '--quiet', action='store_true', help='be quiet')
106
    verbosity.add_argument(
107
        '-v',
108
        '--verbose',
109
        action='store_true',
110
        help='be more verbose',
111
    )
112
113
    parser.add_argument(
114
        '-y',
115
        '--yes',
116
        action='store_true',
117
        help='assume yes as answer for interactive questions',
118
    )
119
120
    args = parser.parse_args()
121
    init_logging(args)
122
123
    if args.yes and not args.erase:
124
        parser.error('Specifying --yes only makes sense with --erase.')
125
126
    if not (args.package or args.directory):
127
        msg = 'A directory (or files) or a list of packages must be specified.'
128
        parser.error(msg)
129
130
    if 'debris' in args:
131
        if 'all' in args.debris:
132
            args.debris = debris_default_topics + debris_optional_topics
133
        elif not args.debris:
134
            args.debris = debris_default_topics
135
        log.debug('Debris topics to scan for: %s', ' '.join(args.debris))
136
    else:
137
        args.debris = []
138
139
    log.debug('Ignored directories: %s', ' '.join(args.ignore))
140
141
    return args
142
143
144
def init_logging(args):
145
    """
146
    Set the log level according to the -v/-q command line options.
147
    """
148
    log_level = (
149
        logging.FATAL if args.quiet else logging.DEBUG if args.verbose else logging.INFO
150
    )
151
    log_format = '%(message)s'
152
    logging.basicConfig(level=log_level, format=log_format)
153
154
155
def main(override=None):
156
    """
157
    Entry point for all scripts
158
    """
159
    args = parse_arguments()
160
    if override or args.legacy:
161
        impl = compat.get_implementation(override=override)
162
        pyclean_main = impl.main
163
    else:
164
        pyclean_main = modern.pyclean
165
166
    try:
167
        pyclean_main(args)
168
    except Exception as err:
169
        raise SystemExit(err)
170
171
172
def py2clean():
173
    """
174
    Forces the use of the implementation for Python 2
175
    """
176
    main('CPython2')
177
178
179
def py3clean():
180
    """
181
    Forces the use of the implementation for Python 3
182
    """
183
    main('CPython3')
184
185
186
def pypyclean():
187
    """
188
    Forces the use of the implementation for PyPy (2+3)
189
    """
190
    main('PyPy2')
191