Skip to content

Enum - Choices

To define a CLI parameter that can take a value from a predefined set of values you can use a standard Python enum.Enum:

from enum import Enum

import typer


class NeuralNetwork(str, Enum):
    simple = "simple"
    conv = "conv"
    lstm = "lstm"


app = typer.Typer()


@app.command()
def main(network: NeuralNetwork = NeuralNetwork.simple):
    print(f"Training neural network of type: {network.value}")


if __name__ == "__main__":
    app()

Tip

Notice that the function parameter network will be an Enum, not a str.

To get the str value in your function's code use network.value.

Check it:

$ python main.py --help

// Notice the predefined values [simple|conv|lstm]
Usage: main.py [OPTIONS]

Options:
  --network [simple|conv|lstm]  [default: simple]
  --help                        Show this message and exit.

// Try it
$ python main.py --network conv

Training neural network of type: conv

// Invalid value
$ python main.py --network capsule

Usage: main.py [OPTIONS]
Try "main.py --help" for help.

Error: Invalid value for '--network': 'capsule' is not one of 'simple', 'conv', 'lstm'.

// Note that enums are case sensitive by default
$ python main.py --network CONV

Usage: main.py [OPTIONS]
Try "main.py --help" for help.

Error: Invalid value for '--network': 'CONV' is not one of 'simple', 'conv', 'lstm'.

Case insensitive Enum choices

You can make an Enum (choice) CLI parameter be case-insensitive with the case_sensitive parameter:

from enum import Enum
from typing import Annotated

import typer


class NeuralNetwork(str, Enum):
    simple = "simple"
    conv = "conv"
    lstm = "lstm"


app = typer.Typer()


@app.command()
def main(
    network: Annotated[
        NeuralNetwork, typer.Option(case_sensitive=False)
    ] = NeuralNetwork.simple,
):
    print(f"Training neural network of type: {network.value}")


if __name__ == "__main__":
    app()
🤓 Other versions and variants

Tip

Prefer to use the Annotated version if possible.

from enum import Enum

import typer


class NeuralNetwork(str, Enum):
    simple = "simple"
    conv = "conv"
    lstm = "lstm"


app = typer.Typer()


@app.command()
def main(
    network: NeuralNetwork = typer.Option(NeuralNetwork.simple, case_sensitive=False),
):
    print(f"Training neural network of type: {network.value}")


if __name__ == "__main__":
    app()

And then the values of the Enum will be checked no matter if lower case, upper case, or a mix:

// Notice the upper case CONV
$ python main.py --network CONV

Training neural network of type: conv

// A mix also works
$ python main.py --network LsTm

Training neural network of type: lstm

List of Enum values

A CLI parameter can also take a list of Enum values:

from enum import Enum
from typing import Annotated

import typer


class Food(str, Enum):
    food_1 = "Eggs"
    food_2 = "Bacon"
    food_3 = "Cheese"


app = typer.Typer()


@app.command()
def main(groceries: Annotated[list[Food], typer.Option()] = [Food.food_1, Food.food_3]):
    print(f"Buying groceries: {', '.join([f.value for f in groceries])}")


if __name__ == "__main__":
    app()
🤓 Other versions and variants

Tip

Prefer to use the Annotated version if possible.

from enum import Enum

import typer


class Food(str, Enum):
    food_1 = "Eggs"
    food_2 = "Bacon"
    food_3 = "Cheese"


app = typer.Typer()


@app.command()
def main(groceries: list[Food] = typer.Option([Food.food_1, Food.food_3])):
    print(f"Buying groceries: {', '.join([f.value for f in groceries])}")


if __name__ == "__main__":
    app()

This works just like any other parameter value taking a list of things:

$ python main.py --help

// Notice the default values being shown
Usage: main.py [OPTIONS]

Options:
  --groceries [Eggs|Bacon|Cheese]  [default: Eggs, Cheese]
  --help                           Show this message and exit.

// Try it with the default values
$ python main.py

Buying groceries: Eggs, Cheese

// Try it with a single value
$ python main.py --groceries "Eggs"

Buying groceries: Eggs

// Try it with multiple values
$ python main.py --groceries "Eggs" --groceries "Bacon"

Buying groceries: Eggs, Bacon

Literal choices

You can also use Literal to represent a set of possible predefined choices, without having to use an Enum:

from typing import Annotated, Literal

import typer

app = typer.Typer()


@app.command()
def main(
    network: Annotated[Literal["simple", "conv", "lstm"], typer.Option()] = "simple",
):
    print(f"Training neural network of type: {network}")


if __name__ == "__main__":
    app()
🤓 Other versions and variants

Tip

Prefer to use the Annotated version if possible.

from typing import Literal

import typer

app = typer.Typer()


@app.command()
def main(network: Literal["simple", "conv", "lstm"] = typer.Option("simple")):
    print(f"Training neural network of type: {network}")


if __name__ == "__main__":
    app()
$ python main.py --help

// Notice the predefined values [simple|conv|lstm]
Usage: main.py [OPTIONS]

Options:
  --network [simple|conv|lstm]  [default: simple]
  --help                        Show this message and exit.

// Try it
$ python main.py --network conv

Training neural network of type: conv

// Invalid value
$ python main.py --network capsule

Usage: main.py [OPTIONS]
Try "main.py --help" for help.

Error: Invalid value for '--network': 'capsule' is not one of 'simple', 'conv', 'lstm'.

Functional API

In order to use an Enum created using the functional API, you need to create an enum with string values.

You also need to supply the default value as a string (not the enum):

from enum import Enum
from typing import Annotated

import typer

NeuralNetwork = Enum("NeuralNetwork", {k: k for k in ["simple", "conv", "lstm"]})

app = typer.Typer()


@app.command()
def main(
    network: Annotated[NeuralNetwork, typer.Option(case_sensitive=False)] = "simple",
):
    print(f"Training neural network of type: {network.value}")


if __name__ == "__main__":
    app()
🤓 Other versions and variants

Tip

Prefer to use the Annotated version if possible.

from enum import Enum

import typer

NeuralNetwork = Enum("NeuralNetwork", {k: k for k in ["simple", "conv", "lstm"]})

app = typer.Typer()


@app.command()
def main(network: NeuralNetwork = typer.Option("simple", case_sensitive=False)):
    print(f"Training neural network of type: {network.value}")


if __name__ == "__main__":
    app()

Alternatively, you can create an Enum that extends both str and Enum. In Python 3.11+, there is enum.StrEnum. For Python 3.10 or earlier, there is the StrEnum package.