Add mypy plugin support to stubtest configuration (#13948)

This commit is contained in:
Huy Nguyen
2025-05-11 19:45:30 +07:00
committed by GitHub
parent 0933303ff0
commit a8fa1ab0d9
6 changed files with 61 additions and 5 deletions
+15 -1
View File
@@ -11,7 +11,7 @@ import urllib.parse
from collections.abc import Mapping
from dataclasses import dataclass
from pathlib import Path
from typing import Annotated, Final, NamedTuple, final
from typing import Annotated, Any, Final, NamedTuple, final
from typing_extensions import TypeGuard
import tomli
@@ -42,6 +42,10 @@ def _is_list_of_strings(obj: object) -> TypeGuard[list[str]]:
return isinstance(obj, list) and all(isinstance(item, str) for item in obj)
def _is_nested_dict(obj: object) -> TypeGuard[dict[str, dict[str, Any]]]:
return isinstance(obj, dict) and all(isinstance(k, str) and isinstance(v, dict) for k, v in obj.items())
@functools.cache
def _get_oldest_supported_python() -> str:
with PYPROJECT_PATH.open("rb") as config:
@@ -71,6 +75,8 @@ class StubtestSettings:
ignore_missing_stub: bool
platforms: list[str]
stubtest_requirements: list[str]
mypy_plugins: list[str]
mypy_plugins_config: dict[str, dict[str, Any]]
def system_requirements_for_platform(self, platform: str) -> list[str]:
assert platform in _STUBTEST_PLATFORM_MAPPING, f"Unrecognised platform {platform!r}"
@@ -93,6 +99,8 @@ def read_stubtest_settings(distribution: str) -> StubtestSettings:
ignore_missing_stub: object = data.get("ignore_missing_stub", False)
specified_platforms: object = data.get("platforms", ["linux"])
stubtest_requirements: object = data.get("stubtest_requirements", [])
mypy_plugins: object = data.get("mypy_plugins", [])
mypy_plugins_config: object = data.get("mypy_plugins_config", {})
assert type(skip) is bool
assert type(ignore_missing_stub) is bool
@@ -104,6 +112,8 @@ def read_stubtest_settings(distribution: str) -> StubtestSettings:
assert _is_list_of_strings(choco_dependencies)
assert _is_list_of_strings(extras)
assert _is_list_of_strings(stubtest_requirements)
assert _is_list_of_strings(mypy_plugins)
assert _is_nested_dict(mypy_plugins_config)
unrecognised_platforms = set(specified_platforms) - _STUBTEST_PLATFORM_MAPPING.keys()
assert not unrecognised_platforms, f"Unrecognised platforms specified for {distribution!r}: {unrecognised_platforms}"
@@ -124,6 +134,8 @@ def read_stubtest_settings(distribution: str) -> StubtestSettings:
ignore_missing_stub=ignore_missing_stub,
platforms=specified_platforms,
stubtest_requirements=stubtest_requirements,
mypy_plugins=mypy_plugins,
mypy_plugins_config=mypy_plugins_config,
)
@@ -179,6 +191,8 @@ _KNOWN_METADATA_TOOL_FIELDS: Final = {
"ignore_missing_stub",
"platforms",
"stubtest_requirements",
"mypy_plugins",
"mypy_plugins_config",
}
}
_DIST_NAME_RE: Final = re.compile(r"^[a-z0-9]([a-z0-9._-]*[a-z0-9])?$", re.IGNORECASE)
+15 -2
View File
@@ -6,7 +6,7 @@ from typing import Any, NamedTuple
import tomli
from ts_utils.metadata import metadata_path
from ts_utils.metadata import StubtestSettings, metadata_path
from ts_utils.utils import NamedTemporaryFile, TemporaryFileWrapper
@@ -50,7 +50,9 @@ def mypy_configuration_from_distribution(distribution: str) -> list[MypyDistConf
@contextmanager
def temporary_mypy_config_file(configurations: Iterable[MypyDistConf]) -> Generator[TemporaryFileWrapper[str]]:
def temporary_mypy_config_file(
configurations: Iterable[MypyDistConf], stubtest_settings: StubtestSettings | None = None
) -> Generator[TemporaryFileWrapper[str]]:
temp = NamedTemporaryFile("w+")
try:
for dist_conf in configurations:
@@ -58,6 +60,17 @@ def temporary_mypy_config_file(configurations: Iterable[MypyDistConf]) -> Genera
for k, v in dist_conf.values.items():
temp.write(f"{k} = {v}\n")
temp.write("[mypy]\n")
if stubtest_settings:
if stubtest_settings.mypy_plugins:
temp.write(f"plugins = {'.'.join(stubtest_settings.mypy_plugins)}\n")
if stubtest_settings.mypy_plugins_config:
for plugin_name, plugin_dict in stubtest_settings.mypy_plugins_config.items():
temp.write(f"[mypy.plugins.{plugin_name}]\n")
for k, v in plugin_dict.items():
temp.write(f"{k} = {v}\n")
temp.flush()
yield temp
finally: