tools/lint/python/pylint.py
author Sebastian Hengst <archaeopteryx@coole-files.de>
Sun, 05 Feb 2023 13:12:06 +0100
changeset 651755 2e6262a410b7c25a3a7349825a7367462d4420fa
parent 643478 2f52237a22ee89f7d73b56d350e61075969cd689
permissions -rw-r--r--
Bug 1815090 - regenerate certificates in build/pgo/certs/. a=me * the build/pgo/certs/ changes were made using `./mach python build/pgo/genpgocert.py`

# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

import json
import os
import signal
import subprocess

from mach.site import InstallPipRequirementsException
from mozlint import result
from mozlint.pathutils import expand_exclusions
from mozprocess import ProcessHandler

here = os.path.abspath(os.path.dirname(__file__))
PYLINT_REQUIREMENTS_PATH = os.path.join(here, "pylint_requirements.txt")

PYLINT_NOT_FOUND = """
Could not find pylint! Install pylint and try again.

    $ pip install -U --require-hashes -r {}
""".strip().format(
    PYLINT_REQUIREMENTS_PATH
)


PYLINT_INSTALL_ERROR = """
Unable to install correct version of pylint
Try to install it manually with:
    $ pip install -U --require-hashes -r {}
""".strip().format(
    PYLINT_REQUIREMENTS_PATH
)


class PylintProcess(ProcessHandler):
    def __init__(self, config, *args, **kwargs):
        self.config = config
        kwargs["stream"] = False
        kwargs["universal_newlines"] = True
        ProcessHandler.__init__(self, *args, **kwargs)

    def run(self, *args, **kwargs):
        orig = signal.signal(signal.SIGINT, signal.SIG_IGN)
        ProcessHandler.run(self, *args, **kwargs)
        signal.signal(signal.SIGINT, orig)


def setup(root, **lintargs):
    virtualenv_manager = lintargs["virtualenv_manager"]
    try:
        virtualenv_manager.install_pip_requirements(
            PYLINT_REQUIREMENTS_PATH,
            quiet=True,
        )
    except (subprocess.CalledProcessError, InstallPipRequirementsException):
        print(PYLINT_INSTALL_ERROR)
        return 1


def get_pylint_binary():
    return "pylint"


def run_process(config, cmd):
    proc = PylintProcess(config, cmd)
    proc.run()
    try:
        proc.wait()
    except KeyboardInterrupt:
        proc.kill()

    return proc.output


def parse_issues(log, config, issues_json, path):
    results = []

    try:
        issues = json.loads(issues_json)
    except json.decoder.JSONDecodeError:
        log.debug("Could not parse the output:")
        log.debug("pylint output: {}".format(issues_json))
        return []

    for issue in issues:
        res = {
            "path": issue["path"],
            "level": issue["type"],
            "lineno": issue["line"],
            "column": issue["column"],
            "message": issue["message"],
            "rule": issue["message-id"],
        }
        results.append(result.from_config(config, **res))
    return results


def get_pylint_version(binary):
    return subprocess.check_output(
        [binary, "--version"],
        universal_newlines=True,
        stderr=subprocess.STDOUT,
    )


def lint(paths, config, **lintargs):
    log = lintargs["log"]

    binary = get_pylint_binary()

    log = lintargs["log"]
    paths = list(expand_exclusions(paths, config, lintargs["root"]))

    cmd_args = [binary]
    results = []

    # list from https://code.visualstudio.com/docs/python/linting#_pylint
    # And ignore a bit more elements
    cmd_args += [
        "-fjson",
        "--disable=all",
        "--enable=F,E,unreachable,duplicate-key,unnecessary-semicolon,global-variable-not-assigned,unused-variable,binary-op-exception,bad-format-string,anomalous-backslash-in-string,bad-open-mode,no-else-return",  # NOQA: E501
        "--disable=import-error,no-member",
    ]

    base_command = cmd_args + paths
    log.debug("Command: {}".format(" ".join(cmd_args)))
    log.debug("pylint version: {}".format(get_pylint_version(binary)))
    output = " ".join(run_process(config, base_command))
    results = parse_issues(log, config, str(output), [])

    return results