Skip to content

Commit 5f44d73

Browse files
Merge branch 'main' into dict_signatures
2 parents fe38489 + a7f0a82 commit 5f44d73

778 files changed

Lines changed: 3796 additions & 5800 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/daily.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ jobs:
3636
matrix:
3737
os: ["ubuntu-latest", "windows-latest", "macos-latest"]
3838
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
39+
exclude:
40+
# https://github.com/python/typeshed/issues/15694
41+
- os: "windows-latest"
42+
python-version: "3.10"
3943
fail-fast: false
4044

4145
steps:

.github/workflows/stubtest_stdlib.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ jobs:
3232
matrix:
3333
os: ["ubuntu-latest", "windows-latest", "macos-latest"]
3434
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
35+
exclude:
36+
# https://github.com/python/typeshed/issues/15694
37+
- os: "windows-latest"
38+
python-version: "3.10"
3539
fail-fast: false
3640

3741
steps:

lib/ts_utils/metadata.py

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@
1313
from collections.abc import Mapping
1414
from dataclasses import dataclass
1515
from pathlib import Path
16-
from typing import Annotated, Any, Final, NamedTuple, final
17-
from typing_extensions import TypeGuard
16+
from typing import Annotated, Any, Final, NamedTuple, TypeGuard, cast, final
1817

