From 49ad99ee66f1d4d51ddd89051404cf6e7b2ecc13 Mon Sep 17 00:00:00 2001 From: karthickkrishnasaisha Date: Wed, 4 Feb 2026 12:54:46 -0800 Subject: [PATCH 1/2] Clarify Container[T] __contains__ parameter rules --- mypy/messages.py | 15 +++++++++++++++ test-data/unit/check-protocols.test | 23 +++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/mypy/messages.py b/mypy/messages.py index c5756a463894..d5cbfc93047f 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -2295,6 +2295,21 @@ def report_protocol_problems( parent_error=parent_error, skip_self=class_obj or is_module, ) + # Clarify common confusion around Container[T] and __contains__. + if ( + name == "__contains__" + and supertype.type.fullname + in {"typing.Container", "collections.abc.Container"} + ): + self.note( + 'Note: "Container[T]" does not restrict the argument type of ' + '"__contains__" (it must accept "object"). The type parameter "T" ' + "is used for membership checks (e.g. flagging non-overlapping " + "checks under --strict).", + context, + offset=OFFSET, + parent_error=parent_error, + ) self.print_more(conflict_types, context, OFFSET, MAX_ITEMS, code=parent_error.code) # Report flag conflicts (i.e. settable vs read-only etc.) diff --git a/test-data/unit/check-protocols.test b/test-data/unit/check-protocols.test index c5136415edd3..442e2003598e 100644 --- a/test-data/unit/check-protocols.test +++ b/test-data/unit/check-protocols.test @@ -4693,3 +4693,26 @@ class Impl: pass x: Proto = Impl() + +[case testContainerContainsAcceptsStr] +from collections.abc import Container + +class StringContainer: + def __contains__(self, item: str) -> bool: + return True + +c: Container[str] = StringContainer() # E: Incompatible types in assignment (expression has type "StringContainer", variable has type "Container[str]") \ + # N: Following member(s) of "StringContainer" have conflicts: \ + # N: Expected: \ + # N: def __contains__(self, object, /) -> bool \ + # N: Got: \ + # N: def __contains__(self, str, /) -> bool \ + # N: Note: "Container[T]" does not restrict the argument type of "__contains__" (it must accept "object"). The type parameter "T" is used for membership checks (e.g. flagging non-overlapping checks under --strict). +[file collections/__init__.pyi] +[file collections/abc.pyi] +from typing import Protocol, TypeVar + +T_co = TypeVar("T_co", covariant=True) + +class Container(Protocol[T_co]): + def __contains__(self, x: object) -> bool: ... From e051f99d185281261e2710c6fbce3e6b25c9a0ae Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 5 Feb 2026 03:10:33 +0000 Subject: [PATCH 2/2] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypy/messages.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/mypy/messages.py b/mypy/messages.py index d5cbfc93047f..96ff55fb4a1e 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -2296,11 +2296,10 @@ def report_protocol_problems( skip_self=class_obj or is_module, ) # Clarify common confusion around Container[T] and __contains__. - if ( - name == "__contains__" - and supertype.type.fullname - in {"typing.Container", "collections.abc.Container"} - ): + if name == "__contains__" and supertype.type.fullname in { + "typing.Container", + "collections.abc.Container", + }: self.note( 'Note: "Container[T]" does not restrict the argument type of ' '"__contains__" (it must accept "object"). The type parameter "T" '