Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Add support for Literal type #669

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 155 additions & 0 deletions docs/tutorial/parameter-types/choices.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
To define a *CLI parameter* that can take a value from a predefined set of values you can use a standard Python
<a href="https://docs.python.org/3/library/enum.html" class="external-link" target="_blank">`enum.Enum`</a> or the
standard type hint <a href="https://docs.python.org/3/library/typing.html#typing.Literal" class="external-link" target="_blank">`typing.Literal`</a>

# Enum

```Python hl_lines="1 6 7 8 9 12 13"
{!../docs_src/parameter_types/choices/tutorial001.py!}
```

!!! 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:

<div class="termy">

```console
$ 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': invalid choice: capsule. (choose from simple, conv, lstm)
```

</div>

### Case insensitive Enum choices

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

=== "Python 3.6+"

```Python hl_lines="15"
{!> ../docs_src/parameter_types/choices/tutorial002_an.py!}
```

=== "Python 3.6+ non-Annotated"

!!! tip
Prefer to use the `Annotated` version if possible.

```Python hl_lines="13"
{!> ../docs_src/parameter_types/choices/tutorial002.py!}
```

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

<div class="termy">

```console
// 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
```

</div>


## Literal

```Python hl_lines="2 4 7"
{!../docs_src/parameter_types/choices/tutorial003.py!}
```

Check it:

<div class="termy">

```console
$ 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': invalid choice: capsule. (choose from simple, conv, lstm)
```

</div>

### Case insensitive Literal choices

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

=== "Python 3.6+"

```Python hl_lines="8"
{!> ../docs_src/parameter_types/choices/tutorial004_an.py!}
```

=== "Python 3.6+ non-Annotated"

!!! tip
Prefer to use the `Annotated` version if possible.

```Python hl_lines="7"
{!> ../docs_src/parameter_types/choices/tutorial004.py!}
```

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

<div class="termy">

```console
// 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
```

</div>
77 changes: 0 additions & 77 deletions docs/tutorial/parameter-types/enum.md

This file was deleted.

12 changes: 12 additions & 0 deletions docs_src/parameter_types/choices/tutorial003.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import typer
from typing_extensions import Literal

NeuralNetworkType = Literal["simple", "conv", "lstm"]


def main(network: NeuralNetworkType = "simple"):
print(f"Training neural network of type: {network}")


if __name__ == "__main__":
typer.run(main)
12 changes: 12 additions & 0 deletions docs_src/parameter_types/choices/tutorial004.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import typer
from typing_extensions import Literal

NeuralNetworkType = Literal["simple", "conv", "lstm"]


def main(network: NeuralNetworkType = typer.Option("simple", case_sensitive=False)):
print(f"Training neural network of type: {network}")


if __name__ == "__main__":
typer.run(main)
14 changes: 14 additions & 0 deletions docs_src/parameter_types/choices/tutorial004_an.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import typer
from typing_extensions import Annotated, Literal

NeuralNetworkType = Literal["simple", "conv", "lstm"]


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


if __name__ == "__main__":
typer.run(main)
2 changes: 1 addition & 1 deletion mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ nav:
- Boolean CLI Options: tutorial/parameter-types/bool.md
- UUID: tutorial/parameter-types/uuid.md
- DateTime: tutorial/parameter-types/datetime.md
- Enum - Choices: tutorial/parameter-types/enum.md
- Choices: tutorial/parameter-types/choices.md
- Path: tutorial/parameter-types/path.md
- File: tutorial/parameter-types/file.md
- Custom Types: tutorial/parameter-types/custom-types.md
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import typer
from typer.testing import CliRunner

from docs_src.parameter_types.enum import tutorial001 as mod
from docs_src.parameter_types.choices import tutorial001 as mod

runner = CliRunner()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import typer
from typer.testing import CliRunner

from docs_src.parameter_types.enum import tutorial002 as mod
from docs_src.parameter_types.choices import tutorial002 as mod

runner = CliRunner()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import typer
from typer.testing import CliRunner

from docs_src.parameter_types.enum import tutorial002_an as mod
from docs_src.parameter_types.choices import tutorial002_an as mod

runner = CliRunner()

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import subprocess
import sys

import typer
from typer.testing import CliRunner

from docs_src.parameter_types.choices import tutorial003 as mod

runner = CliRunner()

app = typer.Typer()
app.command()(mod.main)


def test_help():
result = runner.invoke(app, ["--help"])
assert result.exit_code == 0
assert "--network" in result.output
assert "[simple|conv|lstm]" in result.output


def test_main():
result = runner.invoke(app, ["--network", "conv"])
assert result.exit_code == 0
assert "Training neural network of type: conv" in result.output


def test_invalid():
result = runner.invoke(app, ["--network", "capsule"])
assert result.exit_code != 0
# TODO: when deprecating Click 7, remove second option

assert (
"Invalid value for '--network': 'capsule' is not one of" in result.output
or "Invalid value for '--network': invalid choice: capsule. (choose from"
in result.output
)
assert "simple" in result.output
assert "conv" in result.output
assert "lstm" in result.output


def test_script():
result = subprocess.run(
[sys.executable, "-m", "coverage", "run", mod.__file__, "--help"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
)
assert "Usage" in result.stdout
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import subprocess
import sys

import typer
from typer.testing import CliRunner

from docs_src.parameter_types.choices import tutorial004 as mod

runner = CliRunner()

app = typer.Typer()
app.command()(mod.main)


def test_upper():
result = runner.invoke(app, ["--network", "CONV"])
assert result.exit_code == 0
assert "Training neural network of type: conv" in result.output


def test_mix():
result = runner.invoke(app, ["--network", "LsTm"])
assert result.exit_code == 0
assert "Training neural network of type: lstm" in result.output


def test_script():
result = subprocess.run(
[sys.executable, "-m", "coverage", "run", mod.__file__, "--help"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
)
assert "Usage" in result.stdout
Loading