diff --git a/.circleci/config.yml b/.circleci/config.yml index a77904474..4a3698c62 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -257,6 +257,22 @@ jobs: name: Check for C++ coding style command: ./scripts/check_style.sh + chk_pylint: + docker: + - image: buildpack-deps:eoan + steps: + - checkout + - run: + name: Install pip + command: apt -q update && apt install -y python3-pip + - run: + name: Install pylint + command: python3 -m pip install pylint z3-solver + # also z3-solver to make sure pylint knows about this module + - run: + name: Linting Python Scripts + command: ./scripts/pylint_all.py + chk_buglist: docker: - image: circleci/node @@ -718,6 +734,7 @@ workflows: # DISABLED FOR 0.6.0 - chk_docs_examples: *workflow_trigger_on_tags - chk_buglist: *workflow_trigger_on_tags - chk_proofs: *workflow_trigger_on_tags + - chk_pylint: *workflow_trigger_on_tags # build-only - b_docs: *workflow_trigger_on_tags diff --git a/scripts/pylint_all.py b/scripts/pylint_all.py new file mode 100755 index 000000000..a0ddf701c --- /dev/null +++ b/scripts/pylint_all.py @@ -0,0 +1,56 @@ +#! /usr/bin/env python3 + +""" +Performs pylint on all python files in the project repo's test directory recursively. + +This script is meant to be run from the CI but can also be easily in local dev environment, +where you can optionally pass `-d` as command line argument to let this script abort on first error. +""" + +from os import path, walk, system +from sys import argv, exit as brexit + +PROJECT_ROOT = path.dirname(path.realpath(__file__)) +PYLINT_RCFILE = "{}/pylintrc".format(PROJECT_ROOT) + +SGR_INFO = "\033[1;32m" +SGR_CLEAR = "\033[0m" + +def pylint_all_filenames(dev_mode, rootdirs): + """ Performs pylint on all python files within given root directory (recursively). """ + filenames = [] + for rootdir in rootdirs: + for rootpath, _, filenames_w in walk(rootdir): + for filename in filenames_w: + if filename.endswith('.py'): + filenames.append(path.join(rootpath, filename)) + + checked_count = 0 + failed = [] + for filename in filenames: + checked_count += 1 + cmdline = "pylint --rcfile=\"{}\" \"{}\"".format(PYLINT_RCFILE, filename) + print("{}[{}/{}] Running pylint on file: {}{}".format(SGR_INFO, checked_count, len(filenames), + filename, SGR_CLEAR)) + exit_code = system(cmdline) + if exit_code != 0: + if dev_mode: + return 1, checked_count + else: + failed.append(filename) + + return len(failed), len(filenames) + +def main(): + """" Collects all python script root dirs and runs pylint on them. """ + dev_mode = len(argv) == 2 and argv[1] == "-d" + failed_count, total_count = pylint_all_filenames(dev_mode, [ + path.abspath(path.dirname(__file__) + "/../scripts"), + path.abspath(path.dirname(__file__) + "/../test")]) + if failed_count != 0: + brexit("pylint failed on {}/{} files.".format(failed_count, total_count)) + else: + print("Successfully tested {} files.".format(total_count)) + +if __name__ == "__main__": + main() diff --git a/scripts/pylintrc b/scripts/pylintrc new file mode 100644 index 000000000..bf1f3c0c4 --- /dev/null +++ b/scripts/pylintrc @@ -0,0 +1,74 @@ +# vim:et:ts=4 +[MESSAGES CONTROL] + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifiers separated by comma (,) or put this +# option multiple times (only on the command line, not in the configuration +# file where it should appear only once).You can also use "--disable=all" to +# disable everything first and then re-enable specific checks. For example, if +# you want to run only the similarities checker, you can use "--disable=all +# --enable=similarities". If you want to run only the classes checker, but have +# no Warning level messages displayed, use"--disable=all --enable=classes +# --disable=W" + +# ATTENTION: This list should be extended with care, consider using NOLINT comments inside your +# python files instead, as the goal is actually to reduce the list of globally disabled checks. +# +# TODO: What could be eliminated in future PRs: bad-continuation, invalid-name, redefined-builtin, +# undefined-variable, unused-*, useless-object-inheritance. +disable= + bad-continuation, + bad-indentation, + bad-whitespace, + consider-using-sys-exit, + invalid-name, + missing-docstring, + mixed-indentation, + no-else-return, + no-self-use, + pointless-string-statement, + redefined-builtin, + redefined-outer-name, + singleton-comparison, + superfluous-parens, + too-few-public-methods, + trailing-newlines, + undefined-variable, + ungrouped-imports, + unnecessary-semicolon, + unused-import, + unused-variable, + unused-wildcard-import, + useless-object-inheritance, + wildcard-import + +[BASIC] + +# Good variable names which should always be accepted, separated by a comma +# added: f, h, x, a, b, p +good-names= + Run, + _, + a, + b, + ex, + f, + f, + h, + h, + i, + j, + k, + l, + m, + n, + p, + x + +[FORMAT] + +# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. +expected-line-ending-format=LF + +# Maximum number of characters on a single line. +max-line-length=130