|
1 | 1 | #! /usr/bin/env python3
|
2 | 2 |
|
3 | 3 | """
|
4 |
| -Performs pylint on all python files in the project repo's {test,script,docs} directory recursively. |
5 |
| -
|
6 |
| -This script is meant to be run from the CI but can also be easily in local dev environment, |
7 |
| -where you can optionally pass `-d` as command line argument to let this script abort on first error. |
| 4 | +Runs pylint on all Python files in project directories known to contain Python scripts. |
8 | 5 | """
|
9 | 6 |
|
10 |
| -from os import path, walk, system |
11 |
| -from sys import argv, exit as exitwith |
| 7 | +from argparse import ArgumentParser |
| 8 | +from os import path, walk |
| 9 | +from sys import exit |
| 10 | +from textwrap import dedent |
| 11 | +import subprocess |
| 12 | +import sys |
12 | 13 |
|
13 |
| -PROJECT_ROOT = path.dirname(path.realpath(__file__)) |
14 |
| -PYLINT_RCFILE = "{}/pylintrc".format(PROJECT_ROOT) |
| 14 | +PROJECT_ROOT = path.dirname(path.dirname(path.realpath(__file__))) |
| 15 | +PYLINT_RCFILE = f"{PROJECT_ROOT}/scripts/pylintrc" |
15 | 16 |
|
16 | 17 | SGR_INFO = "\033[1;32m"
|
17 | 18 | SGR_CLEAR = "\033[0m"
|
18 | 19 |
|
19 | 20 | def pylint_all_filenames(dev_mode, rootdirs):
|
20 | 21 | """ Performs pylint on all python files within given root directory (recursively). """
|
| 22 | + |
| 23 | + BARE_COMMAND = [ |
| 24 | + "pylint", |
| 25 | + f"--rcfile={PYLINT_RCFILE}", |
| 26 | + "--output-format=colorized", |
| 27 | + "--score=n" |
| 28 | + ] |
| 29 | + |
21 | 30 | filenames = []
|
22 | 31 | for rootdir in rootdirs:
|
23 | 32 | for rootpath, _, filenames_w in walk(rootdir):
|
24 | 33 | for filename in filenames_w:
|
25 | 34 | if filename.endswith('.py'):
|
26 | 35 | filenames.append(path.join(rootpath, filename))
|
27 | 36 |
|
28 |
| - checked_count = 0 |
29 |
| - failed = [] |
30 |
| - for filename in filenames: |
31 |
| - checked_count += 1 |
32 |
| - cmdline = "pylint --rcfile=\"{}\" \"{}\"".format(PYLINT_RCFILE, filename) |
33 |
| - print("{}[{}/{}] Running pylint on file: {}{}".format(SGR_INFO, checked_count, len(filenames), |
34 |
| - filename, SGR_CLEAR)) |
35 |
| - exit_code = system(cmdline) |
36 |
| - if exit_code != 0: |
37 |
| - if dev_mode: |
38 |
| - return 1, checked_count |
39 |
| - failed.append(filename) |
40 |
| - |
41 |
| - return len(failed), len(filenames) |
| 37 | + if not dev_mode: |
| 38 | + # NOTE: We could just give pylint the directories and it would find the files on its |
| 39 | + # own but it would treat them as packages, which would result in lots of import errors. |
| 40 | + command_line = BARE_COMMAND + filenames |
| 41 | + return subprocess.run(command_line, check=False).returncode == 0 |
| 42 | + |
| 43 | + for i, filename in enumerate(filenames): |
| 44 | + command_line = BARE_COMMAND + [filename] |
| 45 | + print( |
| 46 | + f"{SGR_INFO}" |
| 47 | + f"[{i + 1}/{len(filenames)}] " |
| 48 | + f"Running pylint on file: {filename}{SGR_CLEAR}" |
| 49 | + ) |
| 50 | + |
| 51 | + process = subprocess.run(command_line, check=False) |
| 52 | + |
| 53 | + if process.returncode != 0: |
| 54 | + return False |
| 55 | + |
| 56 | + print() |
| 57 | + return True |
| 58 | + |
| 59 | + |
| 60 | +def parse_command_line(): |
| 61 | + script_description = dedent(""" |
| 62 | + Runs pylint on all Python files in project directories known to contain Python scripts. |
| 63 | +
|
| 64 | + This script is meant to be run from the CI but can also be easily used in the local dev |
| 65 | + environment. |
| 66 | + """) |
| 67 | + |
| 68 | + parser = ArgumentParser(description=script_description) |
| 69 | + parser.add_argument( |
| 70 | + '-d', '--dev-mode', |
| 71 | + dest='dev_mode', |
| 72 | + default=False, |
| 73 | + action='store_true', |
| 74 | + help=( |
| 75 | + "Abort on first error. " |
| 76 | + "In this mode every script is passed to pylint separately. " |
| 77 | + ) |
| 78 | + ) |
| 79 | + return parser.parse_args() |
| 80 | + |
42 | 81 |
|
43 | 82 | def main():
|
44 |
| - """ Collects all python script root dirs and runs pylint on them. You can optionally |
45 |
| - pass `-d` as command line argument to let this script abort on first error. """ |
46 |
| - dev_mode = len(argv) == 2 and argv[1] == "-d" |
47 |
| - failed_count, total_count = pylint_all_filenames(dev_mode, [ |
48 |
| - path.abspath(path.dirname(__file__) + "/../docs"), |
49 |
| - path.abspath(path.dirname(__file__) + "/../scripts"), |
50 |
| - path.abspath(path.dirname(__file__) + "/../test")]) |
51 |
| - if failed_count != 0: |
52 |
| - exitwith("pylint failed on {}/{} files.".format(failed_count, total_count)) |
| 83 | + options = parse_command_line() |
| 84 | + |
| 85 | + rootdirs = [ |
| 86 | + f"{PROJECT_ROOT}/docs", |
| 87 | + f"{PROJECT_ROOT}/scripts", |
| 88 | + f"{PROJECT_ROOT}/test", |
| 89 | + ] |
| 90 | + success = pylint_all_filenames(options.dev_mode, rootdirs) |
| 91 | + |
| 92 | + if not success: |
| 93 | + exit(1) |
53 | 94 | else:
|
54 |
| - print("Successfully tested {} files.".format(total_count)) |
| 95 | + print("No problems found.") |
| 96 | + |
55 | 97 |
|
56 | 98 | if __name__ == "__main__":
|
57 |
| - main() |
| 99 | + try: |
| 100 | + main() |
| 101 | + except KeyboardInterrupt: |
| 102 | + exit("Interrupted by user. Exiting.") |
0 commit comments