Inspired by this highly upvoted issue: Literal of enum values · Issue #781 · python/typing · GitHub, I propose that enum values,
- if they are defined literally
- and are instances of the type of the literal
(So this would be applicable mostly toIntEnumandStrEnum, but not regularEnum)
Then they should be subtypes of the corresponding literal type. For example, Number.ONE should be considered a subtype of Literal[1], but Weekday.MONDAY should not be considered a subtype of Literal[1], because Weekday doesn’t subclass int. Essentially, I propose the following amended inference rule for enums/literals:
enum.CASE <: Literal[<val>] if and only if type(enum.CASE) <: type(<val>) and enum.CASE.value = <val> literally. Moreover, in this case, equality comparisons should evaluate to True.
The rationale is that:
-
It is type safe, if the
enumsubclasses the corresponding type. -
It is consistent with runtime behavior.
In fact, both pyright and mypy are not consistent with runtime behavior here in some cases:
Simple comparison [pyright playground], [mypy-playground]:
from enum import IntEnum class Number(IntEnum): ONE = 1 TWO = 2 if 1 == Number.ONE: # <- incorrect no-overlap print("equal") # get's called at runtime.match-case example [pyright playground], [mypy-playground]:
from enum import StrEnum from typing import Literal, assert_never class Options(StrEnum): A = "foo" B = "bar" def show(x: Literal["foo", "bar"]) -> None: match x: case Options.A: # <-- incorrectly marked as unreachable return print("It's a foo!") case Options.B: # <-- incorrectly marked as unreachable return print("It's a bar!") assert_never(x) # <-- false positive show("foo") # prints "It's a foo!" show("bar") # prints "It's a bar!"And if we annotate
show(x: Options)instead, then we get flagged at the call site if we pass literal strings. So, if both behaviors are to be allowed one has to annotate withOptions | Literal["foo", "bar"]which introduces lots of redundancy. To get the checkers happy, something like this is needed. -
It is consistent with the “core behavior” of Literal types:
-
Literal types indicate that a variable has a specific and concrete value.
Exactly what enums do
-
Given some value v that is a member of type T, the type Literal[v] shall be treated as a subtype of T
Exactly how enums work, for example
isisntance(Number.ONE, int)is True.
-
-
It simplifies type hinting dramatically in some cases (as per Literal of enum values · Issue #781 · python/typing · GitHub)