From ec3a92fca586cebf15ffdaca4267e37020fdffaa Mon Sep 17 00:00:00 2001 From: ShirGanon <45141251+ShirGanon@users.noreply.github.com> Date: Tue, 2 Jun 2026 23:24:10 +0300 Subject: [PATCH] stubtest: don't flag __class_getitem__ as missing on a generic stub A class that implements `__class_getitem__` at runtime to support subscription is correctly modelled in a stub by declaring the class as generic (PEP 695 `class C[T]` or `Generic[T]`). stubtest nevertheless reported `C.__class_getitem__ is not present in stub` in that case. Skip the `__class_getitem__` runtime member when the stub class is generic (`TypeInfo.is_generic()`) and does not explicitly declare it. Non-generic stubs still report the missing method as before. Fixes #21253 --- mypy/stubtest.py | 10 ++++++++++ mypy/test/teststubtest.py | 27 +++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index 63b584e8652f4..5788211749946 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -707,6 +707,16 @@ def verify_typeinfo( mangled_entry = f"_{stub.name.lstrip('_')}{entry}" stub_to_verify = next((t.names[entry].node for t in stub.mro if entry in t.names), MISSING) assert stub_to_verify is not None + # A generic class is subscriptable at the type level via its type + # parameters; a runtime `__class_getitem__` is just the implementation + # detail backing that. Don't report it as missing from a stub that + # already declares the class as generic. See #21253. + if ( + entry == "__class_getitem__" + and isinstance(stub_to_verify, Missing) + and stub.is_generic() + ): + continue try: try: runtime_attr = getattr(runtime, mangled_entry) diff --git a/mypy/test/teststubtest.py b/mypy/test/teststubtest.py index 71555c03b9ad4..bdbb6ef9fb6db 100644 --- a/mypy/test/teststubtest.py +++ b/mypy/test/teststubtest.py @@ -292,6 +292,33 @@ def f(self, number, text): pass error=None, ) + @collect_cases + def test_class_getitem_on_generic(self) -> Iterator[Case]: + # A runtime `__class_getitem__` backs subscriptability that a stub + # expresses via generic type parameters, so it should not be reported + # as missing from a generic stub. See #21253. + yield Case( + stub=""" + from typing import Generic, TypeVar + _T = TypeVar("_T") + class GenericClassGetItem(Generic[_T]): ... + """, + runtime=""" + class GenericClassGetItem: + def __class_getitem__(cls, item, /): return cls + """, + error=None, + ) + # But a non-generic stub should still flag the extra runtime method. + yield Case( + stub="class PlainClassGetItem: ...", + runtime=""" + class PlainClassGetItem: + def __class_getitem__(cls, item, /): return cls + """, + error="PlainClassGetItem.__class_getitem__", + ) + @collect_cases def test_types(self) -> Iterator[Case]: yield Case(