From 2fe41e2702c283d16260e856184569fd14027137 Mon Sep 17 00:00:00 2001 From: Pengji Zhang Date: Sun, 19 Nov 2023 23:07:24 -0500 Subject: [PATCH 1/2] Refine diagnostic severity for flake8 Previously all diagnostics that starts with "F" (generated from pyflakes) are reported as errors, which could be too high. As such, in this commit we change to report only certain pyflakes diagnostics as errors and all the others as warnings. The current list of error codes is in sync with the `pyflakes_lint` plugin. While it is possible to generate such a list (or more precisely, a `set`) during runtime (see the snippet in the comment), that might be less ideal because: - Flake8 does not guarantee a stable API. - We run `flake8` as an executable and parse its outputs, so using the `flake8` module in Python may bring inconsistency (e.g., the user install Flake8 in a different virtual environment from pylsp). - Doing so couples two plugins (flake8 and pyflakes) together. So it seems better to maintain such a list of error codes manually. Fix #256. --- pylsp/plugins/flake8_lint.py | 25 ++++++++++++++++++++++++- test/plugins/test_flake8_lint.py | 8 ++++---- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/pylsp/plugins/flake8_lint.py b/pylsp/plugins/flake8_lint.py index 8d8d4c5f..17ce9db0 100644 --- a/pylsp/plugins/flake8_lint.py +++ b/pylsp/plugins/flake8_lint.py @@ -20,6 +20,29 @@ "F523", # .format(...) unused positional arguments "F841", # local variable `name` is assigned to but never used } +ERROR_CODES = { + # Errors from flake8 itself + "E999", # syntax error + # Errors from the pyflakes plugin of flake8 + # + # The following list should be kept in sync with the `pyflakes_lint` plugin. + # Here is a snippet to generate such a list: + # + # from flake8.plugins.pyflakes import FLAKE8_PYFLAKES_CODES + # from pylsp.plugins.pyflakes_lint import PYFLAKES_ERROR_MESSAGES + # for m in PYFLAKES_ERROR_MESSAGES: + # print(f'"{FLAKE8_PYFLAKES_CODES[m.__name__]}", # {m.__name__}') + "F821", # UndefinedName + "F822", # UndefinedExport + "F823", # UndefinedLocal + "F831", # DuplicateArgument + "F407", # FutureFeatureNotDefined + "F706", # ReturnOutsideFunction + "F704", # YieldOutsideFunction + "F702", # ContinueOutsideLoop + "F701", # BreakOutsideLoop + "F622", # TwoStarredExpressions +} @hookimpl @@ -208,7 +231,7 @@ def parse_stdout(source, stdout): # show also the code in message msg = code + " " + msg severity = lsp.DiagnosticSeverity.Warning - if code == "E999" or code[0] == "F": + if code in ERROR_CODES: severity = lsp.DiagnosticSeverity.Error diagnostic = { "source": "flake8", diff --git a/test/plugins/test_flake8_lint.py b/test/plugins/test_flake8_lint.py index 882bc996..c2d711e7 100644 --- a/test/plugins/test_flake8_lint.py +++ b/test/plugins/test_flake8_lint.py @@ -40,7 +40,7 @@ def test_flake8_unsaved(workspace): assert unused_var["code"] == "F841" assert unused_var["range"]["start"] == {"line": 5, "character": 1} assert unused_var["range"]["end"] == {"line": 5, "character": 11} - assert unused_var["severity"] == lsp.DiagnosticSeverity.Error + assert unused_var["severity"] == lsp.DiagnosticSeverity.Warning assert unused_var["tags"] == [lsp.DiagnosticTag.Unnecessary] @@ -55,7 +55,7 @@ def test_flake8_lint(workspace): assert unused_var["code"] == "F841" assert unused_var["range"]["start"] == {"line": 5, "character": 1} assert unused_var["range"]["end"] == {"line": 5, "character": 11} - assert unused_var["severity"] == lsp.DiagnosticSeverity.Error + assert unused_var["severity"] == lsp.DiagnosticSeverity.Warning finally: os.remove(name) @@ -101,7 +101,7 @@ def test_flake8_respecting_configuration(workspace): "end": {"line": 5, "character": 11}, }, "message": "F841 local variable 'a' is assigned to but never used", - "severity": 1, + "severity": 2, "tags": [1], }, ] @@ -116,7 +116,7 @@ def test_flake8_respecting_configuration(workspace): "end": {"line": 0, "character": 9}, }, "message": "F401 'os' imported but unused", - "severity": 1, + "severity": 2, "tags": [1], } ] From 93d2f260b5f080aad875c17bea5e1ebffb4128af Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Mon, 18 Dec 2023 17:11:13 -0500 Subject: [PATCH 2/2] Get error codes directly from flake8 --- pylsp/plugins/flake8_lint.py | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/pylsp/plugins/flake8_lint.py b/pylsp/plugins/flake8_lint.py index 17ce9db0..77aa22b9 100644 --- a/pylsp/plugins/flake8_lint.py +++ b/pylsp/plugins/flake8_lint.py @@ -9,9 +9,13 @@ from pathlib import PurePath from subprocess import PIPE, Popen +from flake8.plugins.pyflakes import FLAKE8_PYFLAKES_CODES + from pylsp import hookimpl, lsp +from pylsp.plugins.pyflakes_lint import PYFLAKES_ERROR_MESSAGES log = logging.getLogger(__name__) + FIX_IGNORES_RE = re.compile(r"([^a-zA-Z0-9_,]*;.*(\W+||$))") UNNECESSITY_CODES = { "F401", # `module` imported but unused @@ -20,29 +24,14 @@ "F523", # .format(...) unused positional arguments "F841", # local variable `name` is assigned to but never used } -ERROR_CODES = { - # Errors from flake8 itself - "E999", # syntax error +# NOTE: If the user sets the flake8 executable with workspace configuration, the +# error codes in this set may be inaccurate. +ERROR_CODES = ( # Errors from the pyflakes plugin of flake8 - # - # The following list should be kept in sync with the `pyflakes_lint` plugin. - # Here is a snippet to generate such a list: - # - # from flake8.plugins.pyflakes import FLAKE8_PYFLAKES_CODES - # from pylsp.plugins.pyflakes_lint import PYFLAKES_ERROR_MESSAGES - # for m in PYFLAKES_ERROR_MESSAGES: - # print(f'"{FLAKE8_PYFLAKES_CODES[m.__name__]}", # {m.__name__}') - "F821", # UndefinedName - "F822", # UndefinedExport - "F823", # UndefinedLocal - "F831", # DuplicateArgument - "F407", # FutureFeatureNotDefined - "F706", # ReturnOutsideFunction - "F704", # YieldOutsideFunction - "F702", # ContinueOutsideLoop - "F701", # BreakOutsideLoop - "F622", # TwoStarredExpressions -} + {FLAKE8_PYFLAKES_CODES.get(m.__name__, "E999") for m in PYFLAKES_ERROR_MESSAGES} + # Syntax error from flake8 itself + | {"E999"} +) @hookimpl