python / python/mypy

MyPy fails type narrowing on `not issubclass(X, type[T]): raise`

Ouverte
#19,917 0 commentaires 0 réactions 0 personnes assignées Voir sur GitHub

Personne n'a encore pris cette issue.

bug
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 **:

Guide de contribution

Ouvrir le guide de contribution

Par où commencer

  1. Lisez l'issue en entier, puis le guide de contribution du projet.
  2. Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
  3. Forkez le dépôt et travaillez sur une branche.
  4. 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

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.