MyPy fails type narrowing on `not issubclass(X, type[T]): raise`
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
MyPy fails to narrow types using issubclass, and expects an isinstance check which is wrong.
MyPy fails type narrowing on not issubclass(X, type[T]): raise and complains about returning X() later for the function marked as returning T.
To Reproduce
[Mypy Playground example](https://gist.github.com/mypy-play/dcd17343b289523c197a31f850eca90f)
```python from collections.abc import Iterator from typing import TYPE_CHECKING
class MySentinel:
pass
# Works as expected
class OptionalSentinel:
def __init__(self, value: object) -> None:
self._value = value
def get_value[T](self, expected: type[T]) -> Iterator[T]:
if not isinstance(self._value, expected):
raise TypeError
yield self._value
# Reveals bug
class MagicIterator:
def raw[T](self, expected: type[T]) -> Iterator[T]:
"""Get a list of repositories which accept a specific lint to run."""
if not issubclass(MySentinel, expected):
raise ValueError
# if TYPE_CHECKING:
# # an ugly workaround to make mypy happy
# if not isinstance(expected, MySentinel):
# raise ValueError
for project in [MySentinel(), MySentinel()]:
yield project # error: Incompatible types in "yield" (actual type "MySentinel", expected type "T") [misc]
```
Expected Behavior
I expected mypy to infer that after an if not issubclass(T, X): raise block X must be subclass T and it should be ok to return X in place of T.
Since I pass a type and want to compare it to a type an isinstance check is not appropriate for MagicIterator, neiter o I think the issubclass check is the most appropriate (I would prefer to check using expected is not T to guarantee that expected is actually exactly T but ìs`i not listed in the list of supported narrowing expressions.
Actual Behavior
Mypy is only happy if I add an isinstance check, which I deem wrong since I dont pass an instance but a type to the function. Thus, if I do not put the incorrect code in a if TYPE_CHECKCING block my program crashes
src/typeTest.py:31: error: Incompatible types in "yield" (actual type "MySentinel", expected type "T") [misc]
Found 1 error in 1 file (checked 1 source file)
Your Environment
- Mypy version used: mypy 1.18.2 (compiled: yes)
- Mypy command-line flags: none (
mypy src) - Mypy configuration options from
pyproject.toml(and other config files): - Python version used: 3.13.7 (uv)
** Possibly related **:
- https://github.com/python/mypy/issues/10680 (I think these go hand in hand)
- https://github.com/python/mypy/issues/19529
- https://github.com/python/mypy/issues/9003 (might be related, not sure)
- https://github.com/python/mypy/issues/17728 (similar but seems different)
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 l’esempio collegato di MyPy Playground e confronta il percorso di narrowing di issubclass che fallisce con l’esempio isinstance funzionante. Leggi le issue correlate #10680, #19529, #9003 e #17728 prima di individuare i test di narrowing pertinenti e l’implementazione; il lavoro è completato quando l’esempio supera il type-checking senza l’errato workaround a runtime.
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