python / python/mypy

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

Abierto
#21,722 0 comentarios 0 reacciones 0 asignados Ver en GitHub

Nadie ha tomado este issue todavía.

Lenguaje dominante
Python
Estrellas
20.6k
Forks
3.3k
Métricas de merge de PR
Métricas de PR pendientes

Descripción

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.

Guía de contribución

Abrir la guía de contribución

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Línea de trabajo

Empieza ejecutando con mypy la reproducción de TypedDict y Enum, y luego sigue la ruta de comprobación de tipos de la suscripción de TypedDict responsable de aceptar Foo.One. Se considera terminado cuando los miembros de Enum normales se rechazan como claves de dict[str, int], mientras que los enums con str-mixin siguen aceptándose; añade o actualiza una prueba de regresión para ambos casos.

Escrito por el modelo de indexación a partir del texto del issue.

Evaluación

Stack tecnológico
python
Área
compilers, devtools
Tipo de issue
Error
Dificultad
3/5
Tiempo estimado
1-2 días
Estado de actividad
Tranquilo
Claridad
Bien especificado
Aptitud para principiantes
62/100

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.