summaryrefslogtreecommitdiff
path: root/share
diff options
context:
space:
mode:
Diffstat (limited to 'share')
-rw-r--r--share/spack/qa/flake8_formatter.py146
-rwxr-xr-xshare/spack/spack-completion.bash2
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