TypedDict accepts an Enum member as a key and reveals the value type, but KeyErrors at runtime
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
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- 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