MyPy fails type narrowing on `not issubclass(X, type[T]): raise`
Dieses Issue hat noch niemand übernommen.
- Vorherrschende Sprache
- Python
- Sterne
- 20.6k
- Forks
- 3.3k
- PR-Merge-Kennzahlen
- PR-Kennzahlen ausstehend
Beschreibung
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)
Beitragsleitfaden
Erste Schritte
- Lies das ganze Issue und danach den Beitragsleitfaden des Projekts.
- Schreib ins Issue, dass du es übernimmst — das erspart doppelte Arbeit.
- Forke das Repository und arbeite in einem Branch.
- Öffne einen Pull Request, der die Issue-Nummer nennt.
Rechercherichtung
Beginne mit dem verlinkten MyPy Playground-Beispiel und vergleiche den fehlschlagenden issubclass-Narrowing-Pfad mit dem funktionierenden isinstance-Beispiel. Lies die zugehörigen Issues #10680, #19529, #9003 und #17728, bevor du die relevanten Narrowing-Tests und die Implementierung suchst; abgeschlossen ist die Aufgabe, wenn das Beispiel ohne den fehlerhaften Laufzeit-Workaround typgeprüft wird.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Bewertung
- Tech-Stack
- python
- Bereich
- devtools
- Issue-Typ
- Bug
- Schwierigkeit
- 4/5
- Geschätzter Aufwand
- 3-5 Tage
- Aktivitätsstatus
- Veraltet
- Klarheit
- Größtenteils klar
- Anfängerfreundlichkeit
- 35/100