MyPy fails type narrowing on `not issubclass(X, type[T]): raise`
Personne n'a encore pris cette issue.
- Langage dominant
- Python
- Étoiles
- 20.6k
- Forks
- 3.3k
- Métriques de merge des PR
- Métriques de PR en attente
Description
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)
Guide de contribution
Ouvrir le guide de contribution
Par où commencer
- Lisez l'issue en entier, puis le guide de contribution du projet.
- Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
- Forkez le dépôt et travaillez sur une branche.
- Ouvrez une pull request qui référence le numéro de l'issue.
Piste de recherche
Commencez par l’exemple MyPy Playground lié et comparez le chemin de narrowing de issubclass qui échoue avec l’exemple isinstance qui fonctionne. Lisez les issues associées #10680, #19529, #9003 et #17728 avant de localiser les tests de narrowing pertinents et l’implémentation ; c’est terminé lorsque l’exemple passe le type-checking sans le workaround d’exécution incorrect.
Rédigé par le modèle d'indexation à partir du texte de l'issue.
Évaluation
- Stack technique
- python
- Domaine
- devtools
- Type d'issue
- Bug
- Difficulté
- 4/5
- Temps estimé
- 3-5 jours
- Activité
- À l'abandon
- Clarté
- Plutôt claire
- Accessibilité débutants
- 35/100