python / python/mypy

Narrowing an optional class type to an unrelated Protocol causes a false unreachable error

Ouverte
#12,802 0 commentaires 0 réactions 0 personnes assignées Voir sur GitHub

Personne n'a encore pris cette issue.

bug topic-protocols topic-reachability topic-type-narrowing
Langage dominant
Python
Étoiles
20.6k
Forks
3.3k
Métriques de merge des PR
Métriques de PR en attente

Description

Bug Report

If Y is a runtime-checkable protocol which is not implemented by the class X, then mypy will erroneously consider the print statement in the following to be unreachable:

def f(x: Optional[X]) -> None:
    if isinstance(x, Y):
        print(x.bar())

This is incorrect, because x may be an object created from a subclass of X which does implement Y.

If x is annotated as X (not Optional[X]), then mypy behaves correctly.

To Reproduce

Save the following source code to a file (called foo.py below).

from typing import Optional, Protocol, runtime_checkable
from typing_extensions import reveal_type


class X:
    def foo(self) -> int:
        ...


@runtime_checkable
class Y(Protocol):
    def bar(self) -> int:
        ...


class Z(X):
    def foo(self) -> int:
        return 5

    def bar(self) -> int:
        return 6


def f(x: Optional[X]) -> None:
    reveal_type(x)
    if isinstance(x, Y):
        reveal_type(x)
        print(x.bar())


def g(x: X) -> None:
    reveal_type(x)
    if isinstance(x, Y):
        reveal_type(x)
        print(x.bar())


f(Z())
g(Z())

Run mypy on it with --warn-unreachable

Expected Behavior

> mypy --warn-unreachable foo.py
foo.py:25: note: Revealed type is "Union[foo.X, None]"
foo.py:27: note: Revealed type is "foo.<subclass of "X" and "Y">1"
foo.py:32: note: Revealed type is "foo.X"
foo.py:34: note: Revealed type is "foo.<subclass of "X" and "Y">1"
Success: no issues found in 1 source file

Actual Behavior

> mypy --warn-unreachable foo.py
foo.py:25: note: Revealed type is "Union[foo.X, None]"
foo.py:27: error: Statement is unreachable
foo.py:32: note: Revealed type is "foo.X"
foo.py:34: note: Revealed type is "foo.<subclass of "X" and "Y">1"
Found 1 error in 1 file (checked 1 source file)

(Running the script demonstrates that there is no actual unreachable code at runtime.)

Your Environment

  • Mypy version used: mypy 0.950 (compiled: yes)
  • Mypy command-line flags: --warn-unreachable
  • Mypy configuration options from mypy.ini (and other config files): None
  • Python version used: Python 3.10.4
  • Operating system and version:
Edition	Windows 10 Business
Version	21H1
Installed on	‎2.‎8.‎2021
OS build	19043.1706
Experience	Windows Feature Experience Pack 120.2212.4170.0

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 enregistrer le reproducteur fourni sous le nom foo.py et exécuter mypy --warn-unreachable, en comparant f et g. Suivez ensuite le comportement du raffinement de type et du diagnostic de code inaccessible pour Optional[X] par rapport à X. C’est terminé lorsque le cas optionnel est affiné jusqu’à une sous-classe X/Y possible et correspond à la sortie attendue sans erreur de code inaccessible.

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é
Clairement spécifiée
Accessibilité débutants
45/100

Recevez les nouvelles issues par e-mail

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