From b2fb8c8f33df65b6490da5db78ffd3d9ad584f3b Mon Sep 17 00:00:00 2001 From: sanyamk23 Date: Tue, 15 Sep 2026 01:19:07 +0530 Subject: [PATCH] Fix type narrowing with not isinstance(x, cls) in classmethods Rebased onto latest master; dropped the stale actions/checkout and setup-python version bumps from the original commit since upstream has since pinned newer action versions itself. --- mypy/checker.py | 3 +++ test-data/unit/check-narrowing.test | 15 +++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/mypy/checker.py b/mypy/checker.py index 33ed5387554d8..c3e9c087ea66e 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -8397,6 +8397,9 @@ def get_type_range_of_type(self, typ: Type) -> TypeRange | None: is_upper_bound = False if isinstance(typ.item, Instance) and typ.item.type.is_final: is_upper_bound = False + if isinstance(typ.item, TypeVarType) and typ.item.id.is_self(): + # except for Self types, because isinstance checks for exact type + is_upper_bound = False return TypeRange(typ.item, is_upper_bound=is_upper_bound) if isinstance(typ, AnyType): return TypeRange(typ, is_upper_bound=False) diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test index 29f4cd47929d9..02d696a4e4608 100644 --- a/test-data/unit/check-narrowing.test +++ b/test-data/unit/check-narrowing.test @@ -3052,6 +3052,21 @@ def check_d(arg: D[T]) -> None: [builtins fixtures/tuple.pyi] +[case testIsinstanceNarrowingWithSelfTypeInClassmethod] +from typing_extensions import Self + +class Foo: + @classmethod + def foo_1(cls, x: Self | float) -> None: + assert isinstance(x, cls) + reveal_type(x) # N: Revealed type is "Self`0" + + @classmethod + def foo_2(cls, x: Self | float) -> None: + assert not isinstance(x, cls) + reveal_type(x) # N: Revealed type is "builtins.float" +[builtins fixtures/isinstancelist.pyi] + [case testNarrowingUnionMixins] # flags: --strict-equality --warn-unreachable class Base: ...