diff options
Diffstat (limited to '')
-rw-r--r-- | tools/lint/rst/__init__.py | 116 |
1 files changed, 116 insertions, 0 deletions
diff --git a/tools/lint/rst/__init__.py b/tools/lint/rst/__init__.py new file mode 100644 index 0000000000..7151c09a59 --- /dev/null +++ b/tools/lint/rst/__init__.py @@ -0,0 +1,116 @@ +# 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 os +import re +import subprocess + +from mozfile import which +from mozlint import result +from mozlint.pathutils import expand_exclusions + +# Error Levels +# (0, 'debug') +# (1, 'info') +# (2, 'warning') +# (3, 'error') +# (4, 'severe') + +abspath = os.path.abspath(os.path.dirname(__file__)) +rstcheck_requirements_file = os.path.join(abspath, "requirements.txt") + +results = [] + +RSTCHECK_NOT_FOUND = """ +Could not find rstcheck! Install rstcheck and try again. + + $ pip install -U --require-hashes -r {} +""".strip().format( + rstcheck_requirements_file +) + +RSTCHECK_INSTALL_ERROR = """ +Unable to install required version of rstcheck +Try to install it manually with: + $ pip install -U --require-hashes -r {} +""".strip().format( + rstcheck_requirements_file +) + +RSTCHECK_FORMAT_REGEX = re.compile(r"(.*):(.*): \(.*/([0-9]*)\) (.*)$") +IGNORE_NOT_REF_LINK_UPSTREAM_BUG = re.compile( + r"Hyperlink target (.*) is not referenced." +) + + +def setup(root, **lintargs): + virtualenv_manager = lintargs["virtualenv_manager"] + try: + virtualenv_manager.install_pip_requirements( + rstcheck_requirements_file, quiet=True + ) + except subprocess.CalledProcessError: + print(RSTCHECK_INSTALL_ERROR) + return 1 + + +def get_rstcheck_binary(): + """ + Returns the path of the first rstcheck binary available + if not found returns None + """ + binary = os.environ.get("RSTCHECK") + if binary: + return binary + + return which("rstcheck") + + +def parse_with_split(errors): + match = RSTCHECK_FORMAT_REGEX.match(errors) + filename, lineno, level, message = match.groups() + + return filename, lineno, level, message + + +def lint(files, config, **lintargs): + log = lintargs["log"] + config["root"] = lintargs["root"] + paths = expand_exclusions(files, config, config["root"]) + paths = list(paths) + chunk_size = 50 + binary = get_rstcheck_binary() + rstcheck_options = [ + "--ignore-language=cpp,json", + "--ignore-roles=searchfox", + ] + + while paths: + cmdargs = [which("python"), binary] + rstcheck_options + paths[:chunk_size] + log.debug("Command: {}".format(" ".join(cmdargs))) + + proc = subprocess.Popen( + cmdargs, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env=os.environ, + universal_newlines=True, + ) + all_errors = proc.communicate()[1] + for errors in all_errors.split("\n"): + if len(errors) > 1: + filename, lineno, level, message = parse_with_split(errors) + if not IGNORE_NOT_REF_LINK_UPSTREAM_BUG.match(message): + # Ignore an upstream bug + # https://github.com/myint/rstcheck/issues/19 + res = { + "path": filename, + "message": message, + "lineno": lineno, + "level": "error" if int(level) >= 2 else "warning", + } + results.append(result.from_config(config, **res)) + paths = paths[chunk_size:] + + return results |