[regression] Type narrowing for through `not issubclass(…)` broken for union of TypeVar instance / types in 1.17
Nessuno ha ancora preso questa issue.
- Lingua principale
- Python
- Stelle
- 20.6k
- Fork
- 3.3k
- Metriche di merge delle PR
- Metriche PR in attesa
Descrizione
Bug Report
In mypy 1.17.0, type variables for arguments which are defined as the union of a generic TypeVar instance or type are not correctly type-narrowed.
To Reproduce
cf https://mypy-play.net/?mypy=latest&python=3.12&gist=630030506910825d5db3d022cdd4198f
from typing import TypeVar
MyType = TypeVar("MyType")
class HelloMixin:
a: str
def hello(world: MyType | type[MyType]) -> HelloMixin | None:
world = world if isinstance(world, type) else type(world)
reveal_type(world)
if not issubclass(world, HelloMixin):
return None
reveal_type(world)
res = world()
reveal_type(res)
reveal_type(res.a)
return res
Expected Behavior
In mypy 1.16.1, type checking succeeds, and the variables are correctly type-narrowed, i.e. after the if not issubclass(world, HelloMixin):, world is correctly typed as type[subclass_narrowing.HelloMixin]:
% /tmp/core/venv/bin/mypy --version
mypy 1.16.1 (compiled: yes)
% /tmp/core/venv/bin/mypy /tmp/subclass_narrowing.py
/tmp/subclass_narrowing.py: note: In function "hello":
/tmp/subclass_narrowing.py:13:17: note: Revealed type is "builtins.type"
/tmp/subclass_narrowing.py:17:17: note: Revealed type is "type[subclass_narrowing.HelloMixin]"
/tmp/subclass_narrowing.py:19:17: note: Revealed type is "subclass_narrowing.HelloMixin"
/tmp/subclass_narrowing.py:20:17: note: Revealed type is "builtins.str"
Success: no issues found in 1 source file
Actual Behavior
In mypy 1.17.0:
% ./venv/bin/mypy --version
mypy 1.17.0 (compiled: yes)
% ./venv/bin/mypy /tmp/subclass_narrowing.py
/tmp/subclass_narrowing.py: note: In function "hello":
/tmp/subclass_narrowing.py:13:17: note: Revealed type is "MyType`-1 | type[MyType`-1]"
/tmp/subclass_narrowing.py:17:17: note: Revealed type is "MyType`-1 | type[MyType`-1]"
/tmp/subclass_narrowing.py:19:17: note: Revealed type is "Any | MyType`-1"
/tmp/subclass_narrowing.py:20:17: error: Item "object" of "Any | MyType" has no attribute "a" [union-attr]
reveal_type(res.a)
^~~~~
/tmp/subclass_narrowing.py:20:17: note: See https://mypy.rtfd.io/en/stable/_refs.html#code-union-attr for more info
/tmp/subclass_narrowing.py:20:17: note: Revealed type is "Any"
/tmp/subclass_narrowing.py:21:12: error: Incompatible return value type (got "Any | MyType", expected "HelloMixin | None") [return-value]
return res
^~~
Found 2 errors in 1 file (checked 1 source file)
Note the revealed type of the world argument: in 1.16.1 it starts off as just builtins.type, while 1.17 more precisely types it as MyType-1 | type[MyType-1] before the issubclass() check.
A regular world: type[MyType] does not exhibit this behaviour, nor does concrete types (e.g. using Base | type[Base], with HelloMixin as a Base subclass). This only happens when using an union, world: type[MyType] does not exhibit the bug.
I'm not sure where the problem comes from exactly, but the first reveal_type(world) shows us that the line making sure it's always a type already fails to be narrowed to type[MyType-1]. Using a dedicated variable (i.e. world_type = world if isinstance(world, type) else type(world)) still fails as well.
Your Environment
- Mypy version used: 1.17
- Mypy command-line flags:
- Mypy configuration options from
mypy.ini(and other config files): - Python version used: 3.12.10
Guida per i contributori
Apri la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Direzione di ricerca
Inizia con il reproducer fornito e confronta i tipi rivelati con mypy 1.16.1 e 1.17.0, quindi traccia la logica di narrowing per i controlli isinstance e issubclass. Il lavoro è concluso quando l’unione di un’istanza di TypeVar e del suo tipo viene ristretta correttamente, i tipi rivelati corrispondono al comportamento previsto e l’esempio supera il controllo dei tipi senza errori.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Valutazione
- Stack tecnologico
- python
- Ambito
- devtools
- Tipo di issue
- Bug
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Stato di attività
- Ferma
- Chiarezza
- Abbastanza chiara
- Idoneità per principianti
- 35/100