Skip to content

Commit 7cde613

Browse files
authored
typing fixes and improvements (#107)
* Add type parameters to Dict or List * Use dict and list from builtins in type hints Since we target Python 3.9, typing.Dict and typing.List are deprecated and builtins can be used instead. * Type windows_flag to fix 'call-overload' mypy error * Add the error code about windows_flag's type ignore This 'type ignore' is because mypy will raise an error on Linux as the subprocess module does not have a CREATE_NO_WINDOW attribute defined. * Re-use windows_flag in test * Add a comment about 'type: ignore' for WindowsFlag
1 parent cac3c40 commit 7cde613

File tree

2 files changed

+32
-31
lines changed

2 files changed

+32
-31
lines changed

pylsp_mypy/plugin.py

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
import tempfile
1919
from configparser import ConfigParser
2020
from pathlib import Path
21-
from typing import IO, Any, Dict, List, Optional
21+
from typing import IO, Any, Optional, TypedDict
2222

2323
try:
2424
import tomllib
@@ -47,9 +47,9 @@
4747
log = logging.getLogger(__name__)
4848

4949
# A mapping from workspace path to config file path
50-
mypyConfigFileMap: Dict[str, Optional[str]] = {}
50+
mypyConfigFileMap: dict[str, Optional[str]] = {}
5151

52-
settingsCache: Dict[str, Dict[str, Any]] = {}
52+
settingsCache: dict[str, dict[str, Any]] = {}
5353

5454
tmpFile: Optional[IO[bytes]] = None
5555

@@ -58,18 +58,25 @@
5858
# so store a cache of last diagnostics for each file a-la the pylint plugin,
5959
# so we can return some potentially-stale diagnostics.
6060
# https://github.com/python-lsp/python-lsp-server/blob/v1.0.1/pylsp/plugins/pylint_lint.py#L55-L62
61-
last_diagnostics: Dict[str, List[Dict[str, Any]]] = collections.defaultdict(list)
61+
last_diagnostics: dict[str, list[dict[str, Any]]] = collections.defaultdict(list)
62+
6263

6364
# Windows started opening opening a cmd-like window for every subprocess call
6465
# This flag prevents that.
6566
# This flag is new in python 3.7
66-
# This flag only exists on Windows
67-
windows_flag: Dict[str, int] = (
68-
{"creationflags": subprocess.CREATE_NO_WINDOW} if os.name == "nt" else {} # type: ignore
67+
# This flag only exists on Windows, hence the 'type: ignore[attr-defined]' below.
68+
class WindowsFlag(TypedDict, total=False):
69+
creationflags: int
70+
71+
72+
windows_flag: WindowsFlag = (
73+
{"creationflags": subprocess.CREATE_NO_WINDOW} # type: ignore[attr-defined]
74+
if os.name == "nt"
75+
else {}
6976
)
7077

7178

72-
def parse_line(line: str, document: Optional[Document] = None) -> Optional[Dict[str, Any]]:
79+
def parse_line(line: str, document: Optional[Document] = None) -> Optional[dict[str, Any]]:
7380
"""
7481
Return a language-server diagnostic from a line of the Mypy error report.
7582
@@ -128,7 +135,7 @@ def parse_line(line: str, document: Optional[Document] = None) -> Optional[Dict[
128135
return diag
129136

130137

131-
def apply_overrides(args: List[str], overrides: List[Any]) -> List[str]:
138+
def apply_overrides(args: list[str], overrides: list[Any]) -> list[str]:
132139
"""Replace or combine default command-line options with overrides."""
133140
overrides_iterator = iter(overrides)
134141
if True not in overrides_iterator:
@@ -140,7 +147,7 @@ def apply_overrides(args: List[str], overrides: List[Any]) -> List[str]:
140147
return overrides[: -(len(rest) + 1)] + args + rest
141148

142149

143-
def didSettingsChange(workspace: str, settings: Dict[str, Any]) -> None:
150+
def didSettingsChange(workspace: str, settings: dict[str, Any]) -> None:
144151
"""Handle relevant changes to the settings between runs."""
145152
configSubPaths = settings.get("config_sub_paths", [])
146153
if settingsCache[workspace].get("config_sub_paths", []) != configSubPaths:
@@ -154,7 +161,7 @@ def didSettingsChange(workspace: str, settings: Dict[str, Any]) -> None:
154161
settingsCache[workspace] = settings.copy()
155162

156163

157-
def match_exclude_patterns(document_path: str, exclude_patterns: list) -> bool:
164+
def match_exclude_patterns(document_path: str, exclude_patterns: list[str]) -> bool:
158165
"""Check if the current document path matches any of the configures exlude patterns."""
159166
document_path = document_path.replace(os.sep, "/")
160167

@@ -169,14 +176,14 @@ def match_exclude_patterns(document_path: str, exclude_patterns: list) -> bool:
169176
return False
170177

171178

172-
def get_cmd(settings: Dict[str, Any], cmd: str) -> List[str]:
179+
def get_cmd(settings: dict[str, Any], cmd: str) -> list[str]:
173180
"""
174181
Get the command to run from settings, falling back to searching the PATH.
175182
If the command is not found in the settings and is not available on the PATH, an
176183
empty list is returned.
177184
"""
178185
command_key = f"{cmd}_command"
179-
command: List[str] = settings.get(command_key, [])
186+
command: list[str] = settings.get(command_key, [])
180187

181188
if not (command and os.getenv("PYLSP_MYPY_ALLOW_DANGEROUS_CODE_EXECUTION")):
182189
# env var is required to allow command from settings
@@ -196,7 +203,7 @@ def get_cmd(settings: Dict[str, Any], cmd: str) -> List[str]:
196203
@hookimpl
197204
def pylsp_lint(
198205
config: Config, workspace: Workspace, document: Document, is_saved: bool
199-
) -> List[Dict[str, Any]]:
206+
) -> list[dict[str, Any]]:
200207
"""
201208
Call the linter.
202209
@@ -254,9 +261,9 @@ def pylsp_lint(
254261
def get_diagnostics(
255262
workspace: Workspace,
256263
document: Document,
257-
settings: Dict[str, Any],
264+
settings: dict[str, Any],
258265
is_saved: bool,
259-
) -> List[Dict[str, Any]]:
266+
) -> list[dict[str, Any]]:
260267
"""
261268
Lints.
262269
@@ -332,7 +339,7 @@ def get_diagnostics(
332339
args.extend(["--incremental", "--follow-imports", settings.get("follow-imports", "silent")])
333340
args = apply_overrides(args, overrides)
334341

335-
mypy_command: List[str] = get_cmd(settings, "mypy")
342+
mypy_command: list[str] = get_cmd(settings, "mypy")
336343

337344
if mypy_command:
338345
# mypy exists on PATH or was provided by settings
@@ -357,7 +364,7 @@ def get_diagnostics(
357364
# If daemon is dead/absent, kill will no-op.
358365
# In either case, reset to fresh state
359366

360-
dmypy_command: List[str] = get_cmd(settings, "dmypy")
367+
dmypy_command: list[str] = get_cmd(settings, "dmypy")
361368

362369
if dmypy_command:
363370
# dmypy exists on PATH or was provided by settings
@@ -449,7 +456,7 @@ def get_diagnostics(
449456

450457

451458
@hookimpl
452-
def pylsp_settings(config: Config) -> Dict[str, Dict[str, Dict[str, str]]]:
459+
def pylsp_settings(config: Config) -> dict[str, dict[str, dict[str, str]]]:
453460
"""
454461
Read the settings.
455462
@@ -468,7 +475,7 @@ def pylsp_settings(config: Config) -> Dict[str, Dict[str, Dict[str, str]]]:
468475
return {"plugins": {"pylsp_mypy": configuration}}
469476

470477

471-
def init(workspace: str) -> Dict[str, str]:
478+
def init(workspace: str) -> dict[str, str]:
472479
"""
473480
Find plugin and mypy config files and creates the temp file should it be used.
474481
@@ -509,7 +516,7 @@ def init(workspace: str) -> Dict[str, str]:
509516

510517

511518
def findConfigFile(
512-
path: str, configSubPaths: List[str], names: List[str], mypy: bool
519+
path: str, configSubPaths: list[str], names: list[str], mypy: bool
513520
) -> Optional[str]:
514521
"""
515522
Search for a config file.
@@ -580,9 +587,9 @@ def pylsp_code_actions(
580587
config: Config,
581588
workspace: Workspace,
582589
document: Document,
583-
range: Dict,
584-
context: Dict,
585-
) -> List[Dict]:
590+
range: dict[str, Any],
591+
context: dict[str, Any],
592+
) -> list[dict[str, Any]]:
586593
"""
587594
Provide code actions to ignore errors.
588595

test/test_plugin.py

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
import collections
22
import os
33
import re
4-
import subprocess
54
import sys
65
from pathlib import Path
7-
from typing import Dict
86
from unittest.mock import Mock, patch
97

108
import pytest
@@ -31,10 +29,6 @@
3129
'test_plugin.py:124:1:129:77: note: Use "-> None" if function does not return a value'
3230
)
3331

34-
windows_flag: Dict[str, int] = (
35-
{"creationflags": subprocess.CREATE_NO_WINDOW} if os.name == "nt" else {} # type: ignore
36-
)
37-
3832

3933
@pytest.fixture
4034
def last_diagnostics_monkeypatch(monkeypatch):
@@ -252,7 +246,7 @@ def test_option_overrides_dmypy(last_diagnostics_monkeypatch, workspace):
252246
"--no-pretty",
253247
document.path,
254248
]
255-
m.assert_called_with(expected, capture_output=True, **windows_flag, encoding="utf-8")
249+
m.assert_called_with(expected, capture_output=True, **plugin.windows_flag, encoding="utf-8")
256250

257251

258252
def test_dmypy_status_file(tmpdir, last_diagnostics_monkeypatch, workspace):

0 commit comments

Comments
 (0)