Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
29fc205
move explanation about Typer() instance creation to separate tutorial…
svlandeg Nov 21, 2025
1c57854
update questions template
svlandeg Nov 21, 2025
0377ad4
update argument sections in the docs
svlandeg Nov 21, 2025
f654dbc
🎨 [pre-commit.ci] Auto format from pre-commit.com hooks
pre-commit-ci[bot] Nov 21, 2025
e34df56
fix renamed file
svlandeg Nov 21, 2025
ab5e5c9
update exceptions section
svlandeg Nov 21, 2025
024cd31
update Rich exception (TODO: outline border)
svlandeg Nov 21, 2025
877612a
fix exceptions test to refer to app()
svlandeg Nov 21, 2025
b3562ba
remove some extra spaces (wip)
svlandeg Nov 21, 2025
1440f34
add back some extra spaces (wip)
svlandeg Nov 21, 2025
da39182
update launch section
svlandeg Nov 21, 2025
ff40c66
update multiple_values sections
svlandeg Nov 21, 2025
2c2266a
update options sections
svlandeg Nov 21, 2025
c2949d5
fix
svlandeg Nov 21, 2025
07dc0f7
update sections of parameter_types
svlandeg Nov 21, 2025
69a126c
fix
svlandeg Nov 24, 2025
c957c35
update docs section on printing
svlandeg Nov 24, 2025
8446fcc
update docs section on progress bar
svlandeg Nov 24, 2025
7cd2e23
🎨 [pre-commit.ci] Auto format from pre-commit.com hooks
pre-commit-ci[bot] Nov 24, 2025
0676819
fix
svlandeg Nov 24, 2025
bef651b
update docs section on prompts
svlandeg Nov 24, 2025
fb71281
Merge branch 'master' into fix/tutorial
svlandeg Nov 24, 2025
383bd96
update docs section on terminating
svlandeg Nov 24, 2025
f3a01d1
update docs section on app-dir
svlandeg Nov 24, 2025
24aecee
fix
svlandeg Nov 24, 2025
0c768f7
update test
svlandeg Nov 24, 2025
fdf8e21
fix test assert
svlandeg Nov 24, 2025
e415fa1
attempt to fix outline
svlandeg Nov 24, 2025
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
4 changes: 3 additions & 1 deletion .github/DISCUSSION_TEMPLATE/questions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,15 @@ body:
placeholder: |
import typer

app = typer.Typer()

@app.command()
def main(name: str):
typer.echo(f"Hello {name}")


if __name__ == "__main__":
typer.run(main)
app()
render: python
validations:
required: true
Expand Down
4 changes: 2 additions & 2 deletions docs/tutorial/arguments/default.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ That way the *CLI argument* will be optional *and also* have a default value.

We can also use `typer.Argument()` to make a *CLI argument* have a default value other than `None`:

{* docs_src/arguments/default/tutorial001_an.py hl[5] *}
{* docs_src/arguments/default/tutorial001_an.py hl[8] *}

/// tip

Expand Down Expand Up @@ -52,7 +52,7 @@ Hello Camila

And we can even make the default value be dynamically generated by passing a function as the `default_factory` argument:

{* docs_src/arguments/default/tutorial002_an.py hl[7:8,11] *}
{* docs_src/arguments/default/tutorial002_an.py hl[10:11,14] *}

In this case, we created the function `get_name` that will just return a random `str` each time.

