1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309
|
# Commands
We have seen how to create a CLI program with possibly several *CLI options* and *CLI arguments*.
But **Typer** allows you to create CLI programs with several commands (also known as subcommands).
For example, the program `git` has several commands.
One command of `git` is `git push`. And `git push` in turn takes its own *CLI arguments* and *CLI options*.
For example:
<div class="termy">
```console
// The push command with no parameters
$ git push
---> 100%
// The push command with one CLI option --set-upstream and 2 CLI arguments
$ git push --set-upstream origin master
---> 100%
```
</div>
Another command of `git` is `git pull`, it also has some *CLI parameters*.
It's like if the same big program `git` had several small programs inside.
/// tip
A command looks the same as a *CLI argument*, it's just some name without a preceding `--`. But commands have a predefined name, and are used to group different sets of functionalities into the same CLI application.
///
## Command or subcommand
It's common to call a CLI program a "command".
But when one of these programs have subcommands, those subcommands are also frequently called just "commands".
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
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.
Now that you know how to create an explicit `typer.Typer()` application and add one command, let's see how to add multiple commands.
Let's say that we have a CLI application to manage users.
We'll have a command to `create` users and another command to `delete` them.
To begin, let's say it can only create and delete one single predefined user:
{* docs_src/commands/index/tutorial002.py hl[6,11] *}
Now we have a CLI application with 2 commands, `create` and `delete`:
<div class="termy">
```console
// Check the help
$ python main.py --help
Usage: main.py [OPTIONS] COMMAND [ARGS]...
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.
Commands:
create
delete
// Test them
$ python main.py create
Creating user: Hiro Hamada
$ python main.py delete
Deleting user: Hiro Hamada
// Now we have 2 commands! 🎉
```
</div>
Notice that the help text now shows the 2 commands: `create` and `delete`.
/// tip
By default, the names of the commands are generated from the function name.
///
## Show the help message if no command is given
By default, we need to specify `--help` to get the command's help page.
However, by setting `no_args_is_help=True` when defining the `typer.Typer()` application, the help function will be shown whenever no argument is given:
{* docs_src/commands/index/tutorial003.py hl[3] *}
Now we can run this:
<div class="termy">
```console
// Check the help without having to type --help
$ python main.py
Usage: main.py [OPTIONS] COMMAND [ARGS]...
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.
Commands:
create
delete
```
</div>
## Sorting of the commands
Note that by design, **Typer** shows the commands in the order they've been declared.
So, if we take our original example, with `create` and `delete` commands, and reverse the order in the Python file:
{* docs_src/commands/index/tutorial004.py hl[7,12] *}
Then we will see the `delete` command first in the help output:
<div class="termy">
```console
// Check the help
$ python main.py --help
Usage: main.py [OPTIONS] COMMAND [ARGS]...
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.
Commands:
delete
create
```
</div>
## Click Group
If you come from Click, a `typer.Typer` app with subcommands is more or less the equivalent of a <a href="https://click.palletsprojects.com/en/7.x/quickstart/#nesting-commands" class="external-link" target="_blank">Click Group</a>.
/// note | Technical Details
A `typer.Typer` app is *not* a Click Group, but it provides the equivalent functionality. And it creates a Click Group when calling it.
It is not directly a Group because **Typer** doesn't modify the functions in your code to convert them to another type of object, it only registers them.
///
## Decorator Technical Details
When you use `@app.command()` the function under the decorator is registered in the **Typer** application and is then used later by the application.
But Typer doesn't modify that function itself, the function is left as is.
That means that if your function is simple enough that you could create it without using `typer.Option()` or `typer.Argument()`, you could use the same function for a **Typer** application and a **FastAPI** application putting both decorators on top, or similar tricks.
/// note | Click Technical Details
This behavior is a design difference with Click.
In Click, when you add a `@click.command()` decorator it actually modifies the function underneath and replaces it with an object.
///
|