You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[1.20 regression] when same object is narrowed multiple times #21181
There was a regression in narrowing with isinstance checks from 1.19 to 1.20. This seems to only happen when the same object is narrowed multiple times. For example, this works correct if removing the outer is not None guard and the | None from the argument.
from __future__ importannotationsdef_score_test(
exc: BaseException|None, expected_excs: set[type[BaseException]]
) ->None:
ifexcisnotNone:
reveal_type(exc)
reveal_type(expected_excs) # Revealed type is "BaseException"reveal_type(tuple(expected_excs)) # Revealed type is "tuple[type[BaseException], ...]"ifisinstance(exc, tuple(expected_excs)):
reveal_type(exc) # Revealed type is "object"returnlogexc(exc) # Argument 1 to "logexc" has incompatible type "object"; expected "BaseException | None"deflogexc(
exc: BaseException|None
) ->None:
...
changed the title [-]Regression with isinstance narrowing 1.19.1->1.20.0[/-][+][1.20 regression] when same object is narrowed multiple times[/+]on Apr 7, 2026
While you can patch by making changes in the spot that that code touched, I wonder if the more correct fix is in conditional_types, or maybe even the binder... we shouldn't be widening the type at all
from __future__ import annotations
from typing import Iterable
from typing_extensions import TypeAlias
import types
# Regression test for https://github.com/python/mypy/issues/21181
# We don't have the same type context as with the real stubs, so sort of fake it
_ClassInfoLike: TypeAlias = "type | tuple[_ClassInfoLike, ...]"
class A: ...
def fake_type_context(ts: list[type[A]]) -> _ClassInfoLike:
return tuple(ts) # E: Too many arguments for "tuple"
def f1(x: A | None, ts: list[type[A]]) -> None:
if x is not None:
reveal_type(x) # N: Revealed type is "__main__.A"
if isinstance(x, fake_type_context(ts)):
reveal_type(x) # N: Revealed type is "__main__.A"
def f2(x: A | None) -> None:
if x is not None:
reveal_type(x) # N: Revealed type is "__main__.A"
if isinstance(x, object):
reveal_type(x) # N: Revealed type is "__main__.A"
def f3(x: A | None, t: type) -> None:
if x is not None:
reveal_type(x) # N: Revealed type is "__main__.A"
if isinstance(x, t):
reveal_type(x) # N: Revealed type is "__main__.A"
f1 is like your case, and it regresses when we go from 1.19 to 1.20
But f3 I claim is the underlying issue and we incorrectly widen the type in both 1.19 and 1.20
Activity
[-]Regression with isinstance narrowing 1.19.1->1.20.0[/-][+][1.20 regression] when same object is narrowed multiple times[/+]hauntsaninja commentedon Apr 13, 2026
This bisects to #20675
While you can patch by making changes in the spot that that code touched, I wonder if the more correct fix is in conditional_types, or maybe even the binder... we shouldn't be widening the type at all
hauntsaninja commentedon Apr 13, 2026
Okay yeah....
So if you look at the following code
f1 is like your case, and it regresses when we go from 1.19 to 1.20
But f3 I claim is the underlying issue and we incorrectly widen the type in both 1.19 and 1.20
Avoid widening types in conditional_types
Avoid widening types in conditional_types (python#21242)
Avoid widening types in conditional_types (#21242)