Skip to content

Commit 22d4b72

Browse files
committed
Encode patterns so patterns like "d:\\a\..." do work
Especially useful when using Windows paths
1 parent 5888f74 commit 22d4b72

File tree

2 files changed

+51
-8
lines changed

2 files changed

+51
-8
lines changed

pylsp_mypy/plugin.py

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,23 @@ def didSettingsChange(workspace: str, settings: Dict[str, Any]) -> None:
142142
settingsCache[workspace] = settings.copy()
143143

144144

145+
def match_exclude_patterns(document_path: str, exclude_patterns: list) -> bool:
146+
for pattern in exclude_patterns:
147+
# This makes sure that \\ characters (and other unicode characters) are
148+
# escaped first so the regex matcher will be able to parse them correctly.
149+
# Especially useful for matching windows paths without any bad escape errors
150+
pattern = pattern.encode("unicode-escape").decode()
151+
152+
try:
153+
if re.search(pattern, document_path):
154+
log.debug(f"{document_path} matches " f"exclude pattern '{pattern}'")
155+
return True
156+
except re.error as e:
157+
log.error(f"pattern {pattern} is not a valid regular expression: {e}")
158+
159+
return False
160+
161+
145162
@hookimpl
146163
def pylsp_lint(
147164
config: Config, workspace: Workspace, document: Document, is_saved: bool
@@ -185,13 +202,13 @@ def pylsp_lint(
185202
# configured with mypy. We can now add our own exclude section like so:
186203
# [tool.pylsp-mypy]
187204
# exclude = ["tests/*"]
188-
exclude = settings.get("exclude", [])
189-
for pattern in exclude:
190-
if re.search(pattern, document.path):
191-
log.debug(
192-
f"Not running because {document.path} matches " f"exclude pattern '{pattern}'"
193-
)
194-
return []
205+
exclude_patterns = settings.get("exclude", [])
206+
207+
if match_exclude_patterns(document_path=document.path, exclude_patterns=exclude_patterns):
208+
log.debug(
209+
f"Not running because {document.path} matches " f"exclude patterns '{exclude_patterns}'"
210+
)
211+
return []
195212

196213
if settings.get("report_progress", False):
197214
with workspace.report_progress("lint: mypy"):

test/test_plugin.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,33 @@ def foo():
330330
assert diag["code"] == "unreachable"
331331

332332

333-
def test_exclude_path_match_mypy_not_run(tmpdir, workspace):
333+
@pytest.mark.parametrize(
334+
"document_path,pattern,pattern_matched",
335+
(
336+
("/workspace/my-file.py", "/someting-else", False),
337+
("/workspace/my-file.py", "^/workspace$", False),
338+
("/workspace/my-file.py", "/workspace", True),
339+
("/workspace/my-file.py", "^/workspace(.*)$", True),
340+
# This is a broken regex (missing ')'), but should not choke
341+
("/workspace/my-file.py", "/((workspace)", False),
342+
# Windows paths are tricky with all those \\ and unintended escape,
343+
# characters but they should 'just' work
344+
("d:\\a\\my-file.py", "\\a", True),
345+
(
346+
"d:\\a\\pylsp-mypy\\pylsp-mypy\\test\\test_plugin.py",
347+
"d:\\a\\pylsp-mypy\\pylsp-mypy\\test\\test_plugin.py",
348+
True,
349+
),
350+
),
351+
)
352+
def test_match_exclude_patterns(document_path, pattern, pattern_matched):
353+
assert (
354+
plugin.match_exclude_patterns(document_path=document_path, exclude_patterns=[pattern])
355+
is pattern_matched
356+
)
357+
358+
359+
def test_config_exclude(tmpdir, workspace):
334360
"""When exclude is set in config then mypy should not run for that file."""
335361
doc = Document(DOC_URI, workspace, DOC_TYPE_ERR)
336362

0 commit comments

Comments
 (0)