Amend PEP 586 to make `enum` values subtypes of `Literal`

There’s actually a problem I came across where all the type checkers fail to agree with runtime: match-case with a value pattern.

from enum import StrEnum
from typing import Literal, assert_never, reveal_type

class Color(StrEnum):
    RED = "r"
    GREEN = "g"
    BLUE = "b"

def test_literal_as_enum(x: Literal["g"] = "g") -> None:
    match x:
        case Color.RED:
            assert_never(x)
        case Color.GREEN:
            reveal_type(x)
        case Color.BLUE:
            assert_never(x)
        case _:
            assert_never(x)

def test_enum_as_litearl(y: Literal[Color.BLUE] = Color.BLUE) -> None:
    match y:
        case "r":
            assert_never(y)
        case "g":
            assert_never(y)
        case "b":
            reveal_type(y)
        case _:
            assert_never(y)

test_literal_as_enum()
test_enum_as_litearl()

At runtime, this will select the green branch for the first match-case, and the blue branch for the second match-case. But the type checkers all fail:

  • mypy thinks the runtime branches are unreachable
  • pyright thinks the runtime branches are unreachable
  • pyrefly thinks the runtime branches are unreachable
  • ty thinks the runtime branches are unreachable
  • zuban accepts the green branch in the first match-case, but thinks the blue branch is unreachable in the second match-case
2 Likes