["python","python-typing"]
Dynamically create Literal alias from list of valid values

I have a function which validates its argument to accept only values from a given list of valid options. Typing-wise, I reflect this behavior using a Literal type alias, like so: from typing import Literal VALID_ARGUMENTS = ['foo', 'bar'] Argument = Literal['foo', 'bar'] def func(argument: 'Argument') -> None: if argument not in VALID_ARGUMENTS: raise ValueError( f'argument must be one of {VALID_ARGUMENTS}' ) # ... This is a violation of the DRY principle, because I have to rewrite the list of valid arguments in the definition of my Literal type, even if it is already stored in the variable VALID_ARGUMENTS. How can I create the Argument Literal type dynamically, given the VALID_ARGUMENTS variable? The following things do not work: from typing import Literal, Union, NewType Argument = Literal[*VALID_ARGUMENTS] # SyntaxError: invalid syntax Argument = Literal[VALID_ARGUMENTS] # Parameters to generic types must be types Argument = Literal[Union[VALID_ARGUMENTS]] # TypeError: Union[arg, ...]: each arg must be a type. Got ['foo', 'bar']. Argument = NewType( 'Argument', Union[ Literal[valid_argument] for valid_argument in VALID_ARGUMENTS ] ) # Expected type 'Type[_T]', got 'list' instead Can it be done at all?
Go the other way around, and build VALID_ARGUMENTS from Argument: Argument = typing.Literal['foo', 'bar'] VALID_ARGUMENTS: typing.Tuple[Argument, ...] = typing.get_args(Argument) I've used a tuple for VALID_ARGUMENTS here, but if for some reason you really prefer a list, you can get one: VALID_ARGUMENTS: typing.List[Argument] = list(typing.get_args(Argument)) It's possible at runtime to build Argument from VALID_ARGUMENTS, but doing so is incompatible with static analysis, which is the primary use case of type annotations. Doing so is also considered semantically invalid - the spec forbids parameterizing Literal with dynamically computed parameters. The runtime implementation simply doesn't have the information it would need to validate this. Building VALID_ARGUMENTS from Argument is the way to go.