python / python/mypy

Mypy does not correctly narrow indexing operations when using Literal or Final keys

Aperta
#7,905 5 commenti 0 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

priority-2-low topic-final topic-literal-types topic-type-narrowing topic-typed-dict
Lingua principale
Python
Stelle
20.6k
Fork
3.3k
Metriche di merge delle PR
Metriche PR in attesa

Descrizione

Consider the following program:

from typing_extensions import Literal, TypedDict
from enum import Enum

class Key(Enum):
    X = 1
    Y = 2
    Z = 3

class MyDict(TypedDict):
    key: Literal[Key.X, Key.Y]
    blah: int

KEY: Literal["key"] = "key"

d: MyDict

if d["key"] is Key.X:
    reveal_type(d["key"])  # note: Revealed type is 'Literal[Key.X]'

if d[KEY] is Key.X:
    reveal_type(d[KEY])  # note: Revealed type is 'Literal[Key.X, Key.Y]'

Mypy is currently capable of narrowing expressions like d["key"], which we can see in the first expression.

So, it's natural to assume that mypy would be able to do the same for the second since the two programs are theoretically identical -- but we can't.

The root cause has to do with the "literal" subsystem (which is not to be confused with the Literal types subsystem) here: https://github.com/python/mypy/blob/master/mypy/literals.py#L65

The index in the second example is a NameExpr, which causes the if statement to evaluate to false and return a LITERAL_NO. This then makes the narrowing logic rule out d[KEY] as a candidate for narrowing in https://github.com/python/mypy/blob/master/mypy/checker.py#L3775.

I'm not really sure what the best way of fixing this would be. The natural solution would be to also pass along the expression type into the literal(...) function, but that seems very annoying to do. I'm also not entirely sure whether this is even a sound narrowing: I'm not very familiar with the "literals" subsystem, or how it's meant to interact with Literal types.

Guida per i contributori

Apri la guida per i contributori

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Direzione di ricerca

Inizia con la riproduzione nell’issue, quindi esamina mypy/literals.py intorno a literal() e mypy/checker.py intorno alla logica di narrowing alla riga 3775. Traccia il motivo per cui un NameExpr per KEY produce LITERAL_NO e confrontalo con l’indice stringa diretto. Il lavoro è completato quando il caso KEY restringe d[KEY] a Literal[Key.X] in modo coerente con d["key"].

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à
Tranquilla
Chiarezza
Abbastanza chiara
Idoneità per principianti
48/100

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.