1918
if sys.version_info >= (3, 11):
2019
import tomllib
@@ -239,27 +238,32 @@ def read_metadata(distribution: str) -> StubMetadata:
239238
"""
240239
try:
241240
with metadata_path(distribution).open("rb") as f:
242-
data = tomlkit.load(f)
241+
# This cast is necessary for pyright to understand that the
242+
# variable is a dict with object values. Just using
243+
# `data: dict[str, object] = tomlkit.load(f)` doesn't work because
244+
# pyright still infers TOMLDocument which derives from
245+
# dict[Unknown, Unknown].
246+
data = cast(dict[str, object], tomlkit.load(f))
243247
except FileNotFoundError:
244248
raise NoSuchStubError(f"Typeshed has no stubs for {distribution!r}!") from None
245249

246250
unknown_metadata_fields = data.keys() - _KNOWN_METADATA_FIELDS
247251
assert not unknown_metadata_fields, f"Unexpected keys in METADATA.toml for {distribution!r}: {unknown_metadata_fields}"
248252

249253
assert "version" in data, f"Missing 'version' field in METADATA.toml for {distribution!r}"
250-
version: object = data.get("version") # pyright: ignore[reportUnknownMemberType]
254+
version = data.get("version")
251255
assert isinstance(version, str) and len(version) > 0, f"Invalid 'version' field in METADATA.toml for {distribution!r}"
252256
# Check that the version spec parses
253257
if version[0].isdigit():
254258
version = f"=={version}"
255259
version_spec = Specifier(version)
256260
assert version_spec.operator in {"==", "~="}, f"Invalid 'version' field in METADATA.toml for {distribution!r}"
257261

258-
dependencies_s: object = data.get("dependencies", []) # pyright: ignore[reportUnknownMemberType]
262+
dependencies_s = data.get("dependencies", [])
259263
assert isinstance(dependencies_s, list)
260264
dependencies = [parse_dependencies(distribution, dep) for dep in dependencies_s]
261265

262-
extra_description: object = data.get("extra-description") # pyright: ignore[reportUnknownMemberType]
266+
extra_description = data.get("extra-description")
263267
assert isinstance(extra_description, (str, type(None)))
264268

265269
if "stub-distribution" in data:
@@ -269,7 +273,7 @@ def read_metadata(distribution: str) -> StubMetadata:
269273
else:
270274
stub_distribution = f"types-{distribution}"
271275

272-
upstream_repository: object = data.get("upstream-repository") # pyright: ignore[reportUnknownMemberType]
276+
upstream_repository = data.get("upstream-repository")
273277
assert isinstance(upstream_repository, (str, type(None)))
274278
if isinstance(upstream_repository, str):
275279
parsed_url = urllib.parse.urlsplit(upstream_repository)
@@ -293,7 +297,7 @@ def read_metadata(distribution: str) -> StubMetadata:
293297
)
294298
assert num_url_path_parts == 2, bad_github_url_msg
295299

296-
obsolete_since: object = data.get("obsolete-since") # pyright: ignore[reportUnknownMemberType]
300+
obsolete_since = data.get("obsolete-since")
297301
assert isinstance(obsolete_since, (String, type(None)))
298302
if obsolete_since:
299303
comment = obsolete_since.trivia.comment
@@ -302,13 +306,13 @@ def read_metadata(distribution: str) -> StubMetadata:
302306
obsolete = ObsoleteMetadata(since_version=obsolete_since, since_date=since_date)
303307
else:
304308
obsolete = None
305-
no_longer_updated: object = data.get("no-longer-updated", False) # pyright: ignore[reportUnknownMemberType]
309+
no_longer_updated = data.get("no-longer-updated", False)
306310
assert type(no_longer_updated) is bool
307-
uploaded_to_pypi: object = data.get("upload", True) # pyright: ignore[reportUnknownMemberType]
311+
uploaded_to_pypi = data.get("upload", True)
308312
assert type(uploaded_to_pypi) is bool
309-
partial_stub: object = data.get("partial-stub", True) # pyright: ignore[reportUnknownMemberType]
313+
partial_stub = data.get("partial-stub", True)
310314
assert type(partial_stub) is bool
311-
requires_python_str: object = data.get("requires-python") # pyright: ignore[reportUnknownMemberType]
315+
requires_python_str = data.get("requires-python")
312316
oldest_supported_python = get_oldest_supported_python()
313317
oldest_supported_python_specifier = Specifier(f">={oldest_supported_python}")
314318
if requires_python_str is None:
@@ -324,11 +328,11 @@ def read_metadata(distribution: str) -> StubMetadata:
324328
assert requires_python.operator == ">=", "'requires-python' should be a minimum version specifier, use '>=3.x'"
325329

326330
empty_tools: dict[object, object] = {}
327-
tools_settings: object = data.get("tool", empty_tools) # pyright: ignore[reportUnknownMemberType]
331+
tools_settings = data.get("tool", empty_tools)
328332
assert isinstance(tools_settings, dict)
329333
assert tools_settings.keys() <= _KNOWN_METADATA_TOOL_FIELDS.keys(), f"Unrecognised tool for {distribution!r}"
330334
for tool, tk in _KNOWN_METADATA_TOOL_FIELDS.items():
331-
settings_for_tool: object = tools_settings.get(tool, {}) # pyright: ignore[reportUnknownMemberType]
335+
settings_for_tool = cast(dict[str, object], tools_settings).get(tool, {})
332336
assert isinstance(settings_for_tool, dict)
333337
for key in settings_for_tool:
334338
assert key in tk, f"Unrecognised {tool} key {key!r} for {distribution!r}"
@@ -349,23 +353,28 @@ def read_metadata(distribution: str) -> StubMetadata:
349353
)
350354

351355

352-
def update_metadata(distribution: str, **new_values: object) -> tomlkit.TOMLDocument:
356+
def update_metadata(distribution: str, **new_values: object) -> dict[str, object]:
353357
"""Update a distribution's METADATA.toml.
354358
355359
Return the updated TOML dictionary for use without having to open the file separately.
356360
"""
357361
path = metadata_path(distribution)
358362
try:
359-
with path.open("rb") as file:
360-
data = tomlkit.load(file)
363+
with path.open("rb") as f:
364+
# This cast is necessary for pyright to understand that the
365+
# variable is a dict with object values. Just using
366+
# `data: dict[str, object] = tomlkit.load(f)` doesn't work because
367+
# pyright still infers TOMLDocument which derives from
368+
# dict[Unknown, Unknown].
369+
data = cast(dict[str, object], tomlkit.load(f))
361370
except FileNotFoundError:
362371
raise NoSuchStubError(f"Typeshed has no stubs for {distribution!r}!") from None
363-
data.update(new_values) # pyright: ignore[reportUnknownMemberType] # tomlkit.TOMLDocument.update is partially typed
372+
data.update(new_values)
364373
for key in list(data.keys()):
365-
new_key = key.replace("_", "-") # pyright: ignore[reportUnknownMemberType] # tomlkit.TOMLDocument.keys is partially typed
366-
data[new_key] = data.pop(key) # pyright: ignore[reportUnknownMemberType] # tomlkit.TOMLDocument.pop is partially typed
367-
with path.open("w", encoding="UTF-8") as file:
368-
tomlkit.dump(data, file) # pyright: ignore[reportUnknownMemberType] # tomlkit.dump has partially unknown Mapping type
374+
new_key = key.replace("_", "-")
375+
data[new_key] = data.pop(key)
376+
with path.open("w", encoding="UTF-8") as f:
377+
tomlkit.dump(data, f) # pyright: ignore[reportUnknownMemberType] # tomlkit.dump has partially unknown Mapping type
369378
return data
370379

371380

lib/ts_utils/utils.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@
99
from collections.abc import Iterable, Mapping
1010
from pathlib import Path
1111
from types import MethodType
12-
from typing import TYPE_CHECKING, Any, Final, NamedTuple
13-
from typing_extensions import TypeAlias
12+
from typing import TYPE_CHECKING, Any, Final, NamedTuple, TypeAlias
1413

1514
import pathspec
1615
from packaging.requirements import Requirement
@@ -261,12 +260,12 @@ def close(self: TemporaryFileWrapper[str]) -> None:
261260

262261

263262
@functools.cache
264-
def get_gitignore_spec() -> pathspec.PathSpec:
263+
def get_gitignore_spec() -> pathspec.GitIgnoreSpec:
265264
with GITIGNORE_PATH.open(encoding="UTF-8") as f:
266265
return pathspec.GitIgnoreSpec.from_lines(f)
267266

268267

269-
def spec_matches_path(spec: pathspec.PathSpec, path: Path) -> bool:
268+
def spec_matches_path(spec: pathspec.PathSpec[Any], path: Path) -> bool:
270269
normalized_path = path.as_posix()
271270
if path.is_dir():
272271
normalized_path += "/"

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ select = [
106106
"PYI044", # `from __future__ import annotations` has no effect in stub files, since type checkers automatically treat stubs as having those semantics
107107
"PYI055", # Multiple `type[T]` usages in a union. Combine them into one, e.g., `type[{union_str}]`.
108108
"PYI058", # Use `{return_type}` as the return value for simple `{method}` methods
109-
# "PYI059", # TODO: Add when dropping Python 3.9 support
109+
"PYI059", # Checks for classes inheriting from typing.Generic[] where Generic[] is not the last base class in the bases tuple
110110
"PYI061", # Use `None` rather than `Literal[None]`
111111
"PYI062", # Duplicate literal member `{}`
112112
"PYI064", # `Final[Literal[{literal}]]` can be replaced with a bare Final

requirements-tests.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ aiohttp==3.13.5
88
grpcio-tools>=1.76.0 # For grpc_tools.protoc
99
mypy-protobuf==5.0.0
1010
packaging==26.0
11-
pathspec>=1.0.3
11+
pathspec>=1.1.1
1212
pre-commit
1313
# Required by create_baseline_stubs.py. Must match .pre-commit-config.yaml.
1414
ruff==0.15.8
@@ -17,7 +17,7 @@ termcolor>=2.3
1717
tomli==2.4.1; python_version < "3.11"
1818
tomlkit==0.14.0
1919
typing_extensions>=4.15.0rc1
20-
uv==0.11.3
20+
uv==0.11.6
2121

2222
# Utilities for typeshed infrastructure scripts.
2323
ts_utils @ file:lib

scripts/stubsabot.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@
2222
from dataclasses import dataclass, field
2323
from http import HTTPStatus
2424
from pathlib import Path
25-
from typing import Annotated, Any, ClassVar, Literal, NamedTuple, TypedDict, TypeVar
26-
from typing_extensions import Self, TypeAlias
25+
from typing import Annotated, Any, ClassVar, Literal, NamedTuple, TypeAlias, TypedDict, TypeVar
26+
from typing_extensions import Self
2727

2828
if sys.version_info >= (3, 11):
2929
import tomllib

stdlib/@tests/stubtest_allowlists/darwin-py313.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
# =============
2+
# 3.13 and 3.14
3+
# =============
4+
5+
# Starting with Python 3.13.13, these methods accept None for the "scheduler"
6+
# and "setpgroup" parameters, but would raise a TypeError with Python 3.13.12
7+
# and earlier. For compatibility reasons, we don't allow None in the stubs.
8+
os.posix_spawn
9+
os.posix_spawnp
10+
111
# =======
212
# >= 3.13
313
# =======

stdlib/@tests/stubtest_allowlists/darwin-py314.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
# =============
2+
# 3.13 and 3.14
3+
# =============
4+
5+
# Starting with Python 3.14.4, these methods accept None for the "scheduler"
6+
# and "setpgroup" parameters, but would raise a TypeError with Python 3.13.12
7+
# and earlier. For compatibility reasons, we don't allow None in the stubs.
8+
os.posix_spawn
9+
os.posix_spawnp
10+
111
# =========
212
# 3.14 only
313
# =========
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# =============
2+
# 3.13 and 3.14
3+
# =============
4+
5+
# Starting with Python 3.13.13, these methods accept None for the "scheduler"
6+
# and "setpgroup" parameters, but would raise a TypeError with Python 3.13.12
7+
# and earlier. For compatibility reasons, we don't allow None in the stubs.
8+
os.posix_spawn
9+
os.posix_spawnp

0 commit comments

Comments
 (0)