diff options
Diffstat (limited to 'share')
-rw-r--r-- | share/spack/qa/flake8_formatter.py | 146 | ||||
-rwxr-xr-x | share/spack/spack-completion.bash | 2 |
2 files changed, 147 insertions, 1 deletions
diff --git a/share/spack/qa/flake8_formatter.py b/share/spack/qa/flake8_formatter.py new file mode 100644 index 0000000000..0660a97ccf --- /dev/null +++ b/share/spack/qa/flake8_formatter.py @@ -0,0 +1,146 @@ +import re +import sys +import pycodestyle +from collections import defaultdict +from flake8.formatting.default import Pylint +from flake8.style_guide import Violation + +#: This is a dict that maps: +#: filename pattern -> +#: flake8 exemption code -> +#: list of patterns, for which matching lines should have codes applied. +#: +#: For each file, if the filename pattern matches, we'll add per-line +#: exemptions if any patterns in the sub-dict match. +pattern_exemptions = { + # exemptions applied only to package.py files. + r"package.py$": { + # Allow 'from spack import *' in packages, but no other wildcards + "F403": [ + r"^from spack import \*$", + r"^from spack.pkgkit import \*$", + ], + # Exempt lines with urls and descriptions from overlong line errors. + "E501": [ + r"^\s*homepage\s*=", + r"^\s*url\s*=", + r"^\s*git\s*=", + r"^\s*svn\s*=", + r"^\s*hg\s*=", + r"^\s*list_url\s*=", + r"^\s*version\(", + r"^\s*variant\(", + r"^\s*provides\(", + r"^\s*extends\(", + r"^\s*depends_on\(", + r"^\s*conflicts\(", + r"^\s*resource\(", + r"^\s*patch\(", + ], + # Exempt '@when' decorated functions from redefinition errors. + "F811": [ + r"^\s*@when\(.*\)", + ], + }, + # exemptions applied to all files. + r".py$": { + "E501": [ + r"(https?|ftp|file)\:", # URLs + r'([\'"])[0-9a-fA-F]{32,}\1', # long hex checksums + ] + }, +} + + +# compile all regular expressions. +pattern_exemptions = dict( + ( + re.compile(file_pattern), + dict( + (code, [re.compile(p) for p in patterns]) + for code, patterns in error_dict.items() + ), + ) + for file_pattern, error_dict in pattern_exemptions.items() +) + + +class SpackFormatter(Pylint): + def __init__(self, options): + self.spack_errors = {} + self.error_seen = False + super().__init__(options) + + def after_init(self): # type: () -> None + """Overriding to keep format string from being unset in Default""" + pass + + def beginning(self, filename): + self.filename = filename + self.file_lines = None + self.spack_errors = defaultdict(list) + for file_pattern, errors in pattern_exemptions.items(): + if file_pattern.search(filename): + for code, pat_arr in errors.items(): + self.spack_errors[code].extend(pat_arr) + + def handle(self, error): # type: (Violation) -> None + """Handle an error reported by Flake8. + + This defaults to calling :meth:`format`, :meth:`show_source`, and + then :meth:`write`. This version implements the pattern-based ignore + behavior from `spack flake8` as a native flake8 plugin. + + :param error: + This will be an instance of + :class:`~flake8.style_guide.Violation`. + :type error: + flake8.style_guide.Violation + """ + + # print(error.code) + # print(error.physical_line) + # get list of patterns for this error code + pats = self.spack_errors.get(error.code, None) + # if any pattern matches, skip line + if pats is not None and any( + (pat.search(error.physical_line) for pat in pats) + ): + return + + # Special F811 handling + # Prior to Python 3.8, `noqa: F811` needed to be placed on the `@when` + # line + # Starting with Python 3.8, it must be placed on the `def` line + # https://gitlab.com/pycqa/flake8/issues/583 + # we can only determine if F811 should be ignored given the previous + # line, so get the previous line and check it + if ( + self.spack_errors.get("F811", False) + and error.code == "F811" + and error.line_number > 1 + ): + if self.file_lines is None: + if self.filename in {"stdin", "-", "(none)", None}: + self.file_lines = pycodestyle.stdin_get_value().splitlines( + True + ) + else: + self.file_lines = pycodestyle.readlines(self.filename) + for pat in self.spack_errors["F811"]: + if pat.search(self.file_lines[error.line_number - 2]): + return + + self.error_seen = True + line = self.format(error) + source = self.show_source(error) + self.write(line, source) + + def stop(self): + """Override stop to check whether any errors we consider to be errors + were reported. + + This is a hack, but it makes flake8 behave the desired way. + """ + if not self.error_seen: + sys.exit(0) diff --git a/share/spack/spack-completion.bash b/share/spack/spack-completion.bash index 0143d305ca..cbe59bc90a 100755 --- a/share/spack/spack-completion.bash +++ b/share/spack/spack-completion.bash @@ -913,7 +913,7 @@ _spack_find() { _spack_flake8() { if $list_options then - SPACK_COMPREPLY="-h --help -b --base -k --keep-temp -a --all -o --output -r --root-relative -U --no-untracked" + SPACK_COMPREPLY="-h --help -b --base -a --all -o --output -r --root-relative -U --no-untracked" else SPACK_COMPREPLY="" fi |