From 1bbd09d6c4b979990ad1a57f569dd4f6f5ee41d9 Mon Sep 17 00:00:00 2001 From: Jonathan Dung Date: Mon, 11 May 2026 08:52:53 +0800 Subject: [PATCH 01/25] Update type hints to use Hashable for sets --- stdlib/builtins.pyi | 131 +++++++++++++++++++++++--------------------- 1 file changed, 68 insertions(+), 63 deletions(-) diff --git a/stdlib/builtins.pyi b/stdlib/builtins.pyi index 49d91004cd5a..e3b2d0bfd88b 100644 --- a/stdlib/builtins.pyi +++ b/stdlib/builtins.pyi @@ -30,7 +30,7 @@ from _typeshed import ( SupportsRichComparisonT, SupportsWrite, ) -from collections.abc import Awaitable, Callable, Iterable, Iterator, MutableSet, Reversible, Set as AbstractSet, Sized +from collections.abc import Awaitable, Callable, Hashable, Iterable, Iterator, MutableSet, Reversible, Set as AbstractSet, Sized from io import BufferedRandom, BufferedReader, BufferedWriter, FileIO, TextIOWrapper from os import PathLike from types import CellType, CodeType, EllipsisType, GenericAlias, NotImplementedType, TracebackType @@ -75,7 +75,7 @@ _I = TypeVar("_I", default=int) _T_co = TypeVar("_T_co", covariant=True) _T_contra = TypeVar("_T_contra", contravariant=True) _R_co = TypeVar("_R_co", covariant=True) -_KT = TypeVar("_KT") +_KT = TypeVar("_KT", bound=Hashable) _VT = TypeVar("_VT") _S = TypeVar("_S") _T1 = TypeVar("_T1") @@ -83,6 +83,11 @@ _T2 = TypeVar("_T2") _T3 = TypeVar("_T3") _T4 = TypeVar("_T4") _T5 = TypeVar("_T5") +_H1 = TypeVar("_H1", bound=Hashable) +_H2 = TypeVar("_H2", bound=Hashable) +_H1_co = TypeVar("_H1_co", bound=Hashable) +_H2_co = TypeVar("_H2_co", bound=Hashable) + _SupportsNextT_co = TypeVar("_SupportsNextT_co", bound=SupportsNext[Any], covariant=True) _SupportsAnextT_co = TypeVar("_SupportsAnextT_co", bound=SupportsAnext[Any], covariant=True) _AwaitableT = TypeVar("_AwaitableT", bound=Awaitable[Any]) @@ -1226,10 +1231,10 @@ class dict(MutableMapping[_KT, _VT]): # See #3800 & https://github.com/python/typing/issues/548#issuecomment-683336963. @classmethod @overload - def fromkeys(cls, iterable: Iterable[_T], value: None = None, /) -> dict[_T, Any | None]: ... + def fromkeys(cls, iterable: Iterable[_H1], value: None = None, /) -> dict[_H1, Any | None]: ... @classmethod @overload - def fromkeys(cls, iterable: Iterable[_T], value: _S, /) -> dict[_T, _S]: ... + def fromkeys(cls, iterable: Iterable[_H1], value: _T, /) -> dict[_H1, _T]: ... # Positional-only in dict, but not in MutableMapping @overload # type: ignore[override] def get(self, key: _KT, default: None = None, /) -> _VT | None: ... @@ -1253,14 +1258,14 @@ class dict(MutableMapping[_KT, _VT]): __hash__: ClassVar[None] # type: ignore[assignment] def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... if sys.version_info >= (3, 15): - def __or__(self, value: dict[_T1, _T2] | frozendict[_T1, _T2], /) -> dict[_KT | _T1, _VT | _T2]: ... + def __or__(self, value: dict[_H1, _T] | frozendict[_H1, _T], /) -> dict[_KT | _H1, _VT | _T]: ... @overload - def __ror__(self, value: dict[_T1, _T2], /) -> dict[_KT | _T1, _VT | _T2]: ... + def __ror__(self, value: dict[_H1, _T], /) -> dict[_KT | _H1, _VT | _T]: ... @overload - def __ror__(self, value: frozendict[_T1, _T2], /) -> frozendict[_KT | _T1, _VT | _T2]: ... + def __ror__(self, value: frozendict[_H1, _T], /) -> frozendict[_KT | _H1, _VT | _T]: ... 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]: ... + def __or__(self, value: dict[_H1, _T], /) -> dict[_KT | _H1, _VT | _T]: ... + def __ror__(self, value: dict[_H1, _T], /) -> dict[_KT | _H1, _VT | _T]: ... # dict.__ior__ should be kept roughly in line with MutableMapping.update() @overload # type: ignore[misc] def __ior__(self, value: SupportsKeysAndGetItem[_KT, _VT], /) -> Self: ... @@ -1271,7 +1276,7 @@ if sys.version_info >= (3, 15): @disjoint_base class frozendict(Mapping[_KT, _VT]): @overload - def __new__(cls, /) -> frozendict[Any, Any]: ... + def __new__(cls, /) -> frozendict[Hashable, Any]: ... @overload def __new__(cls: type[frozendict[str, _VT]], /, **kwargs: _VT) -> frozendict[str, _VT]: ... @overload @@ -1290,10 +1295,10 @@ if sys.version_info >= (3, 15): def copy(self) -> frozendict[_KT, _VT]: ... @overload @classmethod - def fromkeys(cls, iterable: Iterable[_T], value: None = None, /) -> frozendict[_T, Any | None]: ... + def fromkeys(cls, iterable: Iterable[_H1], value: None = None, /) -> frozendict[_H1, Any | None]: ... @overload @classmethod - def fromkeys(cls, iterable: Iterable[_T], value: _S, /) -> frozendict[_T, _S]: ... + def fromkeys(cls, iterable: Iterable[_H1], value: _T, /) -> frozendict[_H1, _T]: ... @overload # type: ignore[override] def get(self, key: _KT, default: None = None, /) -> _VT | None: ... @overload @@ -1316,70 +1321,70 @@ if sys.version_info >= (3, 15): def __ror__(self, value: frozendict[_T1, _T2], /) -> frozendict[_KT | _T1, _VT | _T2]: ... @disjoint_base -class set(MutableSet[_T]): +class set(MutableSet[_H1]): @overload def __init__(self) -> None: ... @overload - def __init__(self, iterable: Iterable[_T], /) -> None: ... - def add(self, element: _T, /) -> None: ... - def copy(self) -> set[_T]: ... - def difference(self, *s: Iterable[object]) -> set[_T]: ... - def difference_update(self, *s: Iterable[object]) -> None: ... - def discard(self, element: object, /) -> None: ... - def intersection(self, *s: Iterable[object]) -> set[_T]: ... - def intersection_update(self, *s: Iterable[object]) -> None: ... - def isdisjoint(self, s: Iterable[object], /) -> bool: ... - def issubset(self, s: Iterable[object], /) -> bool: ... - def issuperset(self, s: Iterable[object], /) -> bool: ... - def remove(self, element: _T, /) -> None: ... - def symmetric_difference(self, s: Iterable[_S], /) -> set[_T | _S]: ... - def symmetric_difference_update(self, s: Iterable[_T], /) -> None: ... - def union(self, *s: Iterable[_S]) -> set[_T | _S]: ... - def update(self, *s: Iterable[_T]) -> None: ... + def __init__(self, iterable: Iterable[_H1], /) -> None: ... + def add(self, element: _H1, /) -> None: ... + def copy(self) -> set[_H1]: ... + def difference(self, *s: Iterable[Hashable]) -> set[_H1]: ... + def difference_update(self, *s: Iterable[Hashable]) -> None: ... + def discard(self, element: Hashable, /) -> None: ... + def intersection(self, *s: Iterable[Hashable]) -> set[_H1]: ... + def intersection_update(self, *s: Iterable[Hashable]) -> None: ... + def isdisjoint(self, s: Iterable[Hashable], /) -> bool: ... + def issubset(self, s: Iterable[Hashable], /) -> bool: ... + def issuperset(self, s: Iterable[Hashable], /) -> bool: ... + def remove(self, element: _H1, /) -> None: ... + def symmetric_difference(self, s: Iterable[_H2], /) -> set[_H1 | _H2]: ... + def symmetric_difference_update(self, s: Iterable[_H1], /) -> None: ... + def union(self, *s: Iterable[_H2]) -> set[_H1 | _H2]: ... + def update(self, *s: Iterable[_H1]) -> None: ... def __len__(self) -> int: ... - def __contains__(self, o: object, /) -> bool: ... - def __iter__(self) -> Iterator[_T]: ... - def __and__(self, value: AbstractSet[object], /) -> set[_T]: ... - def __iand__(self, value: AbstractSet[object], /) -> Self: ... - def __or__(self, value: AbstractSet[_S], /) -> set[_T | _S]: ... - def __ior__(self, value: AbstractSet[_T], /) -> Self: ... # type: ignore[override,misc] - def __sub__(self, value: AbstractSet[object], /) -> set[_T]: ... - def __isub__(self, value: AbstractSet[object], /) -> Self: ... - def __xor__(self, value: AbstractSet[_S], /) -> set[_T | _S]: ... - def __ixor__(self, value: AbstractSet[_T], /) -> Self: ... # type: ignore[override,misc] - def __le__(self, value: AbstractSet[object], /) -> bool: ... - def __lt__(self, value: AbstractSet[object], /) -> bool: ... - def __ge__(self, value: AbstractSet[object], /) -> bool: ... - def __gt__(self, value: AbstractSet[object], /) -> bool: ... + def __contains__(self, o: Hashable, /) -> bool: ... + def __iter__(self) -> Iterator[_H1]: ... + def __and__(self, value: AbstractSet[Hashable], /) -> set[_H1]: ... + def __iand__(self, value: AbstractSet[Hashable], /) -> Self: ... + def __or__(self, value: AbstractSet[_H2], /) -> set[_H1 | _H2]: ... + def __ior__(self, value: AbstractSet[_H1], /) -> Self: ... # type: ignore[override,misc] + def __sub__(self, value: AbstractSet[Hashable], /) -> set[_H1]: ... + def __isub__(self, value: AbstractSet[Hashable], /) -> Self: ... + def __xor__(self, value: AbstractSet[_H2], /) -> set[_H1 | _H2]: ... + def __ixor__(self, value: AbstractSet[_H1], /) -> Self: ... # type: ignore[override,misc] + def __le__(self, value: AbstractSet[Hashable], /) -> bool: ... + def __lt__(self, value: AbstractSet[Hashable], /) -> bool: ... + def __ge__(self, value: AbstractSet[Hashable], /) -> bool: ... + def __gt__(self, value: AbstractSet[Hashable], /) -> bool: ... def __eq__(self, value: object, /) -> bool: ... __hash__: ClassVar[None] # type: ignore[assignment] def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... @disjoint_base -class frozenset(AbstractSet[_T_co]): +class frozenset(AbstractSet[_H1_co]): @overload def __new__(cls) -> Self: ... @overload - def __new__(cls, iterable: Iterable[_T_co], /) -> Self: ... - def copy(self) -> frozenset[_T_co]: ... - def difference(self, *s: Iterable[object]) -> frozenset[_T_co]: ... - def intersection(self, *s: Iterable[object]) -> frozenset[_T_co]: ... - def isdisjoint(self, s: Iterable[object], /) -> bool: ... - def issubset(self, s: Iterable[object], /) -> bool: ... - def issuperset(self, s: Iterable[object], /) -> bool: ... - def symmetric_difference(self, s: Iterable[_S], /) -> frozenset[_T_co | _S]: ... - def union(self, *s: Iterable[_S]) -> frozenset[_T_co | _S]: ... + def __new__(cls, iterable: Iterable[_H1_co], /) -> Self: ... + def copy(self) -> frozenset[_H1_co]: ... + def difference(self, *s: Iterable[Hashable]) -> frozenset[_H1_co]: ... + def intersection(self, *s: Iterable[Hashable]) -> frozenset[_H1_co]: ... + def isdisjoint(self, s: Iterable[Hashable], /) -> bool: ... + def issubset(self, s: Iterable[Hashable], /) -> bool: ... + def issuperset(self, s: Iterable[Hashable], /) -> bool: ... + def symmetric_difference(self, s: Iterable[_H1], /) -> frozenset[_H1_co | _H1]: ... + def union(self, *s: Iterable[_H1]) -> frozenset[_H1_co | _H1]: ... def __len__(self) -> int: ... - def __contains__(self, o: object, /) -> bool: ... - def __iter__(self) -> Iterator[_T_co]: ... - def __and__(self, value: AbstractSet[object], /) -> frozenset[_T_co]: ... - def __or__(self, value: AbstractSet[_S], /) -> frozenset[_T_co | _S]: ... - def __sub__(self, value: AbstractSet[object], /) -> frozenset[_T_co]: ... - def __xor__(self, value: AbstractSet[_S], /) -> frozenset[_T_co | _S]: ... - def __le__(self, value: AbstractSet[object], /) -> bool: ... - def __lt__(self, value: AbstractSet[object], /) -> bool: ... - def __ge__(self, value: AbstractSet[object], /) -> bool: ... - def __gt__(self, value: AbstractSet[object], /) -> bool: ... + def __contains__(self, o: Hashable, /) -> bool: ... + def __iter__(self) -> Iterator[_H1_co]: ... + def __and__(self, value: AbstractSet[Hashable], /) -> frozenset[_H1_co]: ... + def __or__(self, value: AbstractSet[_H1], /) -> frozenset[_H1_co | _H1]: ... + def __sub__(self, value: AbstractSet[Hashable], /) -> frozenset[_H1_co]: ... + def __xor__(self, value: AbstractSet[_H1], /) -> frozenset[_H1_co | _H1]: ... + def __le__(self, value: AbstractSet[Hashable], /) -> bool: ... + def __lt__(self, value: AbstractSet[Hashable], /) -> bool: ... + def __ge__(self, value: AbstractSet[Hashable], /) -> bool: ... + def __gt__(self, value: AbstractSet[Hashable], /) -> bool: ... def __eq__(self, value: object, /) -> bool: ... def __hash__(self) -> int: ... def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... From 4b2f6f9915ec422fe389dc91b5585380c023825b Mon Sep 17 00:00:00 2001 From: Jonathan Dung Date: Mon, 11 May 2026 13:13:24 +0800 Subject: [PATCH 02/25] commit 2 --- stdlib/_collections_abc.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/_collections_abc.pyi b/stdlib/_collections_abc.pyi index 6fc32078532e..608048852681 100644 --- a/stdlib/_collections_abc.pyi +++ b/stdlib/_collections_abc.pyi @@ -66,7 +66,7 @@ if sys.version_info < (3, 15): if sys.version_info >= (3, 12): __all__ += ["Buffer"] -_KT_co = TypeVar("_KT_co", covariant=True) # Key type covariant containers. +_KT_co = TypeVar("_KT_co", bound=Hashable, covariant=True) # Key type covariant containers. _VT_co = TypeVar("_VT_co", covariant=True) # Value type covariant containers. @final From cb767b6bb88a1f85ac3c864a65b89fee92b8ba67 Mon Sep 17 00:00:00 2001 From: Jonathan Dung Date: Mon, 11 May 2026 13:38:55 +0800 Subject: [PATCH 03/25] commit 3 --- stdlib/collections/__init__.pyi | 114 +++++++++++++++++--------------- 1 file changed, 60 insertions(+), 54 deletions(-) diff --git a/stdlib/collections/__init__.pyi b/stdlib/collections/__init__.pyi index 699d4544bf4a..a07cbce0de46 100644 --- a/stdlib/collections/__init__.pyi +++ b/stdlib/collections/__init__.pyi @@ -22,16 +22,16 @@ if sys.version_info >= (3, 15): __all__ = ["ChainMap", "Counter", "OrderedDict", "UserDict", "UserList", "UserString", "defaultdict", "deque", "namedtuple"] -_S = TypeVar("_S") _T = TypeVar("_T") _T1 = TypeVar("_T1") _T2 = TypeVar("_T2") -_KT = TypeVar("_KT") +_KT = TypeVar("_KT", bound=Hashable) _VT = TypeVar("_VT") -_KT_co = TypeVar("_KT_co", covariant=True) +_KT2 = TypeVar("_KT2") +_KT_co = TypeVar("_KT_co", bound=Hashable, covariant=True) _VT_co = TypeVar("_VT_co", covariant=True) -# namedtuple is special-cased in the type checker; the initializer is ignored. +# namedtuple is special-cased in type checkers; the initializer is ignored. def namedtuple( typename: str, field_names: str | Iterable[str], @@ -86,18 +86,18 @@ class UserDict(MutableMapping[_KT, _VT]): # See #3800 & https://github.com/python/typing/issues/548#issuecomment-683336963. @classmethod @overload - def fromkeys(cls, iterable: Iterable[_T], value: None = None) -> UserDict[_T, Any | None]: ... + def fromkeys(cls, iterable: Iterable[_KT], value: None = None) -> UserDict[_KT, Any | None]: ... @classmethod @overload - def fromkeys(cls, iterable: Iterable[_T], value: _S) -> UserDict[_T, _S]: ... + def fromkeys(cls, iterable: Iterable[_KT], value: _T) -> UserDict[_KT, _T]: ... @overload def __or__(self, other: UserDict[_KT, _VT] | dict[_KT, _VT]) -> Self: ... @overload - def __or__(self, other: UserDict[_T1, _T2] | dict[_T1, _T2]) -> UserDict[_KT | _T1, _VT | _T2]: ... + def __or__(self, other: UserDict[_KT2, _T] | dict[_KT2, _T]) -> UserDict[_KT | _KT2, _VT | _T]: ... @overload def __ror__(self, other: UserDict[_KT, _VT] | dict[_KT, _VT]) -> Self: ... @overload - def __ror__(self, other: UserDict[_T1, _T2] | dict[_T1, _T2]) -> UserDict[_KT | _T1, _VT | _T2]: ... + def __ror__(self, other: UserDict[_KT2, _T] | dict[_KT2, _T]) -> UserDict[_KT | _KT2, _VT | _T]: ... # UserDict.__ior__ should be kept roughly in line with MutableMapping.update() @overload # type: ignore[misc] def __ior__(self, other: SupportsKeysAndGetItem[_KT, _VT]) -> Self: ... @@ -270,63 +270,69 @@ class deque(MutableSequence[_T]): def __eq__(self, value: object, /) -> bool: ... def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... -class Counter(dict[_T, int], Generic[_T]): +class Counter(dict[_KT, int], Generic[_KT]): @overload def __init__(self, iterable: None = None, /) -> None: ... @overload def __init__(self: Counter[str], iterable: None = None, /, **kwargs: int) -> None: ... @overload - def __init__(self, mapping: SupportsKeysAndGetItem[_T, int], /) -> None: ... + def __init__(self, mapping: SupportsKeysAndGetItem[_KT, int], /) -> None: ... @overload - def __init__(self, iterable: Iterable[_T], /) -> None: ... + def __init__(self, iterable: Iterable[_KT], /) -> None: ... def copy(self) -> Self: ... - def elements(self) -> Iterator[_T]: ... - def most_common(self, n: int | None = None) -> list[tuple[_T, int]]: ... + def elements(self) -> Iterator[_KT]: ... + def most_common(self, n: int | None = None) -> list[tuple[_KT, int]]: ... @classmethod def fromkeys(cls, iterable: Any, v: int | None = None) -> NoReturn: ... # type: ignore[override] @overload def subtract(self, iterable: None = None, /) -> None: ... @overload - def subtract(self, mapping: Mapping[_T, int], /) -> None: ... + def subtract(self, mapping: Mapping[_KT, int], /) -> None: ... @overload - def subtract(self, iterable: Iterable[_T], /) -> None: ... + def subtract(self, iterable: Iterable[_KT], /) -> None: ... # Unlike dict.update(), use Mapping instead of SupportsKeysAndGetItem for the first overload # (source code does an `isinstance(other, Mapping)` check) # # The second overload is also deliberately different to dict.update() - # (if it were `Iterable[_T] | Iterable[tuple[_T, int]]`, + # (if it were `Iterable[_KT] | Iterable[tuple[_KT, int]]`, # the tuples would be added as keys, breaking type safety) @overload # type: ignore[override] - def update(self, m: Mapping[_T, int], /, **kwargs: int) -> None: ... + def update(self: Counter[str], m: Mapping[str, int], /, **kwargs: int) -> None: ... @overload - def update(self, iterable: Iterable[_T], /, **kwargs: int) -> None: ... + def update(self: Counter[str], iterable: Iterable[str], /, **kwargs: int) -> None: ... @overload - def update(self, iterable: None = None, /, **kwargs: int) -> None: ... + def update(self: Counter[str], iterable: None = None, /, **kwargs: int) -> None: ... + @overload + def update(self, m: Mapping[_KT, int], /) -> None: ... + @overload + def update(self, iterable: Iterable[_KT], /) -> None: ... + @overload + def update(self, iterable: None = None, /) -> None: ... def total(self) -> int: ... - def __missing__(self, key: _T) -> int: ... - def __delitem__(self, elem: object) -> None: ... - def __eq__(self, other: object) -> bool: ... - def __ne__(self, other: object) -> bool: ... - def __le__(self, other: Counter[Any]) -> bool: ... - def __lt__(self, other: Counter[Any]) -> bool: ... - def __ge__(self, other: Counter[Any]) -> bool: ... - def __gt__(self, other: Counter[Any]) -> bool: ... - def __add__(self, other: Counter[_S]) -> Counter[_T | _S]: ... - def __sub__(self, other: Counter[_T]) -> Counter[_T]: ... - def __and__(self, other: Counter[_T]) -> Counter[_T]: ... - def __or__(self, other: Counter[_S]) -> Counter[_T | _S]: ... # type: ignore[override] + def __missing__(self, key: _KT) -> int: ... + def __delitem__(self, elem: Hashable) -> None: ... + def __eq__(self, other: Hashable) -> bool: ... + def __ne__(self, other: Hashable) -> bool: ... + def __le__(self, other: Counter[Hashable]) -> bool: ... + def __lt__(self, other: Counter[Hashable]) -> bool: ... + def __ge__(self, other: Counter[Hashable]) -> bool: ... + def __gt__(self, other: Counter[Hashable]) -> bool: ... + def __add__(self, other: Counter[_KT2]) -> Counter[_KT | _KT2]: ... + def __sub__(self, other: Counter[_KT]) -> Counter[_KT]: ... + def __and__(self, other: Counter[_KT]) -> Counter[_KT]: ... + def __or__(self, other: Counter[_KT2]) -> Counter[_KT | _KT2]: ... # type: ignore[override] if sys.version_info >= (3, 15): - def __xor__(self, other: Counter[_S]) -> Counter[_T | _S]: ... # type: ignore[override] + def __xor__(self, other: Counter[_KT2]) -> Counter[_KT | _KT2]: ... # type: ignore[override] - def __pos__(self) -> Counter[_T]: ... - def __neg__(self) -> Counter[_T]: ... + def __pos__(self) -> Counter[_KT]: ... + def __neg__(self) -> Counter[_KT]: ... # several type: ignores because __iadd__ is supposedly incompatible with __add__, etc. - def __iadd__(self, other: SupportsItems[_T, int]) -> Self: ... # type: ignore[misc] - def __isub__(self, other: SupportsItems[_T, int]) -> Self: ... - def __iand__(self, other: SupportsItems[_T, int]) -> Self: ... - def __ior__(self, other: SupportsItems[_T, int]) -> Self: ... # type: ignore[override,misc] + def __iadd__(self, other: SupportsItems[_KT, int]) -> Self: ... # type: ignore[misc] + def __isub__(self, other: SupportsItems[_KT, int]) -> Self: ... + def __iand__(self, other: SupportsItems[_KT, int]) -> Self: ... + def __ior__(self, other: SupportsItems[_KT, int]) -> Self: ... # type: ignore[override,misc] if sys.version_info >= (3, 15): - def __ixor__(self, other: Counter[_T]) -> Self: ... # type: ignore[misc] + def __ixor__(self, other: Counter[_KT]) -> Self: ... # type: ignore[misc] # The pure-Python implementations of the "views" classes # These are exposed at runtime in `collections/__init__.py` @@ -372,10 +378,10 @@ class OrderedDict(dict[_KT, _VT]): # See #3800 & https://github.com/python/typing/issues/548#issuecomment-683336963. @classmethod @overload - def fromkeys(cls, iterable: Iterable[_T], value: None = None) -> OrderedDict[_T, Any | None]: ... + def fromkeys(cls, iterable: Iterable[_KT], value: None = None) -> OrderedDict[_KT, Any | None]: ... @classmethod @overload - def fromkeys(cls, iterable: Iterable[_T], value: _S) -> OrderedDict[_T, _S]: ... + def fromkeys(cls, iterable: Iterable[_KT], value: _T) -> OrderedDict[_KT, _T]: ... # Keep OrderedDict.setdefault in line with MutableMapping.setdefault, modulo positional-only differences. @overload def setdefault(self: OrderedDict[_KT, _T | None], key: _KT, default: None = None) -> _T | None: ... @@ -393,22 +399,22 @@ class OrderedDict(dict[_KT, _VT]): @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]: ... + def __or__(self, value: dict[_KT2, _T] | frozendict[_KT2, _T], /) -> OrderedDict[_KT | _KT2, _VT | _T]: ... @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]: ... + self, value: dict[_KT2, _T] | frozendict[_KT2, _T], / + ) -> OrderedDict[_KT | _KT2, _VT | _T]: ... else: @overload def __or__(self, value: dict[_KT, _VT], /) -> Self: ... @overload - def __or__(self, value: dict[_T1, _T2], /) -> OrderedDict[_KT | _T1, _VT | _T2]: ... + def __or__(self, value: dict[_KT2, _T], /) -> OrderedDict[_KT | _KT2, _VT | _T]: ... @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] + def __ror__(self, value: dict[_KT2, _T], /) -> OrderedDict[_KT | _KT2, _VT | _T]: ... # type: ignore[misc] @disjoint_base class defaultdict(dict[_KT, _VT]): @@ -454,11 +460,11 @@ class defaultdict(dict[_KT, _VT]): @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]: ... + def __or__(self, value: dict[_KT2, _T], /) -> defaultdict[_KT | _KT2, _VT | _T]: ... @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] + def __ror__(self, value: dict[_KT2, _T], /) -> defaultdict[_KT | _KT2, _VT | _T]: ... # type: ignore[misc] class ChainMap(MutableMapping[_KT, _VT]): maps: list[MutableMapping[_KT, _VT]] @@ -498,27 +504,27 @@ class ChainMap(MutableMapping[_KT, _VT]): if sys.version_info >= (3, 13): @classmethod @overload - def fromkeys(cls, iterable: Iterable[_T], /) -> ChainMap[_T, Any | None]: ... + def fromkeys(cls, iterable: Iterable[_KT], /) -> ChainMap[_KT, Any | None]: ... else: @classmethod @overload - def fromkeys(cls, iterable: Iterable[_T]) -> ChainMap[_T, Any | None]: ... + def fromkeys(cls, iterable: Iterable[_KT]) -> ChainMap[_KT, Any | None]: ... @classmethod @overload # Special-case None: the user probably wants to add non-None values later. - def fromkeys(cls, iterable: Iterable[_T], value: None, /) -> ChainMap[_T, Any | None]: ... + def fromkeys(cls, iterable: Iterable[_KT], value: None, /) -> ChainMap[_KT, Any | None]: ... @classmethod @overload - def fromkeys(cls, iterable: Iterable[_T], value: _S, /) -> ChainMap[_T, _S]: ... + def fromkeys(cls, iterable: Iterable[_KT], value: _T, /) -> ChainMap[_KT, _T]: ... @overload def __or__(self, other: Mapping[_KT, _VT]) -> Self: ... @overload - def __or__(self, other: Mapping[_T1, _T2]) -> ChainMap[_KT | _T1, _VT | _T2]: ... + def __or__(self, other: Mapping[_KT2, _T]) -> ChainMap[_KT | _KT2, _VT | _T]: ... @overload def __ror__(self, other: Mapping[_KT, _VT]) -> Self: ... @overload - def __ror__(self, other: Mapping[_T1, _T2]) -> ChainMap[_KT | _T1, _VT | _T2]: ... + def __ror__(self, other: Mapping[_KT2, _T]) -> ChainMap[_KT | _KT2, _VT | _T]: ... # ChainMap.__ior__ should be kept roughly in line with MutableMapping.update() @overload # type: ignore[misc] def __ior__(self, other: SupportsKeysAndGetItem[_KT, _VT]) -> Self: ... From 44888404175d6cee170e0b7f1b002bef94744a6e Mon Sep 17 00:00:00 2001 From: Jonathan Dung Date: Mon, 11 May 2026 13:43:11 +0800 Subject: [PATCH 04/25] commit 4 --- stdlib/types.pyi | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/stdlib/types.pyi b/stdlib/types.pyi index d2a8335b0908..87072151e0f2 100644 --- a/stdlib/types.pyi +++ b/stdlib/types.pyi @@ -7,6 +7,7 @@ from collections.abc import ( Callable, Coroutine, Generator, + Hashable, ItemsView, Iterable, Iterator, @@ -70,8 +71,9 @@ if sys.version_info >= (3, 15): _T1 = TypeVar("_T1") _T2 = TypeVar("_T2") -_KT_co = TypeVar("_KT_co", covariant=True) +_KT_co = TypeVar("_KT_co", bound=Hashable, covariant=True) _VT_co = TypeVar("_VT_co", covariant=True) +_KT2 = TypeVar("_KT2", bound=Hashable) # Make sure this class definition stays roughly in line with `builtins.function` @final @@ -286,8 +288,8 @@ class MappingProxyType(Mapping[_KT_co, _VT_co]): # type: ignore[type-var] # py def get(self, key: _KT_co, default: _T2, /) -> _VT_co | _T2: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] # Covariant type as parameter def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... def __reversed__(self) -> Iterator[_KT_co]: ... - def __or__(self, value: Mapping[_T1, _T2], /) -> dict[_KT_co | _T1, _VT_co | _T2]: ... - def __ror__(self, value: Mapping[_T1, _T2], /) -> dict[_KT_co | _T1, _VT_co | _T2]: ... + def __or__(self, value: Mapping[_KT2, _T1], /) -> dict[_KT_co | _KT2, _VT_co | _T1]: ... + def __ror__(self, value: Mapping[_KT2, _T1], /) -> dict[_KT_co | _KT2, _VT_co | _T1]: ... if sys.version_info >= (3, 12): @disjoint_base From c4fced37442d1698a411b62b75c568ef83a71e68 Mon Sep 17 00:00:00 2001 From: Jonathan Dung Date: Mon, 11 May 2026 14:01:13 +0800 Subject: [PATCH 05/25] commit 5 --- stdlib/typing.pyi | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/stdlib/typing.pyi b/stdlib/typing.pyi index 995b30a919b3..76c55e93ddac 100644 --- a/stdlib/typing.pyi +++ b/stdlib/typing.pyi @@ -411,10 +411,10 @@ _FT = TypeVar("_FT", bound=Callable[..., Any] | type) # These type variables are used by the container types. _S = TypeVar("_S") -_KT = TypeVar("_KT") # Key type. +_KT = TypeVar("_KT", bound=Hashable) # Key type. _VT = TypeVar("_VT") # Value type. _T_co = TypeVar("_T_co", covariant=True) # Any type covariant containers. -_KT_co = TypeVar("_KT_co", covariant=True) # Key type covariant containers. +_KT_co = TypeVar("_KT_co", bound=Hashable, covariant=True) # Key type covariant containers. _VT_co = TypeVar("_VT_co", covariant=True) # Value type covariant containers. _TC = TypeVar("_TC", bound=type[object]) @@ -713,37 +713,37 @@ class MutableSequence(Sequence[_T]): def remove(self, value: _T, /) -> None: ... def __iadd__(self, values: Iterable[_T], /) -> typing_extensions.Self: ... -class AbstractSet(Collection[_T_co]): +class AbstractSet(Collection[_KT_co]): @abstractmethod def __contains__(self, x: object, /) -> bool: ... def _hash(self) -> int: ... # Mixin methods @classmethod - def _from_iterable(cls, it: Iterable[_S], /) -> AbstractSet[_S]: ... - def __le__(self, other: AbstractSet[Any], /) -> bool: ... - def __lt__(self, other: AbstractSet[Any], /) -> bool: ... - def __gt__(self, other: AbstractSet[Any], /) -> bool: ... - def __ge__(self, other: AbstractSet[Any], /) -> bool: ... - def __and__(self, other: AbstractSet[Any], /) -> AbstractSet[_T_co]: ... - def __or__(self, other: AbstractSet[_T], /) -> AbstractSet[_T_co | _T]: ... - def __sub__(self, other: AbstractSet[Any], /) -> AbstractSet[_T_co]: ... - def __xor__(self, other: AbstractSet[_T], /) -> AbstractSet[_T_co | _T]: ... + def _from_iterable(cls, it: Iterable[_KT_co], /) -> AbstractSet[_KT_co]: ... + def __le__(self, other: AbstractSet[Hashable], /) -> bool: ... + def __lt__(self, other: AbstractSet[Hashable], /) -> bool: ... + def __gt__(self, other: AbstractSet[Hashable], /) -> bool: ... + def __ge__(self, other: AbstractSet[Hashable], /) -> bool: ... + def __and__(self, other: AbstractSet[Hashable], /) -> AbstractSet[_KT_co]: ... + def __or__(self, other: AbstractSet[_KT], /) -> AbstractSet[_KT_co | _KT]: ... + def __sub__(self, other: AbstractSet[Hashable], /) -> AbstractSet[_T_co]: ... + def __xor__(self, other: AbstractSet[_KT], /) -> AbstractSet[_KT_co | _KT]: ... def __eq__(self, other: object, /) -> bool: ... - def isdisjoint(self, other: Iterable[Any], /) -> bool: ... + def isdisjoint(self, other: Iterable[Hashable], /) -> bool: ... -class MutableSet(AbstractSet[_T]): +class MutableSet(AbstractSet[_KT]): @abstractmethod - def add(self, value: _T, /) -> None: ... + def add(self, value: _KT, /) -> None: ... @abstractmethod - def discard(self, value: _T, /) -> None: ... + def discard(self, value: _KT, /) -> None: ... # Mixin methods def clear(self) -> None: ... - def pop(self) -> _T: ... - def remove(self, value: _T, /) -> None: ... - def __ior__(self, it: AbstractSet[_T], /) -> typing_extensions.Self: ... # type: ignore[override,misc] - def __iand__(self, it: AbstractSet[Any], /) -> typing_extensions.Self: ... - def __ixor__(self, it: AbstractSet[_T], /) -> typing_extensions.Self: ... # type: ignore[override,misc] - def __isub__(self, it: AbstractSet[Any], /) -> typing_extensions.Self: ... + def pop(self) -> _KT: ... + def remove(self, value: _KT, /) -> None: ... + def __ior__(self, it: AbstractSet[_KT], /) -> typing_extensions.Self: ... # type: ignore[override,misc] + def __iand__(self, it: AbstractSet[Hashable], /) -> typing_extensions.Self: ... + def __ixor__(self, it: AbstractSet[_KT], /) -> typing_extensions.Self: ... # type: ignore[override,misc] + def __isub__(self, it: AbstractSet[Hashable], /) -> typing_extensions.Self: ... class MappingView(Sized): __slots__ = ("_mapping",) From e19516756982f19a8eccf32c4fef37fb2b9c9d72 Mon Sep 17 00:00:00 2001 From: Jonathan Dung Date: Tue, 12 May 2026 17:14:04 +0800 Subject: [PATCH 06/25] commit 6 --- stdlib/collections/__init__.pyi | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/stdlib/collections/__init__.pyi b/stdlib/collections/__init__.pyi index a07cbce0de46..4b71c66d7c2f 100644 --- a/stdlib/collections/__init__.pyi +++ b/stdlib/collections/__init__.pyi @@ -3,6 +3,7 @@ from _collections_abc import dict_items, dict_keys, dict_values from _typeshed import SupportsItems, SupportsKeysAndGetItem, SupportsRichComparison, SupportsRichComparisonT from collections.abc import ( Callable, + Hashable, ItemsView, Iterable, Iterator, @@ -23,8 +24,6 @@ if sys.version_info >= (3, 15): __all__ = ["ChainMap", "Counter", "OrderedDict", "UserDict", "UserList", "UserString", "defaultdict", "deque", "namedtuple"] _T = TypeVar("_T") -_T1 = TypeVar("_T1") -_T2 = TypeVar("_T2") _KT = TypeVar("_KT", bound=Hashable) _VT = TypeVar("_VT") _KT2 = TypeVar("_KT2") From 0677026f92d827d97a125d0ee45e95d21e360701 Mon Sep 17 00:00:00 2001 From: Jonathan Dung Date: Tue, 12 May 2026 17:15:22 +0800 Subject: [PATCH 07/25] commit 7 --- stdlib/builtins.pyi | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/stdlib/builtins.pyi b/stdlib/builtins.pyi index e3b2d0bfd88b..2701b8e17e2d 100644 --- a/stdlib/builtins.pyi +++ b/stdlib/builtins.pyi @@ -85,8 +85,7 @@ _T4 = TypeVar("_T4") _T5 = TypeVar("_T5") _H1 = TypeVar("_H1", bound=Hashable) _H2 = TypeVar("_H2", bound=Hashable) -_H1_co = TypeVar("_H1_co", bound=Hashable) -_H2_co = TypeVar("_H2_co", bound=Hashable) +_H1_co = TypeVar("_H1_co", bound=Hashable, covariant=True) _SupportsNextT_co = TypeVar("_SupportsNextT_co", bound=SupportsNext[Any], covariant=True) _SupportsAnextT_co = TypeVar("_SupportsAnextT_co", bound=SupportsAnext[Any], covariant=True) From 59dc0761af440963e923400c7a573185153a03fe Mon Sep 17 00:00:00 2001 From: Jonathan Dung Date: Sun, 17 May 2026 15:48:22 +0800 Subject: [PATCH 08/25] commit 8 --- stdlib/_weakrefset.pyi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/_weakrefset.pyi b/stdlib/_weakrefset.pyi index dad1ed7a4fb5..1b610e572983 100644 --- a/stdlib/_weakrefset.pyi +++ b/stdlib/_weakrefset.pyi @@ -1,4 +1,4 @@ -from collections.abc import Iterable, Iterator, MutableSet +from collections.abc import Hashable, Iterable, Iterator, MutableSet from types import GenericAlias from typing import Any, ClassVar, TypeVar, overload from typing_extensions import Self @@ -6,7 +6,7 @@ from typing_extensions import Self __all__ = ["WeakSet"] _S = TypeVar("_S") -_T = TypeVar("_T") +_T = TypeVar("_T", bound=Hashable) class WeakSet(MutableSet[_T]): @overload From d1d1816133a6accf4cfabdb8d0e9515bd83a1bfa Mon Sep 17 00:00:00 2001 From: Jonathan Dung Date: Sun, 17 May 2026 15:49:00 +0800 Subject: [PATCH 09/25] commit 9 --- stdlib/weakref.pyi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/weakref.pyi b/stdlib/weakref.pyi index 53310c91b83a..0daf11cba422 100644 --- a/stdlib/weakref.pyi +++ b/stdlib/weakref.pyi @@ -1,7 +1,7 @@ from _typeshed import SupportsKeysAndGetItem from _weakref import getweakrefcount as getweakrefcount, getweakrefs as getweakrefs, proxy as proxy from _weakrefset import WeakSet as WeakSet -from collections.abc import Callable, Iterable, Iterator, Mapping, MutableMapping +from collections.abc import Callable, Hashable, Iterable, Iterator, Mapping, MutableMapping from types import GenericAlias from typing import Any, ClassVar, Generic, ParamSpec, TypeVar, final, overload from typing_extensions import Self, disjoint_base @@ -25,7 +25,7 @@ __all__ = [ _T = TypeVar("_T") _T1 = TypeVar("_T1") _T2 = TypeVar("_T2") -_KT = TypeVar("_KT") +_KT = TypeVar("_KT", bound=Hashable) _VT = TypeVar("_VT") _CallableT = TypeVar("_CallableT", bound=Callable[..., Any]) _P = ParamSpec("_P") From 672c349bb61e41c465f50d79d56cc5ed92ab449d Mon Sep 17 00:00:00 2001 From: Jonathan Dung Date: Sun, 17 May 2026 16:30:40 +0800 Subject: [PATCH 10/25] commit 10 --- stdlib/typing.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/typing.pyi b/stdlib/typing.pyi index 76c55e93ddac..9d422ceb9a1f 100644 --- a/stdlib/typing.pyi +++ b/stdlib/typing.pyi @@ -726,7 +726,7 @@ class AbstractSet(Collection[_KT_co]): def __ge__(self, other: AbstractSet[Hashable], /) -> bool: ... def __and__(self, other: AbstractSet[Hashable], /) -> AbstractSet[_KT_co]: ... def __or__(self, other: AbstractSet[_KT], /) -> AbstractSet[_KT_co | _KT]: ... - def __sub__(self, other: AbstractSet[Hashable], /) -> AbstractSet[_T_co]: ... + def __sub__(self, other: AbstractSet[Hashable], /) -> AbstractSet[_KT_co]: ... def __xor__(self, other: AbstractSet[_KT], /) -> AbstractSet[_KT_co | _KT]: ... def __eq__(self, other: object, /) -> bool: ... def isdisjoint(self, other: Iterable[Hashable], /) -> bool: ... From 0bcfb0c241895d320261471012132cd870c19431 Mon Sep 17 00:00:00 2001 From: Jonathan Dung Date: Sun, 17 May 2026 16:33:38 +0800 Subject: [PATCH 11/25] commit 11 --- stdlib/collections/__init__.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/collections/__init__.pyi b/stdlib/collections/__init__.pyi index 4b71c66d7c2f..0c20bd2ea8c0 100644 --- a/stdlib/collections/__init__.pyi +++ b/stdlib/collections/__init__.pyi @@ -375,7 +375,7 @@ class OrderedDict(dict[_KT, _VT]): # The signature of OrderedDict.fromkeys should be kept in line with `dict.fromkeys`, modulo positional-only differences. # Like dict.fromkeys, its true signature is not expressible in the current type system. # See #3800 & https://github.com/python/typing/issues/548#issuecomment-683336963. - @classmethod + @classmethod # type: ignore[override] @overload def fromkeys(cls, iterable: Iterable[_KT], value: None = None) -> OrderedDict[_KT, Any | None]: ... @classmethod From 61884f05926633621d6b7933b4a932fd0f29fc49 Mon Sep 17 00:00:00 2001 From: Jonathan Dung Date: Sun, 17 May 2026 16:36:47 +0800 Subject: [PATCH 12/25] commit 12 --- stdlib/_typeshed/__init__.pyi | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/stdlib/_typeshed/__init__.pyi b/stdlib/_typeshed/__init__.pyi index c96f212b42e9..aa5fc8bc86e9 100644 --- a/stdlib/_typeshed/__init__.pyi +++ b/stdlib/_typeshed/__init__.pyi @@ -261,9 +261,13 @@ OpenBinaryMode: TypeAlias = OpenBinaryModeUpdating | OpenBinaryModeReading | Ope class HasFileno(Protocol): def fileno(self) -> int: ... +class HasFilenoAndHash(HasFileno, Protocol): + def __hash__(self) -> int: ... + FileDescriptor: TypeAlias = int # stable FileDescriptorLike: TypeAlias = int | HasFileno # stable FileDescriptorOrPath: TypeAlias = int | StrOrBytesPath +HashableFDLike: TypeAlias = int | HasFilenoAndHash # stable class SupportsRead(Protocol[_T_co]): From e21ad776f643591e2df9134ba71e300a5bbd3b00 Mon Sep 17 00:00:00 2001 From: Jonathan Dung Date: Sun, 17 May 2026 16:49:42 +0800 Subject: [PATCH 13/25] commit 13 --- stdlib/selectors.pyi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/selectors.pyi b/stdlib/selectors.pyi index 4a3478a67552..62db2eb0a52a 100644 --- a/stdlib/selectors.pyi +++ b/stdlib/selectors.pyi @@ -25,7 +25,7 @@ class BaseSelector(metaclass=ABCMeta): def close(self) -> None: ... def get_key(self, fileobj: FileDescriptorLike) -> SelectorKey: ... @abstractmethod - def get_map(self) -> Mapping[FileDescriptorLike, SelectorKey]: ... + def get_map(self) -> Mapping[FileDescriptorLike, SelectorKey]: ... # type: ignore[type-var] def __enter__(self) -> Self: ... def __exit__(self, *args: Unused) -> None: ... @@ -33,7 +33,7 @@ class _BaseSelectorImpl(BaseSelector, metaclass=ABCMeta): def register(self, fileobj: FileDescriptorLike, events: int, data: Any = None) -> SelectorKey: ... def unregister(self, fileobj: FileDescriptorLike) -> SelectorKey: ... def modify(self, fileobj: FileDescriptorLike, events: int, data: Any = None) -> SelectorKey: ... - def get_map(self) -> Mapping[FileDescriptorLike, SelectorKey]: ... + def get_map(self) -> Mapping[FileDescriptorLike, SelectorKey]: ... # type: ignore[type-var] class SelectSelector(_BaseSelectorImpl): def select(self, timeout: float | None = None) -> list[tuple[SelectorKey, int]]: ... From f6d11b83e704a7b76c4078ca1c2916cc0a03642e Mon Sep 17 00:00:00 2001 From: Jonathan Dung Date: Sun, 17 May 2026 16:50:55 +0800 Subject: [PATCH 14/25] commit 14 --- stdlib/marshal.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/marshal.pyi b/stdlib/marshal.pyi index d72abe7758b7..54730bc2010c 100644 --- a/stdlib/marshal.pyi +++ b/stdlib/marshal.pyi @@ -22,7 +22,7 @@ _Marshallable: TypeAlias = ( | list[Any] | dict[Any, Any] | set[Any] - | frozenset[_Marshallable] + | frozenset[_Marshallable] # type: ignore[type-var] | types.CodeType | ReadableBuffer ) From 5b73d71ff62acb6590f86d7c27bc1b9d420aa96c Mon Sep 17 00:00:00 2001 From: Jonathan Dung Date: Sun, 17 May 2026 17:25:48 +0800 Subject: [PATCH 15/25] commit 15 --- stdlib/_weakrefset.pyi | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/stdlib/_weakrefset.pyi b/stdlib/_weakrefset.pyi index 1b610e572983..b504c4e5b9c8 100644 --- a/stdlib/_weakrefset.pyi +++ b/stdlib/_weakrefset.pyi @@ -31,12 +31,12 @@ class WeakSet(MutableSet[_T]): def __and__(self, other: Iterable[Any]) -> Self: ... def intersection_update(self, other: Iterable[Any]) -> None: ... def __iand__(self, other: Iterable[Any]) -> Self: ... - def issubset(self, other: Iterable[_T]) -> bool: ... - def __le__(self, other: Iterable[_T]) -> bool: ... - def __lt__(self, other: Iterable[_T]) -> bool: ... - def issuperset(self, other: Iterable[_T]) -> bool: ... - def __ge__(self, other: Iterable[_T]) -> bool: ... - def __gt__(self, other: Iterable[_T]) -> bool: ... + def issubset(self, other: Iterable[Any]) -> bool: ... + def __le__(self, other: Iterable[Any]) -> bool: ... + def __lt__(self, other: Iterable[Any]) -> bool: ... + def issuperset(self, other: Iterable[Any]) -> bool: ... + def __ge__(self, other: Iterable[Any]) -> bool: ... + def __gt__(self, other: Iterable[Any]) -> bool: ... def __eq__(self, other: object) -> bool: ... def symmetric_difference(self, other: Iterable[_S]) -> WeakSet[_S | _T]: ... def __xor__(self, other: Iterable[_S]) -> WeakSet[_S | _T]: ... @@ -44,5 +44,5 @@ class WeakSet(MutableSet[_T]): def __ixor__(self, other: Iterable[_T]) -> Self: ... # type: ignore[override,misc] def union(self, other: Iterable[_S]) -> WeakSet[_S | _T]: ... def __or__(self, other: Iterable[_S]) -> WeakSet[_S | _T]: ... - def isdisjoint(self, other: Iterable[_T]) -> bool: ... + def isdisjoint(self, other: Iterable[Any]) -> bool: ... def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... From 6a22c20677d789941e68858f16acbe25037c0927 Mon Sep 17 00:00:00 2001 From: Jonathan Dung Date: Sun, 17 May 2026 17:26:52 +0800 Subject: [PATCH 16/25] commit 16 --- stdlib/decimal.pyi | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/stdlib/decimal.pyi b/stdlib/decimal.pyi index 42cc2ff4eafb..84de9d819fd0 100644 --- a/stdlib/decimal.pyi +++ b/stdlib/decimal.pyi @@ -189,8 +189,8 @@ class Context: Emax: int capitals: int clamp: int - traps: dict[_TrapType, bool] - flags: dict[_TrapType, bool] + traps: dict[_TrapType, bool] # type: ignore[type-var] + flags: dict[_TrapType, bool] # type: ignore[type-var] def __init__( self, prec: int | None = None, @@ -199,8 +199,8 @@ class Context: Emax: int | None = None, capitals: int | None = None, clamp: int | None = None, - flags: dict[_TrapType, bool] | Container[_TrapType] | None = None, - traps: dict[_TrapType, bool] | Container[_TrapType] | None = None, + flags: dict[_TrapType, bool] | Container[_TrapType] | None = None, # type: ignore[type-var] + traps: dict[_TrapType, bool] | Container[_TrapType] | None = None, # type: ignore[type-var] ) -> None: ... def __reduce__(self) -> tuple[type[Self], tuple[Any, ...]]: ... def clear_flags(self) -> None: ... From 29376a98a93d2ecfc747d3a4c9f01ab6ab05ee27 Mon Sep 17 00:00:00 2001 From: Jonathan Dung Date: Sun, 17 May 2026 17:27:50 +0800 Subject: [PATCH 17/25] commit 17 --- stdlib/xmlrpc/client.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/xmlrpc/client.pyi b/stdlib/xmlrpc/client.pyi index 18f8987f78fb..b3ab0e6afaef 100644 --- a/stdlib/xmlrpc/client.pyi +++ b/stdlib/xmlrpc/client.pyi @@ -111,7 +111,7 @@ class ExpatParser: # undocumented _WriteCallback: TypeAlias = Callable[[str], object] class Marshaller: - dispatch: dict[type[_Marshallable] | Literal["_arbitrary_instance"], Callable[[Marshaller, Any, _WriteCallback], None]] + dispatch: dict[type[_Marshallable] | Literal["_arbitrary_instance"], Callable[[Marshaller, Any, _WriteCallback], None]] # type: ignore[type-var] memo: dict[Any, None] data: None encoding: str | None From 852f3fa2592916e958b5bf3881e92cf773fd4b99 Mon Sep 17 00:00:00 2001 From: Jonathan Dung Date: Sun, 17 May 2026 17:31:56 +0800 Subject: [PATCH 18/25] commit 18 --- stdlib/_decimal.pyi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/_decimal.pyi b/stdlib/_decimal.pyi index 75d5be277f95..4e231e8e53e5 100644 --- a/stdlib/_decimal.pyi +++ b/stdlib/_decimal.pyi @@ -58,8 +58,8 @@ if sys.version_info >= (3, 11): Emax: int | None = None, capitals: int | None = None, clamp: int | None = None, - traps: dict[_TrapType, bool] | None = None, - flags: dict[_TrapType, bool] | None = None, + traps: dict[_TrapType, bool] | None = None, # type: ignore[type-var] + flags: dict[_TrapType, bool] | None = None, # type: ignore[type-var] ) -> _ContextManager: ... else: From d77eb3a2f363bb75ac8f68b04b7774d400c6c534 Mon Sep 17 00:00:00 2001 From: Jonathan Dung Date: Sun, 17 May 2026 17:32:46 +0800 Subject: [PATCH 19/25] commit 19 --- stdlib/inspect.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/inspect.pyi b/stdlib/inspect.pyi index 5a7b74b2d1ba..74d50d9de9dd 100644 --- a/stdlib/inspect.pyi +++ b/stdlib/inspect.pyi @@ -482,7 +482,7 @@ class BoundArguments: _ClassTreeItem: TypeAlias = list[tuple[type, ...]] | list[_ClassTreeItem] def getclasstree(classes: list[type], unique: bool = False) -> _ClassTreeItem: ... -def walktree(classes: list[type], children: Mapping[type[Any], list[type]], parent: type[Any] | None) -> _ClassTreeItem: ... +def walktree(classes: list[type], children: Mapping[type[Any], list[type]], parent: type[Any] | None) -> _ClassTreeItem: ... # type: ignore[type-var] class Arguments(NamedTuple): args: list[str] From 197295b445eec8954ea4bcb99d274e1eedfef60e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 18 May 2026 00:08:46 +0000 Subject: [PATCH 20/25] [pre-commit.ci] auto fixes from pre-commit.com hooks --- stdlib/builtins.pyi | 6 ++++++ stdlib/collections/__init__.pyi | 13 +++++++++++++ 2 files changed, 19 insertions(+) diff --git a/stdlib/builtins.pyi b/stdlib/builtins.pyi index b2b5787700f8..8a00d373fa20 100644 --- a/stdlib/builtins.pyi +++ b/stdlib/builtins.pyi @@ -1331,6 +1331,7 @@ class dict(MutableMapping[_KT, _VT]): @classmethod @overload def fromkeys(cls, iterable: Iterable[_H1], value: _T, /) -> dict[_H1, _T]: ... + # Positional-only in dict, but not in MutableMapping @overload # type: ignore[override] def get(self, key: _KT, default: None = None, /) -> _VT | None: ... @@ -1357,6 +1358,7 @@ class dict(MutableMapping[_KT, _VT]): def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... if sys.version_info >= (3, 15): def __or__(self, value: dict[_H1, _T] | frozendict[_H1, _T], /) -> dict[_KT | _H1, _VT | _T]: ... + @overload def __ror__(self, value: dict[_H1, _T], /) -> dict[_KT | _H1, _VT | _T]: ... @overload @@ -1364,6 +1366,7 @@ class dict(MutableMapping[_KT, _VT]): else: def __or__(self, value: dict[_H1, _T], /) -> dict[_KT | _H1, _VT | _T]: ... def __ror__(self, value: dict[_H1, _T], /) -> dict[_KT | _H1, _VT | _T]: ... + # dict.__ior__ should be kept roughly in line with MutableMapping.update() @overload # type: ignore[misc] def __ior__(self, value: SupportsKeysAndGetItem[_KT, _VT], /) -> Self: ... @@ -1399,6 +1402,7 @@ if sys.version_info >= (3, 15): @overload @classmethod def fromkeys(cls, iterable: Iterable[_H1], value: _T, /) -> frozendict[_H1, _T]: ... + @overload # type: ignore[override] def get(self, key: _KT, default: None = None, /) -> _VT | None: ... @overload @@ -1428,6 +1432,7 @@ class set(MutableSet[_H1]): def __init__(self) -> None: ... @overload def __init__(self, iterable: Iterable[_H1], /) -> None: ... + def add(self, element: _H1, /) -> None: ... def copy(self) -> set[_H1]: ... def difference(self, *s: Iterable[Hashable]) -> set[_H1]: ... @@ -1468,6 +1473,7 @@ class frozenset(AbstractSet[_H1_co]): def __new__(cls) -> Self: ... @overload def __new__(cls, iterable: Iterable[_H1_co], /) -> Self: ... + def copy(self) -> frozenset[_H1_co]: ... def difference(self, *s: Iterable[Hashable]) -> frozenset[_H1_co]: ... def intersection(self, *s: Iterable[Hashable]) -> frozenset[_H1_co]: ... diff --git a/stdlib/collections/__init__.pyi b/stdlib/collections/__init__.pyi index 7115236f768b..e5b5e7ce7d57 100644 --- a/stdlib/collections/__init__.pyi +++ b/stdlib/collections/__init__.pyi @@ -91,14 +91,17 @@ class UserDict(MutableMapping[_KT, _VT]): @classmethod @overload def fromkeys(cls, iterable: Iterable[_KT], value: _T) -> UserDict[_KT, _T]: ... + @overload def __or__(self, other: UserDict[_KT, _VT] | dict[_KT, _VT]) -> Self: ... @overload def __or__(self, other: UserDict[_KT2, _T] | dict[_KT2, _T]) -> UserDict[_KT | _KT2, _VT | _T]: ... + @overload def __ror__(self, other: UserDict[_KT, _VT] | dict[_KT, _VT]) -> Self: ... @overload def __ror__(self, other: UserDict[_KT2, _T] | dict[_KT2, _T]) -> UserDict[_KT | _KT2, _VT | _T]: ... + # UserDict.__ior__ should be kept roughly in line with MutableMapping.update() @overload # type: ignore[misc] def __ior__(self, other: SupportsKeysAndGetItem[_KT, _VT]) -> Self: ... @@ -290,6 +293,7 @@ class Counter(dict[_KT, int], Generic[_KT]): def __init__(self, mapping: SupportsKeysAndGetItem[_KT, int], /) -> None: ... @overload def __init__(self, iterable: Iterable[_KT], /) -> None: ... + def copy(self) -> Self: ... def elements(self) -> Iterator[_KT]: ... def most_common(self, n: int | None = None) -> list[tuple[_KT, int]]: ... @@ -302,6 +306,7 @@ class Counter(dict[_KT, int], Generic[_KT]): def subtract(self, mapping: Mapping[_KT, int], /) -> None: ... @overload def subtract(self, iterable: Iterable[_KT], /) -> None: ... + # Unlike dict.update(), use Mapping instead of SupportsKeysAndGetItem for the first overload # (source code does an `isinstance(other, Mapping)` check) # @@ -320,6 +325,7 @@ class Counter(dict[_KT, int], Generic[_KT]): def update(self, iterable: Iterable[_KT], /) -> None: ... @overload def update(self, iterable: None = None, /) -> None: ... + def total(self) -> int: ... def __missing__(self, key: _KT) -> int: ... def __delitem__(self, elem: Hashable) -> None: ... @@ -395,6 +401,7 @@ class OrderedDict(dict[_KT, _VT]): @classmethod @overload def fromkeys(cls, iterable: Iterable[_KT], value: _T) -> OrderedDict[_KT, _T]: ... + # Keep OrderedDict.setdefault in line with MutableMapping.setdefault, modulo positional-only differences. @overload def setdefault(self: OrderedDict[_KT, _T | None], key: _KT, default: None = None) -> _T | None: ... @@ -416,6 +423,7 @@ class OrderedDict(dict[_KT, _VT]): def __or__(self, value: dict[_KT, _VT] | frozendict[_KT, _VT], /) -> Self: ... @overload def __or__(self, value: dict[_KT2, _T] | frozendict[_KT2, _T], /) -> OrderedDict[_KT | _KT2, _VT | _T]: ... + @overload # type: ignore[override] def __ror__(self, value: dict[_KT, _VT] | frozendict[_KT, _VT], /) -> Self: ... # type: ignore[override,misc] @overload @@ -427,6 +435,7 @@ class OrderedDict(dict[_KT, _VT]): def __or__(self, value: dict[_KT, _VT], /) -> Self: ... @overload def __or__(self, value: dict[_KT2, _T], /) -> OrderedDict[_KT | _KT2, _VT | _T]: ... + @overload def __ror__(self, value: dict[_KT, _VT], /) -> Self: ... @overload @@ -480,6 +489,7 @@ class defaultdict(dict[_KT, _VT]): def __or__(self, value: dict[_KT, _VT], /) -> Self: ... @overload def __or__(self, value: dict[_KT2, _T], /) -> defaultdict[_KT | _KT2, _VT | _T]: ... + @overload # type: ignore[override] def __ror__(self, value: dict[_KT, _VT], /) -> Self: ... @overload @@ -541,14 +551,17 @@ class ChainMap(MutableMapping[_KT, _VT]): @classmethod @overload def fromkeys(cls, iterable: Iterable[_KT], value: _T, /) -> ChainMap[_KT, _T]: ... + @overload def __or__(self, other: Mapping[_KT, _VT]) -> Self: ... @overload def __or__(self, other: Mapping[_KT2, _T]) -> ChainMap[_KT | _KT2, _VT | _T]: ... + @overload def __ror__(self, other: Mapping[_KT, _VT]) -> Self: ... @overload def __ror__(self, other: Mapping[_KT2, _T]) -> ChainMap[_KT | _KT2, _VT | _T]: ... + # ChainMap.__ior__ should be kept roughly in line with MutableMapping.update() @overload # type: ignore[misc] def __ior__(self, other: SupportsKeysAndGetItem[_KT, _VT]) -> Self: ... From fb308795ac05dd8459987e460268d3ff0ffad1c3 Mon Sep 17 00:00:00 2001 From: Jonathan Dung Date: Mon, 18 May 2026 09:12:02 +0800 Subject: [PATCH 21/25] commit 22 --- stdlib/_collections_abc.pyi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/_collections_abc.pyi b/stdlib/_collections_abc.pyi index 608048852681..8c2fad9fd044 100644 --- a/stdlib/_collections_abc.pyi +++ b/stdlib/_collections_abc.pyi @@ -75,7 +75,7 @@ class dict_keys(KeysView[_KT_co], Generic[_KT_co, _VT_co]): # undocumented def __reversed__(self) -> Iterator[_KT_co]: ... __hash__: ClassVar[None] # type: ignore[assignment] if sys.version_info >= (3, 13): - def isdisjoint(self, other: Iterable[_KT_co], /) -> bool: ... + def isdisjoint(self, other: Iterable[Hashable], /) -> bool: ... @property def mapping(self) -> MappingProxyType[_KT_co, _VT_co]: ... @@ -92,7 +92,7 @@ class dict_items(ItemsView[_KT_co, _VT_co]): # undocumented def __reversed__(self) -> Iterator[tuple[_KT_co, _VT_co]]: ... __hash__: ClassVar[None] # type: ignore[assignment] if sys.version_info >= (3, 13): - def isdisjoint(self, other: Iterable[tuple[_KT_co, _VT_co]], /) -> bool: ... + def isdisjoint(self, other: Iterable[Hashable], /) -> bool: ... @property def mapping(self) -> MappingProxyType[_KT_co, _VT_co]: ... From 031b650f62f97dc8cf7813258c94afa5524b488c Mon Sep 17 00:00:00 2001 From: Jonathan Dung Date: Mon, 18 May 2026 09:14:50 +0800 Subject: [PATCH 22/25] commit 23 --- stdlib/multiprocessing/managers.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/multiprocessing/managers.pyi b/stdlib/multiprocessing/managers.pyi index 40639e867834..addc5772e56d 100644 --- a/stdlib/multiprocessing/managers.pyi +++ b/stdlib/multiprocessing/managers.pyi @@ -182,7 +182,7 @@ if sys.version_info >= (3, 14): def __iand__(self, value: AbstractSet[object], /) -> Self: ... def __or__(self, value: AbstractSet[_S], /) -> set[_T | _S]: ... def __ior__(self, value: AbstractSet[_T], /) -> Self: ... # type: ignore[override,misc] - def __sub__(self, value: AbstractSet[_T | None], /) -> set[_T]: ... + def __sub__(self, value: AbstractSet[_T | None], /) -> set[_T]: ... # type: ignore[override] def __isub__(self, value: AbstractSet[object], /) -> Self: ... def __xor__(self, value: AbstractSet[_S], /) -> set[_T | _S]: ... def __ixor__(self, value: AbstractSet[_T], /) -> Self: ... # type: ignore[override,misc] From 5b1e83e48f5068baf3ca4694346bff2dc9ccb082 Mon Sep 17 00:00:00 2001 From: Jonathan Dung Date: Mon, 18 May 2026 09:32:37 +0800 Subject: [PATCH 23/25] commit --- stubs/dateparser/dateparser/languages/locale.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stubs/dateparser/dateparser/languages/locale.pyi b/stubs/dateparser/dateparser/languages/locale.pyi index ad6ae9d283d2..074ec1aeb253 100644 --- a/stubs/dateparser/dateparser/languages/locale.pyi +++ b/stubs/dateparser/dateparser/languages/locale.pyi @@ -18,7 +18,7 @@ class Locale: def is_applicable(self, date_string: str, strip_timezone: bool = False, settings: Settings | None = None) -> bool: ... def count_applicability(self, text: str, strip_timezone: bool = False, settings: Settings | None = None) -> list[int]: ... @staticmethod - def clean_dictionary(dictionary: Mapping[_K, _V], threshold: int = 2) -> Mapping[_K, _V]: ... + def clean_dictionary(dictionary: Mapping[_K, _V], threshold: int = 2) -> Mapping[_K, _V]: ... # type: ignore[type-var] def translate(self, date_string: str, keep_formatting: bool = False, settings: Settings | None = None) -> str: ... def translate_search(self, search_string: str, settings: Settings | None = None) -> tuple[list[str], list[str]]: ... def get_wordchars_for_detection(self, settings: Settings) -> set[str]: ... From 9bfb3c046336a45c260b0912c569f53d1e90de32 Mon Sep 17 00:00:00 2001 From: Jonathan Dung Date: Mon, 18 May 2026 09:35:22 +0800 Subject: [PATCH 24/25] commit 25 --- stubs/gevent/gevent/selectors.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stubs/gevent/gevent/selectors.pyi b/stubs/gevent/gevent/selectors.pyi index 12614ef1e6ff..432af20dc4e2 100644 --- a/stubs/gevent/gevent/selectors.pyi +++ b/stubs/gevent/gevent/selectors.pyi @@ -19,6 +19,6 @@ class GeventSelector(BaseSelector): def unregister(self, fileobj: FileDescriptorLike) -> SelectorKey: ... def select(self, timeout: float | None = None) -> list[tuple[SelectorKey, int]]: ... def close(self) -> None: ... - def get_map(self) -> Mapping[FileDescriptorLike, SelectorKey]: ... + def get_map(self) -> Mapping[FileDescriptorLike, SelectorKey]: ... # type: ignore[type-var] DefaultSelector = GeventSelector From 034baa95734f2d5b81e72a0ea663b343828e8dad Mon Sep 17 00:00:00 2001 From: Jonathan Dung Date: Mon, 18 May 2026 09:36:56 +0800 Subject: [PATCH 25/25] commit 26 --- stubs/requests-oauthlib/requests_oauthlib/oauth2_session.pyi | 3 +++ 1 file changed, 3 insertions(+) diff --git a/stubs/requests-oauthlib/requests_oauthlib/oauth2_session.pyi b/stubs/requests-oauthlib/requests_oauthlib/oauth2_session.pyi index bdd4b2aa0e0e..79e769bb5e7d 100644 --- a/stubs/requests-oauthlib/requests_oauthlib/oauth2_session.pyi +++ b/stubs/requests-oauthlib/requests_oauthlib/oauth2_session.pyi @@ -11,14 +11,17 @@ _Token: TypeAlias = dict[str, Incomplete] # oauthlib.oauth2.Client.token @type_check_only class _AccessTokenResponseHook(Protocol): def __call__(self, response: requests.Response, /) -> requests.Response: ... + def __hash__(self) -> int: ... @type_check_only class _RefreshTokenResponseHook(Protocol): def __call__(self, response: requests.Response, /) -> requests.Response: ... + def __hash__(self) -> int: ... @type_check_only class _ProtectedRequestHook(Protocol): def __call__(self, url, headers, data, /) -> tuple[Incomplete, Incomplete, Incomplete]: ... + def __hash__(self) -> int: ... @type_check_only class _ComplianceHooks(TypedDict):