diff options
Diffstat (limited to 'tools/lint/spell')
-rw-r--r-- | tools/lint/spell/__init__.py | 168 | ||||
-rw-r--r-- | tools/lint/spell/codespell_requirements.in | 1 | ||||
-rw-r--r-- | tools/lint/spell/codespell_requirements.txt | 10 | ||||
-rw-r--r-- | tools/lint/spell/exclude-list.txt | 23 |
4 files changed, 202 insertions, 0 deletions
diff --git a/tools/lint/spell/__init__.py b/tools/lint/spell/__init__.py new file mode 100644 index 0000000000..65712acdd7 --- /dev/null +++ b/tools/lint/spell/__init__.py @@ -0,0 +1,168 @@ +# 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 + +# py2-compat +try: + from json.decoder import JSONDecodeError +except ImportError: + JSONDecodeError = ValueError + +from mozfile import which +from mozlint import result +from mozlint.util.implementation import LintProcess + +here = os.path.abspath(os.path.dirname(__file__)) +CODESPELL_REQUIREMENTS_PATH = os.path.join(here, "codespell_requirements.txt") + +CODESPELL_NOT_FOUND = """ +Could not find codespell! Install codespell and try again. + + $ pip install -U --require-hashes -r {} +""".strip().format( + CODESPELL_REQUIREMENTS_PATH +) + + +CODESPELL_INSTALL_ERROR = """ +Unable to install correct version of codespell +Try to install it manually with: + $ pip install -U --require-hashes -r {} +""".strip().format( + CODESPELL_REQUIREMENTS_PATH +) + +results = [] + +CODESPELL_FORMAT_REGEX = re.compile(r"(.*):(.*): (.*) ==> (.*)$") + + +class CodespellProcess(LintProcess): + fixed = 0 + _fix = None + + def process_line(self, line): + try: + match = CODESPELL_FORMAT_REGEX.match(line) + abspath, line, typo, correct = match.groups() + except AttributeError: + if "FIXED: " not in line: + print("Unable to match regex against output: {}".format(line)) + return + + if CodespellProcess._fix: + CodespellProcess.fixed += 1 + + # Ignore false positive like aParent (which would be fixed to apparent) + # See https://github.com/lucasdemarchi/codespell/issues/314 + m = re.match(r"^[a-z][A-Z][a-z]*", typo) + if m: + return + res = { + "path": abspath, + "message": typo.strip() + " ==> " + correct, + "level": "error", + "lineno": line, + } + results.append(result.from_config(self.config, **res)) + + +def run_process(config, cmd): + proc = CodespellProcess(config, cmd) + proc.run() + try: + proc.wait() + except KeyboardInterrupt: + proc.kill() + + +def get_codespell_binary(): + """ + Returns the path of the first codespell binary available + if not found returns None + """ + binary = os.environ.get("CODESPELL") + if binary: + return binary + + return which("codespell") + + +def setup(root, **lintargs): + virtualenv_manager = lintargs["virtualenv_manager"] + try: + virtualenv_manager.install_pip_requirements( + CODESPELL_REQUIREMENTS_PATH, quiet=True + ) + except subprocess.CalledProcessError: + print(CODESPELL_INSTALL_ERROR) + return 1 + + +def get_codespell_version(binary): + return subprocess.check_output( + [which("python"), binary, "--version"], + universal_newlines=True, + stderr=subprocess.STDOUT, + ) + + +def get_ignored_words_file(config): + config_root = os.path.dirname(config["path"]) + return os.path.join(config_root, "spell", "exclude-list.txt") + + +def lint(paths, config, fix=None, **lintargs): + log = lintargs["log"] + binary = get_codespell_binary() + if not binary: + print(CODESPELL_NOT_FOUND) + if "MOZ_AUTOMATION" in os.environ: + return 1 + return [] + + config["root"] = lintargs["root"] + + exclude_list = get_ignored_words_file(config) + cmd_args = [ + which("python"), + binary, + "--disable-colors", + # Silence some warnings: + # 1: disable warnings about wrong encoding + # 2: disable warnings about binary file + # 4: shut down warnings about automatic fixes + # that were disabled in dictionary. + "--quiet-level=7", + "--ignore-words=" + exclude_list, + ] + + if "exclude" in config: + cmd_args.append("--skip=*.dic,{}".format(",".join(config["exclude"]))) + + log.debug("Command: {}".format(" ".join(cmd_args))) + log.debug("Version: {}".format(get_codespell_version(binary))) + + if fix: + CodespellProcess._fix = True + + base_command = cmd_args + paths + run_process(config, base_command) + + if fix: + global results + results = [] + cmd_args.append("--write-changes") + log.debug("Command: {}".format(" ".join(cmd_args))) + log.debug("Version: {}".format(get_codespell_version(binary))) + base_command = cmd_args + paths + run_process(config, base_command) + CodespellProcess.fixed = CodespellProcess.fixed - len(results) + else: + CodespellProcess.fixed = 0 + + return {"results": results, "fixed": CodespellProcess.fixed} diff --git a/tools/lint/spell/codespell_requirements.in b/tools/lint/spell/codespell_requirements.in new file mode 100644 index 0000000000..407f17489c --- /dev/null +++ b/tools/lint/spell/codespell_requirements.in @@ -0,0 +1 @@ +codespell==2.2.4 diff --git a/tools/lint/spell/codespell_requirements.txt b/tools/lint/spell/codespell_requirements.txt new file mode 100644 index 0000000000..d8f0be543b --- /dev/null +++ b/tools/lint/spell/codespell_requirements.txt @@ -0,0 +1,10 @@ +# +# This file is autogenerated by pip-compile with Python 3.11 +# by the following command: +# +# pip-compile --generate-hashes tools/lint/spell/codespell_requirements.in +# +codespell==2.2.4 \ + --hash=sha256:0b4620473c257d9cde1ff8998b26b2bb209a35c2b7489f5dc3436024298ce83a \ + --hash=sha256:7d984b8130108e6f82524b7d09f8b7bf2fb1e398c5d4b37d9e2bd310145b3e29 + # via -r tools/lint/spell/codespell_requirements.in diff --git a/tools/lint/spell/exclude-list.txt b/tools/lint/spell/exclude-list.txt new file mode 100644 index 0000000000..b5c0290882 --- /dev/null +++ b/tools/lint/spell/exclude-list.txt @@ -0,0 +1,23 @@ +cas +optin +aparent +acount +te +wasn +incrementall +aare +whats +crate +files' +thru +referer +dur +ue +tring +delink +warmup +aNumber +falsy +rduce +complies +ehr |