Skip to content
Draft
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
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
4 changes: 2 additions & 2 deletions docs/tutorial/commands/one-or-multiple.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# One or Multiple Commands

You might have noticed that if you create a single command, as in the first example:
You might have noticed that if you create a single command, as in the following example:

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

**Typer** is smart enough to create a CLI application with that single function as the main CLI application, not as a command/subcommand:

Expand Down
22 changes: 11 additions & 11 deletions docs/tutorial/exceptions.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Typer does some tricks to help you detect those errors quickly.

Let's take this example broken app:

{* docs_src/exceptions/tutorial001.py hl[5] *}
{* docs_src/exceptions/tutorial001.py hl[8] *}

This code is broken because you can't sum a string and a number (`name + 3`).

Expand All @@ -26,15 +26,15 @@ So, the error you see will be **much clearer** and simpler, to help you detect t
$ python main.py

<font color="#F92672">╭──────────────── </font><font color="#F92672"><b>Traceback (most recent call last)</b></font><font color="#F92672"> ────────────────╮</font>
<font color="#F92672">│</font> <font color="#A37F4E">/home/user/code/superapp/</font><font color="#F4BF75"><b>main.py</b></font>:<font color="#66D9EF">5</font> in <font color="#A6E22E">main</font> <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#A37F4E">/home/user/code/superapp/</font><font color="#F4BF75"><b>main.py</b></font>:<font color="#66D9EF">8</font> in <font color="#A6E22E">main</font> <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#F92672">│</font>
<font color="#F92672">│</font> 2 <font color="#F92672">│</font>
<font color="#F92672">│</font> 3 <font color="#F92672">│</font>
<font color="#F92672">│</font> 4 <font color="#66D9EF">def</font> <font color="#A6E22E">main</font>(name: <font color="#A1EFE4">str</font> = <font color="#F4BF75">&quot;morty&quot;</font>): <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#F92672">❱ </font> 5 │ <font color="#A1EFE4">print</font>(name + <font color="#66D9EF">3</font>) <font color="#F92672">│</font>
<font color="#F92672">│</font> 6 <font color="#F92672">│</font>
<font color="#F92672">│</font> 7 <font color="#F92672">│</font>
<font color="#F92672">│</font> 8 <font color="#66D9EF">if</font> <font color="#F92672">__name__</font> == <font color="#F4BF75">&quot;__main__&quot;</font>: <font color="#F92672">│</font>
<font color="#F92672">│</font> 5 <font color="#F92672">│</font>
<font color="#F92672">│</font> 6 <font color="#FF00FF">@app</font>.command() <font color="#F92672">│</font>
<font color="#F92672">│</font> 7 <font color="#66D9EF">def</font> <font color="#A6E22E">main</font>(name: <font color="#A1EFE4">str</font> = <font color="#F4BF75">&quot;morty&quot;</font>): <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#F92672">❱ </font> 8 │ <font color="#A1EFE4">print</font>(name + <font color="#66D9EF">3</font>) <font color="#F92672">│</font>
<font color="#F92672">│</font> 9 <font color="#F92672">│</font>
<font color="#F92672">│</font> 10 <font color="#F92672">│</font>
<font color="#F92672">│</font> 11 <font color="#66D9EF">if</font> <font color="#F92672">__name__</font> == <font color="#F4BF75">&quot;__main__&quot;</font>: <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#F4BF75">╭──── locals ────╮</font> <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#F4BF75">│</font> name = <font color="#F4BF75">&apos;morty&apos;</font> <font color="#F4BF75">│</font> <font color="#F92672">│</font>
Expand All @@ -57,7 +57,7 @@ $ python main.py
Traceback (most recent call last):

File "main.py", line 12, in <module>
typer.run(main)
app()

File "main.py", line 8, in main
print(name + 3)
Expand All @@ -83,7 +83,7 @@ In this case, `name` is a local variable, it comes from a parameter passed to th

But if it was something like a password, you would have liked to hide it.

In that case, you can create the `typer.Typer()` application explicitly and set the parameter `pretty_exceptions_show_locals=False`:
In that case, you can set the parameter `pretty_exceptions_show_locals=False` when creating the `typer.Typer()` application:

{* docs_src/exceptions/tutorial002.py hl[3] *}

Expand Down
Loading
Loading