From 53675b6ba3e268258e1d31be5d077f090f7428f1 Mon Sep 17 00:00:00 2001 From: Randolf Scholz Date: Sat, 24 Jan 2026 15:46:41 +0100 Subject: [PATCH 1/5] use 'Any' rather than object in Container --- stdlib/typing.pyi | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/stdlib/typing.pyi b/stdlib/typing.pyi index 96a05a291604..bb80d3f48fcf 100644 --- a/stdlib/typing.pyi +++ b/stdlib/typing.pyi @@ -642,9 +642,14 @@ class AsyncGenerator(AsyncIterator[_YieldT_co], Protocol[_YieldT_co, _SendT_cont @runtime_checkable class Container(Protocol[_T_co]): - # This is generic more on vibes than anything else - @abstractmethod - def __contains__(self, x: object, /) -> bool: ... + # This Protocol is broken, as it should have used a contravariant type variable. + # But since it has been used in contravariant types, we cannot change it without + # causing breakage. + # Therefore, the key is annotated as `Any`, so that implemented can override it + # appropriately. + # A typical usage in Collection[X] types may be to set it to the upper bound of X. + @abstractmethod + def __contains__(self, x: Any, /) -> bool: ... @runtime_checkable class Collection(Iterable[_T_co], Container[_T_co], Protocol[_T_co]): From 2602adcea6e986d46c9f1a4c36eb3ab06fb26a73 Mon Sep 17 00:00:00 2001 From: Randolf Scholz Date: Sat, 24 Jan 2026 15:52:38 +0100 Subject: [PATCH 2/5] use contravariant typevar --- stdlib/typing.pyi | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/stdlib/typing.pyi b/stdlib/typing.pyi index bb80d3f48fcf..d1dea3f41132 100644 --- a/stdlib/typing.pyi +++ b/stdlib/typing.pyi @@ -403,6 +403,7 @@ _S = TypeVar("_S") _KT = TypeVar("_KT") # Key type. _VT = TypeVar("_VT") # Value type. _T_co = TypeVar("_T_co", covariant=True) # Any type covariant containers. +_T_contra = TypeVar("_T_contra", contravariant=True) # Any type contravariant containers. _KT_co = TypeVar("_KT_co", covariant=True) # Key type covariant containers. _VT_co = TypeVar("_VT_co", covariant=True) # Value type covariant containers. _TC = TypeVar("_TC", bound=type[object]) @@ -641,18 +642,12 @@ class AsyncGenerator(AsyncIterator[_YieldT_co], Protocol[_YieldT_co, _SendT_cont def aclose(self) -> Coroutine[Any, Any, None]: ... @runtime_checkable -class Container(Protocol[_T_co]): - # This Protocol is broken, as it should have used a contravariant type variable. - # But since it has been used in contravariant types, we cannot change it without - # causing breakage. - # Therefore, the key is annotated as `Any`, so that implemented can override it - # appropriately. - # A typical usage in Collection[X] types may be to set it to the upper bound of X. +class Container(Protocol[_T_contra]): @abstractmethod - def __contains__(self, x: Any, /) -> bool: ... + def __contains__(self, x: _T_contra, /) -> bool: ... @runtime_checkable -class Collection(Iterable[_T_co], Container[_T_co], Protocol[_T_co]): +class Collection(Iterable[_T_co], Container[Any], Protocol[_T_co]): # Implement Sized (but don't have it as a base class). @abstractmethod def __len__(self) -> int: ... From 09a0f41ea240944b6ff69531352c492a0e706a2c Mon Sep 17 00:00:00 2001 From: Randolf Scholz Date: Sat, 24 Jan 2026 16:14:58 +0100 Subject: [PATCH 3/5] add comment for Container[Any] usage in Collection --- stdlib/typing.pyi | 1 + 1 file changed, 1 insertion(+) diff --git a/stdlib/typing.pyi b/stdlib/typing.pyi index d1dea3f41132..d6c365862831 100644 --- a/stdlib/typing.pyi +++ b/stdlib/typing.pyi @@ -648,6 +648,7 @@ class Container(Protocol[_T_contra]): @runtime_checkable class Collection(Iterable[_T_co], Container[Any], Protocol[_T_co]): + # Note: need to use Container[Any] instead of Container[_T_co] to ensure covariance. # Implement Sized (but don't have it as a base class). @abstractmethod def __len__(self) -> int: ... From 7201649f2fcdbd1ce3bb073ee72896f59f9b2743 Mon Sep 17 00:00:00 2001 From: Randolf Scholz Date: Sat, 24 Jan 2026 16:22:13 +0100 Subject: [PATCH 4/5] make _T_contra default to Any --- stdlib/typing.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/typing.pyi b/stdlib/typing.pyi index d6c365862831..987ffa85c7d0 100644 --- a/stdlib/typing.pyi +++ b/stdlib/typing.pyi @@ -403,7 +403,7 @@ _S = TypeVar("_S") _KT = TypeVar("_KT") # Key type. _VT = TypeVar("_VT") # Value type. _T_co = TypeVar("_T_co", covariant=True) # Any type covariant containers. -_T_contra = TypeVar("_T_contra", contravariant=True) # Any type contravariant containers. +_T_contra = TypeVar("_T_contra", contravariant=True, default=Any) # Any type contravariant containers. _KT_co = TypeVar("_KT_co", covariant=True) # Key type covariant containers. _VT_co = TypeVar("_VT_co", covariant=True) # Value type covariant containers. _TC = TypeVar("_TC", bound=type[object]) From 45ebd8a496bd33b238fbb636d5f7be0c85d68364 Mon Sep 17 00:00:00 2001 From: Randolf Scholz Date: Sat, 24 Jan 2026 17:01:53 +0100 Subject: [PATCH 5/5] renamed type var and restored crucial comment --- stdlib/typing.pyi | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/stdlib/typing.pyi b/stdlib/typing.pyi index 987ffa85c7d0..b1ae6bbdae37 100644 --- a/stdlib/typing.pyi +++ b/stdlib/typing.pyi @@ -403,7 +403,6 @@ _S = TypeVar("_S") _KT = TypeVar("_KT") # Key type. _VT = TypeVar("_VT") # Value type. _T_co = TypeVar("_T_co", covariant=True) # Any type covariant containers. -_T_contra = TypeVar("_T_contra", contravariant=True, default=Any) # Any type contravariant containers. _KT_co = TypeVar("_KT_co", covariant=True) # Key type covariant containers. _VT_co = TypeVar("_VT_co", covariant=True) # Value type covariant containers. _TC = TypeVar("_TC", bound=type[object]) @@ -641,10 +640,13 @@ class AsyncGenerator(AsyncIterator[_YieldT_co], Protocol[_YieldT_co, _SendT_cont ) -> Coroutine[Any, Any, _YieldT_co]: ... def aclose(self) -> Coroutine[Any, Any, None]: ... +_ContainerT_contra = TypeVar("_ContainerT_contra", contravariant=True, default=Any) + @runtime_checkable -class Container(Protocol[_T_contra]): +class Container(Protocol[_ContainerT_contra]): + # This is generic more on vibes than anything else @abstractmethod - def __contains__(self, x: _T_contra, /) -> bool: ... + def __contains__(self, x: _ContainerT_contra, /) -> bool: ... @runtime_checkable class Collection(Iterable[_T_co], Container[Any], Protocol[_T_co]):