python / python/mypy

TypedDict accepts an Enum member as a key and reveals the value type, but KeyErrors at runtime

Aperta
#21,722 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

Lingua principale
Python
Stelle
20.6k
Fork
3.3k
Metriche di merge delle PR
Metriche PR in attesa

Descrizione

mypy lets you index a TypedDict with an Enum member, and reveal_type gives the correct value type. But it's a KeyError at runtime. Under the hood mypy substitutes the member's .value for the key, a substitution it refuses to make anywhere else.

To Reproduce

from enum import Enum
from typing import Literal, TypedDict

class Foo(Enum):
    One = "One"

class TD(TypedDict):
    One: int

td: TD = {"One": 1}
d: dict[str, int] = {"One": 1}

reveal_type(td[Foo.One])       # Revealed type is "int"  -- accepted
x: Literal["One"] = Foo.One    # error: incompatible types  -- member is not the literal
d[Foo.One]                     # error: invalid index type  -- not a valid str key

Only the TypedDict line gets through. The other two are rejected, and correctly so: Foo.One is Literal[Foo.One], not Literal["One"], and it isn't a str. So mypy already knows the member isn't its value, everywhere except TypedDict subscription.

At runtime a plain Enum member doesn't compare equal to its value, so the "type-safe" access is the thing that breaks:

>>> td[Foo.One]
KeyError: <Foo.One: 'One'>

Expected Behavior

td[Foo.One] should be an error, the same way d[Foo.One] on a dict[str, int] is. pyright rejects it: "Could not access item in TypedDict."

Actual Behavior

Accepted. reveal_type is int. KeyError when you run it.

One wrinkle worth noting: the only case where the runtime agrees with mypy is a str-mixin enum (StrEnum, or class Foo(str, Enum)), because there the member genuinely is the string. So the acceptance happens to be sound for str enums and is a false negative for every other Enum.

Your Environment

  • mypy 2.2.0 (compiled). Reproduces with and without --strict.
  • Python 3.12.8
  • No flags beyond the above; no plugins.
  • For comparison: pyright 1.1.411 flags it.

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 eseguendo con mypy la riproduzione di TypedDict ed Enum, quindi traccia il percorso di type checking della sottoscrizione TypedDict responsabile dell’accettazione di Foo.One. Il lavoro è completato quando i membri Enum normali vengono rifiutati come chiavi di dict[str, int], mentre gli enum con str-mixin continuano a essere accettati; aggiungi o aggiorna un regression test per entrambi i casi.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Valutazione

Stack tecnologico
python
Ambito
compilers, devtools
Tipo di issue
Bug
Difficoltà
3/5
Tempo stimato
1-2 giorni
Stato di attività
Tranquilla
Chiarezza
Specificata chiaramente
Idoneità per principianti
62/100

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.