Type expression describing the string literal union of enum names?

Viewed 2789

Given:

class Color(Enum):
    RED = 1
    GREEN = 2
    BLUE = 3

Is there a type expression Foo such that:

Foo[Color] = Literal["RED", "GREEN", "BLUE"]
2 Answers

From PEP 586 -- Literal Types

Legal parameters for Literal at type check time

Literal[Color.RED] # Assuming Color is some enum

Also in PEP 586:

Literals, enums, and forward references

  • Literal["Color.RED"] (...) In cases like these, we always assume the user meant to construct a literal string.
  • "Literal[Color.RED]" (...) If the user wants a forward reference, they must wrap the entire literal type in a string
from typing import Literal
from enum import Enum


class Color(Enum):
    RED = 1
    GREEN = 2
    BLUE = 3


FIRST = Literal["RED", "GREEN", "BLUE"]
SECOND = Literal[Color.RED, Color.GREEN]
THIRD = Literal["Color.RED", "Color.GREEN"]


def one(arg: FIRST):
    print("FIRST")


def two(arg: SECOND):
    print("SECOND")


def three(arg: THIRD):
    print("THIRD")


one("GREEN")  # Linter issues no warning
one(Color.RED)  # Expected type 'Literal["RED", "GREEN", "BLUE"]', got 'Color' instead
one(Color.RED.name)  # Expected type 'Literal["RED", "GREEN", "BLUE"]', got 'str' instead 

two("RED")  # Expected type 'Color', got 'str' instead 
two(Color.RED)  # Linter issues no warning

three("RED")  # Expected type 'Literal["Color.RED", "Color.GREEN"]', got 'Literal["RED"]' instead 
three("Color.RED") # Linter issues no warning
three(Color.RED.name)  # Expected type 'Literal["Color.RED", "Color.GREEN"]', got 'str' instead 

If you want to declare the Literal by passing as argument a collection of Enum members I think that isn't possible (see: Illegal parameters for Literal at type check time) the static type checker gives the following warning:

'Literal' may be parameterized with literal ints, byte and unicode strings, bools, Enum values, None, other literal types, or type aliases to other literal types

Can't use starred expression here

teste_one = ("RED", "GREEN", "BLUE")
FIRST = Literal[teste_one]
FIRST = Literal[*teste_one]

test_two = Color.__members__.keys()
SECOND = Literal[test_two]
SECOND = Literal[*test_two]

You'll always have to declare the arguments to Literal[...] explicitly.

You could use Enum name programmatic access for a similar effect like :

class Colors(Enum):
    RED = 1
    GREEN = 2
    BLUE = 3
   

ColorsNames = [color.name for color in Colors]

print(ColorsNames)

['RED', 'GREEN', 'BLUE']
Related