Expand Down
6 changes: 3 additions & 3 deletions docs/tutorial/arguments/envvar.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ You can learn more about environment variables in the [Environment Variables](..

To do that, use the `envvar` parameter for `typer.Argument()`:

{* docs_src/arguments/envvar/tutorial001_an.py hl[5] *}
{* docs_src/arguments/envvar/tutorial001_an.py hl[8] *}

In this case, the *CLI argument* `name` will have a default value of `"World"`, but will also read any value passed to the environment variable `AWESOME_NAME` if no value is provided in the command line:

Expand Down Expand Up @@ -55,7 +55,7 @@ Hello Mr. Czernobog

You are not restricted to a single environment variable, you can declare a list of environment variables that could be used to get a value if it was not passed in the command line:

{* docs_src/arguments/envvar/tutorial002_an.py hl[6] *}
{* docs_src/arguments/envvar/tutorial002_an.py hl[9] *}

Check it:

Expand Down Expand Up @@ -90,7 +90,7 @@ Hello Mr. Anubis

By default, environment variables used will be shown in the help text, but you can disable them with `show_envvar=False`:

{* docs_src/arguments/envvar/tutorial003_an.py hl[7] *}
{* docs_src/arguments/envvar/tutorial003_an.py hl[10] *}

Check it:

Expand Down
16 changes: 8 additions & 8 deletions docs/tutorial/arguments/help.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Now that you also know how to use `typer.Argument()`, let's use it to add docume

You can use the `help` parameter to add a help text for a *CLI argument*:

{* docs_src/arguments/help/tutorial001_an.py hl[5] *}
{* docs_src/arguments/help/tutorial001_an.py hl[8] *}

And it will be used in the automatic `--help` option:

Expand All @@ -37,7 +37,7 @@ Options:

And of course, you can also combine that `help` with the <abbr title="a multi-line string as the first expression inside a function (not assigned to any variable) used for documentation">docstring</abbr>:

{* docs_src/arguments/help/tutorial002_an.py hl[5:8] *}
{* docs_src/arguments/help/tutorial002_an.py hl[8:11] *}

And the `--help` option will combine all the information:

Expand All @@ -64,7 +64,7 @@ Options:

If you have a *CLI argument* with a default value, like `"World"`:

{* docs_src/arguments/help/tutorial003_an.py hl[5] *}
{* docs_src/arguments/help/tutorial003_an.py hl[8] *}

It will show that default value in the help text:

Expand All @@ -89,7 +89,7 @@ Options:

But you can disable that if you want to, with `show_default=False`:

{* docs_src/arguments/help/tutorial004_an.py hl[7] *}
{* docs_src/arguments/help/tutorial004_an.py hl[10] *}

And then it won't show the default value:

Expand Down Expand Up @@ -124,7 +124,7 @@ In **Typer** these default values are shown by default. 👀

You can use the same `show_default` to pass a custom string (instead of a `bool`) to customize the default value to be shown in the help text:

{* docs_src/arguments/help/tutorial005_an.py hl[9] *}
{* docs_src/arguments/help/tutorial005_an.py hl[12] *}

And it will be used in the help text:

Expand Down Expand Up @@ -169,7 +169,7 @@ But you can customize it with the `metavar` parameter for `typer.Argument()`.

For example, let's say you don't want to have the default of `NAME`, you want to have `username`, in lowercase, and you really want ✨ emojis ✨ everywhere:

{* docs_src/arguments/help/tutorial006_an.py hl[5] *}
{* docs_src/arguments/help/tutorial006_an.py hl[8] *}

Now the generated help text will have `✨username✨` instead of `NAME`:

Expand All @@ -195,7 +195,7 @@ You might want to show the help information for *CLI arguments* in different pan

If you have installed Rich as described in the docs for [Printing and Colors](../printing.md){.internal-link target=_blank}, you can set the `rich_help_panel` parameter to the name of the panel where you want this *CLI argument* to be shown:

{* docs_src/arguments/help/tutorial007_an.py hl[8,12] *}
{* docs_src/arguments/help/tutorial007_an.py hl[11,15] *}

Then, if you check the `--help` option, you will see a default panel named "`Arguments`" for the *CLI arguments* that don't have a custom `rich_help_panel`.

Expand Down Expand Up @@ -238,7 +238,7 @@ If you want, you can make a *CLI argument* **not** show up in the `Arguments` se

You will probably not want to do this normally, but it's possible:

{* docs_src/arguments/help/tutorial008_an.py hl[5] *}
{* docs_src/arguments/help/tutorial008_an.py hl[8] *}

Check it:

Expand Down
19 changes: 11 additions & 8 deletions docs/tutorial/arguments/optional.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,11 @@ In the [First Steps](../first-steps.md#add-a-cli-argument){.internal-link target

Now let's see an alternative way to create the same *CLI argument*:

{* docs_src/arguments/optional/tutorial000.py hl[4] *}

{* docs_src/arguments/optional/tutorial001_an.py hl[5] *}
Or, using an explicit `Typer()` instance creation:

{* docs_src/arguments/optional/tutorial001_an.py hl[8] *}
Comment on lines +42 to +46
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here, I've created a new tutorial file (docs_src/arguments/optional/tutorial000.py) to better show the difference with the more simplistic docs_src/first_steps/tutorial002.py. The next version then (docs_src/arguments/optional/tutorial001_an.py) introduces both Annotated as well as Typer().


/// info

Expand Down Expand Up @@ -111,7 +114,7 @@ Now, finally what we came for, an optional *CLI argument*.

To make a *CLI argument* optional, use `typer.Argument()` and make sure to provide a "default" value, for example `"World"`:

{* docs_src/arguments/optional/tutorial002_an.py hl[5] *}
{* docs_src/arguments/optional/tutorial002_an.py hl[8] *}

Now we have:

Expand Down Expand Up @@ -178,7 +181,7 @@ Notice that "`Camila`" here is an optional *CLI argument*, not a *CLI option*, b

Instead of using `Annotated`, you can use `typer.Argument()` as the default value:

{* docs_src/arguments/optional/tutorial001.py hl[4] *}
{* docs_src/arguments/optional/tutorial001.py hl[7] *}

/// tip

Expand Down Expand Up @@ -212,13 +215,13 @@ If you hadn't seen that `...` before: it is a special single value, it is <a hre

///

{* docs_src/arguments/optional/tutorial003.py hl[4] *}
{* docs_src/arguments/optional/tutorial003.py hl[7] *}

And the same way, you can make it optional by passing a different `default` value, for example `None`:
And the same way, you can make it optional by passing a different `default` value, for example `"World"`:
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here, and in the lines below, I've had to make extra edits, because this section had become out-of-date on master after PR #983. Basically in PR 983 we updated tutorial002 to have the default value "World" instead of None, and we updated the explanation around L112 but didn't notice that this same example was being used here at the end of the page at L220 and beyond.


{* docs_src/arguments/optional/tutorial002.py hl[6] *}
{* docs_src/arguments/optional/tutorial002.py hl[7] *}

Because the first parameter passed to `typer.Argument(default=None)` (the new "default" value) is `None`, **Typer** knows that this is an **optional** *CLI argument*, if no value is provided when calling it in the command line, it will have that default value of `None`.
Because the first parameter passed to `typer.Argument(default="World")` (the new "default" value) is `"World"`, **Typer** knows that this is an **optional** *CLI argument*, if no value is provided when calling it in the command line, it will have that default value of `"World"`.

The `default` argument is the first one, so it's possible that you see code that passes the value without explicitly using `default=`, like:

Expand All @@ -229,7 +232,7 @@ name: str = typer.Argument(...)
...or like:

```Python
name: str = typer.Argument(None)
name: str = typer.Argument("World")
```

...but again, try to use `Annotated` if possible, that way your code in terms of Python will mean the same thing as with **Typer** and you won't have to remember any of these details.
121 changes: 2 additions & 119 deletions docs/tutorial/commands/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,127 +46,10 @@ Have that in mind so you don't get confused.

Here I'll use **CLI application** or **program** to refer to the program you are building in Python with Typer, and **command** to refer to one of these "subcommands" of your program.

## Explicit application
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This bit is moved almost verbatim to typer-instance.md, with some minor edits to ensure correct flow.


Before creating CLI applications with multiple commands/subcommands we need to understand how to create an explicit `typer.Typer()` application.

In the *CLI options* and *CLI argument* tutorials you have seen how to create a single function and then pass that function to `typer.run()`.

For example:

{* docs_src/first_steps/tutorial002.py hl[9] *}

But that is actually a shortcut. Under the hood, **Typer** converts that to a CLI application with `typer.Typer()` and executes it. All that inside of `typer.run()`.

There's also a more explicit way to achieve the same:

{* docs_src/commands/index/tutorial001.py hl[3,6,12] *}

When you use `typer.run()`, **Typer** is doing more or less the same as above, it will:

* Create a new `typer.Typer()` "application".
* Create a new "`command`" with your function.
* Call the same "application" as if it was a function with "`app()`".

/// info | `@decorator` Info

That `@something` syntax in Python is called a "decorator".

You put it on top of a function. Like a pretty decorative hat (I guess that's where the term came from).

A "decorator" takes the function below and does something with it.

In our case, this decorator tells **Typer** that the function below is a "`command`".

///

Both ways, with `typer.run()` and creating the explicit application, achieve almost the same.

/// tip

If your use case is solved with just `typer.run()`, that's fine, you don't have to create the explicit `app` and use `@app.command()`, etc.

You might want to do that later when your app needs the extra features, but if it doesn't need them yet, that's fine.

///

If you run the second example, with the explicit `app`, it works exactly the same:

<div class="termy">

```console
// Without a CLI argument
$ python main.py

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

Error: Missing argument 'NAME'.

// With the NAME CLI argument
$ python main.py Camila

Hello Camila

// Asking for help
$ python main.py --help

Usage: main.py [OPTIONS] NAME

Options:
--install-completion Install completion for the current shell.
--show-completion Show completion for the current shell, to copy it or customize the installation.
--help Show this message and exit.
```

</div>

## CLI application completion

There's a little detail that is worth noting here.

Now the help shows two new *CLI options*:

* `--install-completion`
* `--show-completion`

To get shell/tab completion, it's necessary to build a package that you and your users can install and **call directly**.

So instead of running a Python script like:

<div class="termy">

```console
$ python main.py

✨ Some magic here ✨
```

</div>

...It would be called like:

<div class="termy">

```console
$ magic-app

✨ Some magic here ✨
```

</div>

Having a standalone program like that allows setting up shell/tab completion.

The first step to be able to create an installable package like that is to use an explicit `typer.Typer()` app.

Later you can learn all the process to create a standalone CLI application and [Build a Package](../package.md){.internal-link target=_blank}.

But for now, it's just good to know that you are on that path. 😎

## A CLI application with multiple commands

Coming back to the CLI applications with multiple commands/subcommands, **Typer** allows creating CLI applications with multiple of them.

**Typer** allows creating CLI applications with multiple commands/subcommands.

Now that you know how to create an explicit `typer.Typer()` application and add one command, let's see how to add multiple commands.

Expand Down
Loading
Loading