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 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: ... 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: ... 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 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",)