From b7388f82e4de9034ee5148312d82b4e94eb40d6b Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Fri, 8 May 2026 10:24:17 -0700 Subject: [PATCH 1/4] Add Python 3.15 builtins updates --- stdlib/@tests/stubtest_allowlists/py315.txt | 11 -- stdlib/builtins.pyi | 167 ++++++++++++++++++-- stdlib/collections/__init__.pyi | 37 +++-- 3 files changed, 182 insertions(+), 33 deletions(-) diff --git a/stdlib/@tests/stubtest_allowlists/py315.txt b/stdlib/@tests/stubtest_allowlists/py315.txt index d76872ab2ebb..d66fae1d4def 100644 --- a/stdlib/@tests/stubtest_allowlists/py315.txt +++ b/stdlib/@tests/stubtest_allowlists/py315.txt @@ -118,18 +118,7 @@ binascii.b2a_base32 binascii.b2a_base64 binascii.b2a_base85 binascii.unhexlify -builtins.ImportCycleError -builtins.__lazy_import__ -builtins.bin -builtins.bytearray.replace -builtins.bytearray.take_bytes -builtins.bytes.replace builtins.compile -builtins.frozendict -builtins.hex -builtins.oct -builtins.sentinel -builtins.slice.__class_getitem__ cProfile.label calendar.HTMLCalendar.formatmonthpage calendar.__all__ diff --git a/stdlib/builtins.pyi b/stdlib/builtins.pyi index e518369ef230..cce539d837e0 100644 --- a/stdlib/builtins.pyi +++ b/stdlib/builtins.pyi @@ -603,9 +603,25 @@ class str(Sequence[str]): def zfill(self: LiteralString, width: SupportsIndex, /) -> LiteralString: ... @overload def zfill(self, width: SupportsIndex, /) -> str: ... # type: ignore[misc] - @staticmethod - @overload - def maketrans(x: dict[int, _T] | dict[str, _T] | dict[str | int, _T], /) -> dict[int, _T]: ... + if sys.version_info >= (3, 15): + @staticmethod + @overload + def maketrans( + x: ( + dict[int, _T] + | dict[str, _T] + | dict[str | int, _T] + | frozendict[int, _T] + | frozendict[str, _T] + | frozendict[str | int, _T] + ), + /, + ) -> dict[int, _T]: ... + else: + @staticmethod + @overload + def maketrans(x: dict[int, _T] | dict[str, _T] | dict[str | int, _T], /) -> dict[int, _T]: ... + @staticmethod @overload def maketrans(x: str, y: str, /) -> dict[int, int]: ... @@ -691,7 +707,11 @@ class bytes(Sequence[int]): def lower(self) -> bytes: ... def lstrip(self, bytes: ReadableBuffer | None = None, /) -> bytes: ... def partition(self, sep: ReadableBuffer, /) -> tuple[bytes, bytes, bytes]: ... - def replace(self, old: ReadableBuffer, new: ReadableBuffer, count: SupportsIndex = -1, /) -> bytes: ... + if sys.version_info >= (3, 15): + def replace(self, old: ReadableBuffer, new: ReadableBuffer, /, count: SupportsIndex = -1) -> bytes: ... + else: + def replace(self, old: ReadableBuffer, new: ReadableBuffer, count: SupportsIndex = -1, /) -> bytes: ... + def removeprefix(self, prefix: ReadableBuffer, /) -> bytes: ... def removesuffix(self, suffix: ReadableBuffer, /) -> bytes: ... def rfind( @@ -803,7 +823,11 @@ class bytearray(MutableSequence[int]): def remove(self, value: int, /) -> None: ... def removeprefix(self, prefix: ReadableBuffer, /) -> bytearray: ... def removesuffix(self, suffix: ReadableBuffer, /) -> bytearray: ... - def replace(self, old: ReadableBuffer, new: ReadableBuffer, count: SupportsIndex = -1, /) -> bytearray: ... + if sys.version_info >= (3, 15): + def replace(self, old: ReadableBuffer, new: ReadableBuffer, /, count: SupportsIndex = -1) -> bytearray: ... + else: + def replace(self, old: ReadableBuffer, new: ReadableBuffer, count: SupportsIndex = -1, /) -> bytearray: ... + def rfind( self, sub: ReadableBuffer | SupportsIndex, start: SupportsIndex | None = None, end: SupportsIndex | None = None, / ) -> int: ... @@ -827,6 +851,9 @@ class bytearray(MutableSequence[int]): def swapcase(self) -> bytearray: ... def title(self) -> bytearray: ... def translate(self, table: ReadableBuffer | None, /, delete: bytes = b"") -> bytearray: ... + if sys.version_info >= (3, 15): + def take_bytes(self, n: int | None = None, /) -> bytes: ... + def upper(self) -> bytearray: ... def zfill(self, width: SupportsIndex, /) -> bytearray: ... if sys.version_info >= (3, 14): @@ -1022,6 +1049,8 @@ class slice(Generic[_StartT_co, _StopT_co, _StepT_co]): __hash__: ClassVar[None] # type: ignore[assignment] def indices(self, len: SupportsIndex, /) -> tuple[int, int, int]: ... + if sys.version_info >= (3, 15): + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... @disjoint_base class tuple(Sequence[_T_co]): @@ -1223,14 +1252,69 @@ class dict(MutableMapping[_KT, _VT]): def __reversed__(self) -> Iterator[_KT]: ... __hash__: ClassVar[None] # type: ignore[assignment] def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... - def __or__(self, value: dict[_T1, _T2], /) -> dict[_KT | _T1, _VT | _T2]: ... - def __ror__(self, value: dict[_T1, _T2], /) -> dict[_KT | _T1, _VT | _T2]: ... + if sys.version_info >= (3, 15): + def __or__(self, value: dict[_T1, _T2] | frozendict[_T1, _T2], /) -> dict[_KT | _T1, _VT | _T2]: ... + @overload + def __ror__(self, value: dict[_T1, _T2], /) -> dict[_KT | _T1, _VT | _T2]: ... + @overload + def __ror__(self, value: frozendict[_T1, _T2], /) -> frozendict[_KT | _T1, _VT | _T2]: ... + else: + def __or__(self, value: dict[_T1, _T2], /) -> dict[_KT | _T1, _VT | _T2]: ... + def __ror__(self, value: dict[_T1, _T2], /) -> dict[_KT | _T1, _VT | _T2]: ... # dict.__ior__ should be kept roughly in line with MutableMapping.update() @overload # type: ignore[misc] def __ior__(self, value: SupportsKeysAndGetItem[_KT, _VT], /) -> Self: ... @overload def __ior__(self, value: Iterable[tuple[_KT, _VT]], /) -> Self: ... +if sys.version_info >= (3, 15): + @disjoint_base + class frozendict(Mapping[_KT, _VT]): + @overload + def __new__(cls, /) -> frozendict[Any, Any]: ... + @overload + def __new__(cls: type[frozendict[str, _VT]], /, **kwargs: _VT) -> frozendict[str, _VT]: ... + @overload + def __new__(cls, map: SupportsKeysAndGetItem[_KT, _VT], /) -> frozendict[_KT, _VT]: ... + @overload + def __new__( + cls: type[frozendict[str, _VT]], map: SupportsKeysAndGetItem[str, _VT], /, **kwargs: _VT + ) -> frozendict[str, _VT]: ... + @overload + def __new__(cls, iterable: Iterable[tuple[_KT, _VT]], /) -> frozendict[_KT, _VT]: ... + @overload + def __new__( + cls: type[frozendict[str, _VT]], iterable: Iterable[tuple[str, _VT]], /, **kwargs: _VT + ) -> frozendict[str, _VT]: ... + def __init__(self) -> None: ... + def copy(self) -> frozendict[_KT, _VT]: ... + @overload + @classmethod + def fromkeys(cls, iterable: Iterable[_T], value: None = None, /) -> frozendict[_T, Any | None]: ... + @overload + @classmethod + def fromkeys(cls, iterable: Iterable[_T], value: _S, /) -> frozendict[_T, _S]: ... + @overload # type: ignore[override] + def get(self, key: _KT, default: None = None, /) -> _VT | None: ... + @overload + def get(self, key: _KT, default: _VT, /) -> _VT: ... + @overload + def get(self, key: _KT, default: _T, /) -> _VT | _T: ... + def keys(self) -> dict_keys[_KT, _VT]: ... + def values(self) -> dict_values[_KT, _VT]: ... + def items(self) -> dict_items[_KT, _VT]: ... + def __len__(self) -> int: ... + def __getitem__(self, key: _KT, /) -> _VT: ... + def __reversed__(self) -> Iterator[_KT]: ... + def __iter__(self) -> Iterator[_KT]: ... + def __hash__(self) -> int: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + def __or__(self, value: dict[_T1, _T2] | frozendict[_T1, _T2], /) -> frozendict[_KT | _T1, _VT | _T2]: ... + @overload + def __ror__(self, value: dict[_T1, _T2], /) -> dict[_KT | _T1, _VT | _T2]: ... + @overload + def __ror__(self, value: frozendict[_T1, _T2], /) -> frozendict[_KT | _T1, _VT | _T2]: ... + @disjoint_base class set(MutableSet[_T]): @overload @@ -1362,7 +1446,13 @@ def abs(x: SupportsAbs[_T], /) -> _T: ... def all(iterable: Iterable[object], /) -> bool: ... def any(iterable: Iterable[object], /) -> bool: ... def ascii(obj: object, /) -> str: ... -def bin(number: SupportsIndex, /) -> str: ... + +if sys.version_info >= (3, 15): + def bin(integer: SupportsIndex, /) -> str: ... + +else: + def bin(number: SupportsIndex, /) -> str: ... + def breakpoint(*args: Any, **kws: Any) -> None: ... def callable(obj: object, /) -> TypeIs[Callable[..., object]]: ... def chr(i: SupportsIndex, /) -> str: ... @@ -1438,7 +1528,15 @@ def divmod(x: _T_contra, y: SupportsRDivMod[_T_contra, _T_co], /) -> _T_co: ... # The `globals` argument to `eval` has to be `dict[str, Any]` rather than `dict[str, object]` due to invariance. # (The `globals` argument has to be a "real dict", rather than any old mapping, unlike the `locals` argument.) -if sys.version_info >= (3, 13): +if sys.version_info >= (3, 15): + def eval( + source: str | ReadableBuffer | CodeType, + /, + globals: dict[str, Any] | frozendict[str, Any] | None = None, + locals: Mapping[str, object] | None = None, + ) -> Any: ... + +elif sys.version_info >= (3, 13): def eval( source: str | ReadableBuffer | CodeType, /, @@ -1455,7 +1553,17 @@ else: ) -> Any: ... # Comment above regarding `eval` applies to `exec` as well -if sys.version_info >= (3, 13): +if sys.version_info >= (3, 15): + def exec( + source: str | ReadableBuffer | CodeType, + /, + globals: dict[str, Any] | frozendict[str, Any] | None = None, + locals: Mapping[str, object] | None = None, + *, + closure: tuple[CellType, ...] | None = None, + ) -> None: ... + +elif sys.version_info >= (3, 13): def exec( source: str | ReadableBuffer | CodeType, /, @@ -1521,7 +1629,12 @@ def hash(obj: object, /) -> int: ... help: _sitebuiltins._Helper -def hex(number: SupportsIndex, /) -> str: ... +if sys.version_info >= (3, 15): + def hex(integer: SupportsIndex, /) -> str: ... + +else: + def hex(number: SupportsIndex, /) -> str: ... + def id(obj: object, /) -> int: ... def input(prompt: object = "", /) -> str: ... @type_check_only @@ -1685,7 +1798,12 @@ def min(iterable: Iterable[_T1], /, *, key: Callable[[_T1], SupportsRichComparis def next(i: SupportsNext[_T], /) -> _T: ... @overload def next(i: SupportsNext[_T], default: _VT, /) -> _T | _VT: ... -def oct(number: SupportsIndex, /) -> str: ... + +if sys.version_info >= (3, 15): + def oct(integer: SupportsIndex, /) -> str: ... + +else: + def oct(number: SupportsIndex, /) -> str: ... _Opener: TypeAlias = Callable[[str, int], int] @@ -1885,6 +2003,18 @@ def round(number: _SupportsRound2[_T], ndigits: SupportsIndex) -> _T: ... # See https://github.com/python/typeshed/pull/6292#discussion_r748875189 # for why arg 3 of `setattr` should be annotated with `Any` and not `object` def setattr(obj: object, name: str, value: Any, /) -> None: ... + +if sys.version_info >= (3, 15): + @final + class sentinel: + __name__: str + __module__: str + def __new__(cls, name: str, /) -> Self: ... + def __copy__(self, /) -> Self: ... + def __deepcopy__(self, memo: Any, /) -> Self: ... + def __or__(self, other: Any, /) -> types.UnionType: ... + def __ror__(self, other: Any, /) -> types.UnionType: ... + @overload def sorted( iterable: Iterable[SupportsRichComparisonT], /, *, key: None = None, reverse: bool = False @@ -1970,6 +2100,16 @@ def __import__( fromlist: Sequence[str] | None = (), level: int = 0, ) -> types.ModuleType: ... + +if sys.version_info >= (3, 15): + def __lazy_import__( + name: str, + globals: Mapping[str, object] | None = None, + locals: Mapping[str, object] | None = None, + fromlist: Sequence[str] | None = (), + level: int = 0, + ) -> Any: ... + def __build_class__(func: Callable[[], CellType | Any], name: str, /, *bases: Any, metaclass: Any = ..., **kwds: Any) -> Any: ... # Backwards compatibility hack for folks who relied on the ellipsis type @@ -2048,6 +2188,9 @@ class ImportError(Exception): if sys.version_info >= (3, 12): name_from: str | None # undocumented +if sys.version_info >= (3, 15): + class ImportCycleError(ImportError): ... + class LookupError(Exception): ... class MemoryError(Exception): ... diff --git a/stdlib/collections/__init__.pyi b/stdlib/collections/__init__.pyi index 72007a15093e..d691e3addfae 100644 --- a/stdlib/collections/__init__.pyi +++ b/stdlib/collections/__init__.pyi @@ -17,6 +17,9 @@ from types import GenericAlias from typing import Any, ClassVar, Generic, NoReturn, SupportsIndex, TypeVar, final, overload, type_check_only from typing_extensions import Self, disjoint_base +if sys.version_info >= (3, 15): + from builtins import frozendict as frozendict + __all__ = ["ChainMap", "Counter", "OrderedDict", "UserDict", "UserList", "UserString", "defaultdict", "deque", "namedtuple"] _S = TypeVar("_S") @@ -381,14 +384,26 @@ class OrderedDict(dict[_KT, _VT]): @overload def pop(self, key: _KT, default: _T) -> _VT | _T: ... def __eq__(self, value: object, /) -> bool: ... - @overload - def __or__(self, value: dict[_KT, _VT], /) -> Self: ... - @overload - def __or__(self, value: dict[_T1, _T2], /) -> OrderedDict[_KT | _T1, _VT | _T2]: ... - @overload - def __ror__(self, value: dict[_KT, _VT], /) -> Self: ... - @overload - def __ror__(self, value: dict[_T1, _T2], /) -> OrderedDict[_KT | _T1, _VT | _T2]: ... # type: ignore[misc] + if sys.version_info >= (3, 15): + @overload + def __or__(self, value: dict[_KT, _VT] | frozendict[_KT, _VT], /) -> Self: ... + @overload + def __or__(self, value: dict[_T1, _T2] | frozendict[_T1, _T2], /) -> OrderedDict[_KT | _T1, _VT | _T2]: ... + @overload # type: ignore[override] + def __ror__(self, value: dict[_KT, _VT] | frozendict[_KT, _VT], /) -> Self: ... # type: ignore[override,misc] + @overload + def __ror__( # type: ignore[misc] + self, value: dict[_T1, _T2] | frozendict[_T1, _T2], / + ) -> OrderedDict[_KT | _T1, _VT | _T2]: ... + else: + @overload + def __or__(self, value: dict[_KT, _VT], /) -> Self: ... + @overload + def __or__(self, value: dict[_T1, _T2], /) -> OrderedDict[_KT | _T1, _VT | _T2]: ... + @overload + def __ror__(self, value: dict[_KT, _VT], /) -> Self: ... + @overload + def __ror__(self, value: dict[_T1, _T2], /) -> OrderedDict[_KT | _T1, _VT | _T2]: ... # type: ignore[misc] @disjoint_base class defaultdict(dict[_KT, _VT]): @@ -429,11 +444,13 @@ class defaultdict(dict[_KT, _VT]): def __missing__(self, key: _KT, /) -> _VT: ... def __copy__(self) -> Self: ... def copy(self) -> Self: ... - @overload + # defaultdict rejects frozendict in its direct __or__/__ror__ methods, even though dict accepts it. + # See https://github.com/python/cpython/issues/149534. + @overload # type: ignore[override] def __or__(self, value: dict[_KT, _VT], /) -> Self: ... @overload def __or__(self, value: dict[_T1, _T2], /) -> defaultdict[_KT | _T1, _VT | _T2]: ... - @overload + @overload # type: ignore[override] def __ror__(self, value: dict[_KT, _VT], /) -> Self: ... @overload def __ror__(self, value: dict[_T1, _T2], /) -> defaultdict[_KT | _T1, _VT | _T2]: ... # type: ignore[misc] From 349a955da9a90c94de471a091f73040f79ab0f4d Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Fri, 8 May 2026 10:29:07 -0700 Subject: [PATCH 2/4] Handle Python 3.15 sentinel and frozendict fallout --- stdlib/dataclasses.pyi | 46 +++++++++++++++++++++++++----------------- stdlib/opcode.pyi | 8 +++++++- 2 files changed, 34 insertions(+), 20 deletions(-) diff --git a/stdlib/dataclasses.pyi b/stdlib/dataclasses.pyi index f24ff6a3dd50..4a6c66507371 100644 --- a/stdlib/dataclasses.pyi +++ b/stdlib/dataclasses.pyi @@ -5,9 +5,12 @@ from _typeshed import DataclassInstance from builtins import type as Type # alias to avoid name clashes with fields named "type" from collections.abc import Callable, Iterable, Mapping from types import GenericAlias -from typing import Any, Final, Generic, Literal, Protocol, TypeVar, overload, type_check_only +from typing import Any, Final, Generic, Literal, Protocol, TypeAlias, TypeVar, overload, type_check_only from typing_extensions import Never, TypeIs +if sys.version_info >= (3, 15): + from builtins import sentinel as _sentinel + _T = TypeVar("_T") _T_co = TypeVar("_T_co", covariant=True) @@ -56,7 +59,12 @@ class _DataclassFactory(Protocol): class _MISSING_TYPE(enum.Enum): MISSING = enum.auto() -MISSING: Final = _MISSING_TYPE.MISSING +if sys.version_info >= (3, 15): + _MISSING: TypeAlias = _sentinel +else: + _MISSING: TypeAlias = Literal[_MISSING_TYPE.MISSING] + +MISSING: Final[_MISSING] class KW_ONLY: ... @@ -172,8 +180,8 @@ class Field(Generic[_T]): ) name: str type: Type[_T] | str | Any - default: _T | Literal[_MISSING_TYPE.MISSING] - default_factory: _DefaultFactory[_T] | Literal[_MISSING_TYPE.MISSING] + default: _T | _MISSING + default_factory: _DefaultFactory[_T] | _MISSING repr: bool hash: bool | None init: bool @@ -183,7 +191,7 @@ class Field(Generic[_T]): if sys.version_info >= (3, 14): doc: str | None - kw_only: bool | Literal[_MISSING_TYPE.MISSING] + kw_only: bool | _MISSING if sys.version_info >= (3, 14): def __init__( @@ -221,39 +229,39 @@ if sys.version_info >= (3, 14): def field( *, default: _T, - default_factory: Literal[_MISSING_TYPE.MISSING] = ..., + default_factory: _MISSING = ..., init: bool = True, repr: bool = True, hash: bool | None = None, compare: bool = True, metadata: Mapping[Any, Any] | None = None, - kw_only: bool | Literal[_MISSING_TYPE.MISSING] = ..., + kw_only: bool | _MISSING = ..., doc: str | None = None, ) -> _T: ... @overload def field( *, - default: Literal[_MISSING_TYPE.MISSING] = ..., + default: _MISSING = ..., default_factory: Callable[[], _T], init: bool = True, repr: bool = True, hash: bool | None = None, compare: bool = True, metadata: Mapping[Any, Any] | None = None, - kw_only: bool | Literal[_MISSING_TYPE.MISSING] = ..., + kw_only: bool | _MISSING = ..., doc: str | None = None, ) -> _T: ... @overload def field( *, - default: Literal[_MISSING_TYPE.MISSING] = ..., - default_factory: Literal[_MISSING_TYPE.MISSING] = ..., + default: _MISSING = ..., + default_factory: _MISSING = ..., init: bool = True, repr: bool = True, hash: bool | None = None, compare: bool = True, metadata: Mapping[Any, Any] | None = None, - kw_only: bool | Literal[_MISSING_TYPE.MISSING] = ..., + kw_only: bool | _MISSING = ..., doc: str | None = None, ) -> Any: ... @@ -262,37 +270,37 @@ else: def field( *, default: _T, - default_factory: Literal[_MISSING_TYPE.MISSING] = ..., + default_factory: _MISSING = ..., init: bool = True, repr: bool = True, hash: bool | None = None, compare: bool = True, metadata: Mapping[Any, Any] | None = None, - kw_only: bool | Literal[_MISSING_TYPE.MISSING] = ..., + kw_only: bool | _MISSING = ..., ) -> _T: ... @overload def field( *, - default: Literal[_MISSING_TYPE.MISSING] = ..., + default: _MISSING = ..., default_factory: Callable[[], _T], init: bool = True, repr: bool = True, hash: bool | None = None, compare: bool = True, metadata: Mapping[Any, Any] | None = None, - kw_only: bool | Literal[_MISSING_TYPE.MISSING] = ..., + kw_only: bool | _MISSING = ..., ) -> _T: ... @overload def field( *, - default: Literal[_MISSING_TYPE.MISSING] = ..., - default_factory: Literal[_MISSING_TYPE.MISSING] = ..., + default: _MISSING = ..., + default_factory: _MISSING = ..., init: bool = True, repr: bool = True, hash: bool | None = None, compare: bool = True, metadata: Mapping[Any, Any] | None = None, - kw_only: bool | Literal[_MISSING_TYPE.MISSING] = ..., + kw_only: bool | _MISSING = ..., ) -> Any: ... def fields(class_or_instance: DataclassInstance | type[DataclassInstance]) -> tuple[Field[Any], ...]: ... diff --git a/stdlib/opcode.pyi b/stdlib/opcode.pyi index 67c2ef27ff36..3bc41db42bb6 100644 --- a/stdlib/opcode.pyi +++ b/stdlib/opcode.pyi @@ -1,6 +1,9 @@ import sys from typing import Final, Literal +if sys.version_info >= (3, 15): + from builtins import frozendict + __all__ = [ "cmp_op", "hasconst", @@ -40,7 +43,10 @@ if sys.version_info >= (3, 13): hasjump: Final[list[int]] opname: Final[list[str]] -opmap: Final[dict[str, int]] +if sys.version_info >= (3, 15): + opmap: Final[frozendict[str, int]] +else: + opmap: Final[dict[str, int]] HAVE_ARGUMENT: Final[int] EXTENDED_ARG: Final[int] From bd39c020db762cccefc8e84d8afabca5c47e5c2c Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Fri, 8 May 2026 10:31:53 -0700 Subject: [PATCH 3/4] Preserve older dataclasses MISSING stubtest behavior --- stdlib/dataclasses.pyi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/dataclasses.pyi b/stdlib/dataclasses.pyi index 4a6c66507371..4cb0606b8738 100644 --- a/stdlib/dataclasses.pyi +++ b/stdlib/dataclasses.pyi @@ -61,10 +61,10 @@ class _MISSING_TYPE(enum.Enum): if sys.version_info >= (3, 15): _MISSING: TypeAlias = _sentinel + MISSING: Final[_MISSING] else: _MISSING: TypeAlias = Literal[_MISSING_TYPE.MISSING] - -MISSING: Final[_MISSING] + MISSING: Final = _MISSING_TYPE.MISSING class KW_ONLY: ... From 0d91a807baf035ddaf6fdc5580148a527d7a7df9 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Fri, 8 May 2026 10:56:19 -0700 Subject: [PATCH 4/4] Address builtins PR review feedback --- stdlib/@tests/stubtest_allowlists/py315.txt | 3 +- stdlib/builtins.pyi | 140 +++++++++++++------- stdlib/collections/__init__.pyi | 2 +- stdlib/dataclasses.pyi | 46 +++---- 4 files changed, 117 insertions(+), 74 deletions(-) diff --git a/stdlib/@tests/stubtest_allowlists/py315.txt b/stdlib/@tests/stubtest_allowlists/py315.txt index d66fae1d4def..865a1c6bd0ca 100644 --- a/stdlib/@tests/stubtest_allowlists/py315.txt +++ b/stdlib/@tests/stubtest_allowlists/py315.txt @@ -118,7 +118,6 @@ binascii.b2a_base32 binascii.b2a_base64 binascii.b2a_base85 binascii.unhexlify -builtins.compile cProfile.label calendar.HTMLCalendar.formatmonthpage calendar.__all__ @@ -147,6 +146,8 @@ concurrent.interpreters._crossinterp.classonly.__wrapped__ copy.deepcopy ctypes.SetPointerType dataclasses._MISSING_TYPE +dataclasses.MISSING +dataclasses.field datetime.date.fromisoformat datetime.date.strptime datetime.datetime.fromisoformat diff --git a/stdlib/builtins.pyi b/stdlib/builtins.pyi index cce539d837e0..49d91004cd5a 100644 --- a/stdlib/builtins.pyi +++ b/stdlib/builtins.pyi @@ -1472,49 +1472,99 @@ async def anext(i: SupportsAnext[_T], default: _VT, /) -> _T | _VT: ... # compile() returns a CodeType, unless the flags argument includes PyCF_ONLY_AST (=1024), # in which case it returns ast.AST. We have overloads for flag 0 (the default) and for # explicitly passing PyCF_ONLY_AST. We fall back to Any for other values of flags. -@overload -def compile( - source: str | ReadableBuffer | _ast.Module | _ast.Expression | _ast.Interactive, - filename: str | bytes | PathLike[Any], - mode: str, - flags: Literal[0], - dont_inherit: bool = False, - optimize: int = -1, - *, - _feature_version: int = -1, -) -> CodeType: ... -@overload -def compile( - source: str | ReadableBuffer | _ast.Module | _ast.Expression | _ast.Interactive, - filename: str | bytes | PathLike[Any], - mode: str, - *, - dont_inherit: bool = False, - optimize: int = -1, - _feature_version: int = -1, -) -> CodeType: ... -@overload -def compile( - source: str | ReadableBuffer | _ast.Module | _ast.Expression | _ast.Interactive, - filename: str | bytes | PathLike[Any], - mode: str, - flags: Literal[1024], - dont_inherit: bool = False, - optimize: int = -1, - *, - _feature_version: int = -1, -) -> _ast.AST: ... -@overload -def compile( - source: str | ReadableBuffer | _ast.Module | _ast.Expression | _ast.Interactive, - filename: str | bytes | PathLike[Any], - mode: str, - flags: int, - dont_inherit: bool = False, - optimize: int = -1, - *, - _feature_version: int = -1, -) -> Any: ... +if sys.version_info >= (3, 15): + @overload + def compile( + source: str | ReadableBuffer | _ast.Module | _ast.Expression | _ast.Interactive, + filename: str | bytes | PathLike[Any], + mode: str, + flags: Literal[0], + dont_inherit: bool = False, + optimize: int = -1, + *, + module: str | None = None, + _feature_version: int = -1, + ) -> CodeType: ... + @overload + def compile( + source: str | ReadableBuffer | _ast.Module | _ast.Expression | _ast.Interactive, + filename: str | bytes | PathLike[Any], + mode: str, + *, + dont_inherit: bool = False, + optimize: int = -1, + module: str | None = None, + _feature_version: int = -1, + ) -> CodeType: ... + @overload + def compile( + source: str | ReadableBuffer | _ast.Module | _ast.Expression | _ast.Interactive, + filename: str | bytes | PathLike[Any], + mode: str, + flags: Literal[1024], + dont_inherit: bool = False, + optimize: int = -1, + *, + module: str | None = None, + _feature_version: int = -1, + ) -> _ast.AST: ... + @overload + def compile( + source: str | ReadableBuffer | _ast.Module | _ast.Expression | _ast.Interactive, + filename: str | bytes | PathLike[Any], + mode: str, + flags: int, + dont_inherit: bool = False, + optimize: int = -1, + *, + module: str | None = None, + _feature_version: int = -1, + ) -> Any: ... + +else: + @overload + def compile( + source: str | ReadableBuffer | _ast.Module | _ast.Expression | _ast.Interactive, + filename: str | bytes | PathLike[Any], + mode: str, + flags: Literal[0], + dont_inherit: bool = False, + optimize: int = -1, + *, + _feature_version: int = -1, + ) -> CodeType: ... + @overload + def compile( + source: str | ReadableBuffer | _ast.Module | _ast.Expression | _ast.Interactive, + filename: str | bytes | PathLike[Any], + mode: str, + *, + dont_inherit: bool = False, + optimize: int = -1, + _feature_version: int = -1, + ) -> CodeType: ... + @overload + def compile( + source: str | ReadableBuffer | _ast.Module | _ast.Expression | _ast.Interactive, + filename: str | bytes | PathLike[Any], + mode: str, + flags: Literal[1024], + dont_inherit: bool = False, + optimize: int = -1, + *, + _feature_version: int = -1, + ) -> _ast.AST: ... + @overload + def compile( + source: str | ReadableBuffer | _ast.Module | _ast.Expression | _ast.Interactive, + filename: str | bytes | PathLike[Any], + mode: str, + flags: int, + dont_inherit: bool = False, + optimize: int = -1, + *, + _feature_version: int = -1, + ) -> Any: ... copyright: _sitebuiltins._Printer credits: _sitebuiltins._Printer @@ -2012,8 +2062,8 @@ if sys.version_info >= (3, 15): def __new__(cls, name: str, /) -> Self: ... def __copy__(self, /) -> Self: ... def __deepcopy__(self, memo: Any, /) -> Self: ... - def __or__(self, other: Any, /) -> types.UnionType: ... - def __ror__(self, other: Any, /) -> types.UnionType: ... + def __or__(self, other: Any, /) -> Any: ... + def __ror__(self, other: Any, /) -> Any: ... @overload def sorted( diff --git a/stdlib/collections/__init__.pyi b/stdlib/collections/__init__.pyi index d691e3addfae..e835060468ca 100644 --- a/stdlib/collections/__init__.pyi +++ b/stdlib/collections/__init__.pyi @@ -18,7 +18,7 @@ from typing import Any, ClassVar, Generic, NoReturn, SupportsIndex, TypeVar, fin from typing_extensions import Self, disjoint_base if sys.version_info >= (3, 15): - from builtins import frozendict as frozendict + from builtins import frozendict __all__ = ["ChainMap", "Counter", "OrderedDict", "UserDict", "UserList", "UserString", "defaultdict", "deque", "namedtuple"] diff --git a/stdlib/dataclasses.pyi b/stdlib/dataclasses.pyi index 4cb0606b8738..f24ff6a3dd50 100644 --- a/stdlib/dataclasses.pyi +++ b/stdlib/dataclasses.pyi @@ -5,12 +5,9 @@ from _typeshed import DataclassInstance from builtins import type as Type # alias to avoid name clashes with fields named "type" from collections.abc import Callable, Iterable, Mapping from types import GenericAlias -from typing import Any, Final, Generic, Literal, Protocol, TypeAlias, TypeVar, overload, type_check_only +from typing import Any, Final, Generic, Literal, Protocol, TypeVar, overload, type_check_only from typing_extensions import Never, TypeIs -if sys.version_info >= (3, 15): - from builtins import sentinel as _sentinel - _T = TypeVar("_T") _T_co = TypeVar("_T_co", covariant=True) @@ -59,12 +56,7 @@ class _DataclassFactory(Protocol): class _MISSING_TYPE(enum.Enum): MISSING = enum.auto() -if sys.version_info >= (3, 15): - _MISSING: TypeAlias = _sentinel - MISSING: Final[_MISSING] -else: - _MISSING: TypeAlias = Literal[_MISSING_TYPE.MISSING] - MISSING: Final = _MISSING_TYPE.MISSING +MISSING: Final = _MISSING_TYPE.MISSING class KW_ONLY: ... @@ -180,8 +172,8 @@ class Field(Generic[_T]): ) name: str type: Type[_T] | str | Any - default: _T | _MISSING - default_factory: _DefaultFactory[_T] | _MISSING + default: _T | Literal[_MISSING_TYPE.MISSING] + default_factory: _DefaultFactory[_T] | Literal[_MISSING_TYPE.MISSING] repr: bool hash: bool | None init: bool @@ -191,7 +183,7 @@ class Field(Generic[_T]): if sys.version_info >= (3, 14): doc: str | None - kw_only: bool | _MISSING + kw_only: bool | Literal[_MISSING_TYPE.MISSING] if sys.version_info >= (3, 14): def __init__( @@ -229,39 +221,39 @@ if sys.version_info >= (3, 14): def field( *, default: _T, - default_factory: _MISSING = ..., + default_factory: Literal[_MISSING_TYPE.MISSING] = ..., init: bool = True, repr: bool = True, hash: bool | None = None, compare: bool = True, metadata: Mapping[Any, Any] | None = None, - kw_only: bool | _MISSING = ..., + kw_only: bool | Literal[_MISSING_TYPE.MISSING] = ..., doc: str | None = None, ) -> _T: ... @overload def field( *, - default: _MISSING = ..., + default: Literal[_MISSING_TYPE.MISSING] = ..., default_factory: Callable[[], _T], init: bool = True, repr: bool = True, hash: bool | None = None, compare: bool = True, metadata: Mapping[Any, Any] | None = None, - kw_only: bool | _MISSING = ..., + kw_only: bool | Literal[_MISSING_TYPE.MISSING] = ..., doc: str | None = None, ) -> _T: ... @overload def field( *, - default: _MISSING = ..., - default_factory: _MISSING = ..., + default: Literal[_MISSING_TYPE.MISSING] = ..., + default_factory: Literal[_MISSING_TYPE.MISSING] = ..., init: bool = True, repr: bool = True, hash: bool | None = None, compare: bool = True, metadata: Mapping[Any, Any] | None = None, - kw_only: bool | _MISSING = ..., + kw_only: bool | Literal[_MISSING_TYPE.MISSING] = ..., doc: str | None = None, ) -> Any: ... @@ -270,37 +262,37 @@ else: def field( *, default: _T, - default_factory: _MISSING = ..., + default_factory: Literal[_MISSING_TYPE.MISSING] = ..., init: bool = True, repr: bool = True, hash: bool | None = None, compare: bool = True, metadata: Mapping[Any, Any] | None = None, - kw_only: bool | _MISSING = ..., + kw_only: bool | Literal[_MISSING_TYPE.MISSING] = ..., ) -> _T: ... @overload def field( *, - default: _MISSING = ..., + default: Literal[_MISSING_TYPE.MISSING] = ..., default_factory: Callable[[], _T], init: bool = True, repr: bool = True, hash: bool | None = None, compare: bool = True, metadata: Mapping[Any, Any] | None = None, - kw_only: bool | _MISSING = ..., + kw_only: bool | Literal[_MISSING_TYPE.MISSING] = ..., ) -> _T: ... @overload def field( *, - default: _MISSING = ..., - default_factory: _MISSING = ..., + default: Literal[_MISSING_TYPE.MISSING] = ..., + default_factory: Literal[_MISSING_TYPE.MISSING] = ..., init: bool = True, repr: bool = True, hash: bool | None = None, compare: bool = True, metadata: Mapping[Any, Any] | None = None, - kw_only: bool | _MISSING = ..., + kw_only: bool | Literal[_MISSING_TYPE.MISSING] = ..., ) -> Any: ... def fields(class_or_instance: DataclassInstance | type[DataclassInstance]) -> tuple[Field[Any], ...]: ...