Repository: python-lsp/python-lsp-server
Pull request #217 (2022-05-31 12:02:04)

Allow multiple per-file-ignores for the same pattern in flake8 plugin
Why this is needed:
In flake8, support for multiple ignores for the same pattern is allowed, but is currently not supported by the flake8 plugin.
If I have something like the following in my project's setup.cfg file:
```
[flake8]
per-file-ignores = __init__.py: F401, F403
```
Then running flake8 manually from the command line seems to behave as expected (ignore errors F401 and F403 from all `__init__.py` files), but running it from inside pylsp causes an error to be written to the log, and flake8 doesn't actually run. This small and admittedly not very elegant piece of code lets the flake8 plugin accept such a configuration. 

(In fact, with my version of flake8 (3.8.4), I couldn't find any way to write the above which would work both under pylsp and when running flake8 manually. A configuration of the form
```
[flake8]
per-file-ignores = 
    __init__.py: F401
    __init__.py: F403
```
which seem to be valid according to the flake8 spec, doesn't actually make flake8 ignore both errors, so this fix is the only way I found to have a configuration which behaves identically when running either manually or under pylsp)

(edit: some typos. Sorry).


ccordoba12: Hey @dedi, thanks a lot for your contribution! Could you add a test for this? That way it won't regress in the future.
dedi: Sure, sorry I missed that. The regression test is now there.

Patch:
diff --git a/pylsp/plugins/flake8_lint.py b/pylsp/plugins/flake8_lint.py
index 3707222..5c77e1d 100644
--- a/pylsp/plugins/flake8_lint.py
+++ b/pylsp/plugins/flake8_lint.py
@@ -31,8 +31,20 @@ def pylsp_lint(workspace, document):
     per_file_ignores = settings.get("perFileIgnores")
 
     if per_file_ignores:
+        prev_file_pat = None
         for path in per_file_ignores:
-            file_pat, errors = path.split(":")
+            try:
+                file_pat, errors = path.split(":")
+                prev_file_pat = file_pat
+            except ValueError:
+                # It's legal to just specify another error type for the same
+                # file pattern:
+                if prev_file_pat is None:
+                    log.warning(
+                        "skipping a Per-file-ignore with no file pattern")
+                    continue
+                file_pat = prev_file_pat
+                errors = path
             if PurePath(document.path).match(file_pat):
                 ignores.extend(errors.split(","))
 


Test patch:
diff --git a/test/plugins/test_flake8_lint.py b/test/plugins/test_flake8_lint.py
index 59a776a..a72383e 100644
--- a/test/plugins/test_flake8_lint.py
+++ b/test/plugins/test_flake8_lint.py
@@ -158,3 +158,25 @@ exclude =
     assert not res
 
     os.unlink(os.path.join(workspace.root_path, "setup.cfg"))
+
+
+def test_per_file_ignores_alternative_syntax(workspace):
+    config_str = r"""[flake8]
+per-file-ignores = **/__init__.py:F401,E402
+    """
+
+    doc_str = "print('hi')\nimport os\n"
+
+    doc_uri = uris.from_fs_path(os.path.join(workspace.root_path, "blah/__init__.py"))
+    workspace.put_document(doc_uri, doc_str)
+
+    flake8_settings = get_flake8_cfg_settings(workspace, config_str)
+
+    assert "perFileIgnores" in flake8_settings
+    assert len(flake8_settings["perFileIgnores"]) == 2
+
+    doc = workspace.get_document(doc_uri)
+    res = flake8_lint.pylsp_lint(workspace, doc)
+    assert not res
+
+    os.unlink(os.path.join(workspace.root_path, "setup.cfg"))