diff --git a/docs/src/commands/tutorial003.py b/docs/src/commands/arguments/tutorial001.py
similarity index 100%
rename from docs/src/commands/tutorial003.py
rename to docs/src/commands/arguments/tutorial001.py
diff --git a/docs/src/commands/tutorial005.py b/docs/src/commands/help/tutorial001.py
similarity index 100%
rename from docs/src/commands/tutorial005.py
rename to docs/src/commands/help/tutorial001.py
diff --git a/docs/src/commands/help/tutorial002.py b/docs/src/commands/help/tutorial002.py
new file mode 100644
index 0000000..d098d23
--- /dev/null
+++ b/docs/src/commands/help/tutorial002.py
@@ -0,0 +1,23 @@
+import typer
+
+app = typer.Typer()
+
+
+@app.command(help="Create a new user with USERNAME.")
+def create(username: str):
+ """
+ Some internal utility function to create.
+ """
+ typer.echo(f"Creating user: {username}")
+
+
+@app.command(help="Delete a user with USERNAME.")
+def delete(username: str):
+ """
+ Some internal utility function to delete.
+ """
+ typer.echo(f"Deleting user: {username}")
+
+
+if __name__ == "__main__":
+ app()
diff --git a/docs/src/commands/tutorial001.py b/docs/src/commands/index/tutorial001.py
similarity index 100%
rename from docs/src/commands/tutorial001.py
rename to docs/src/commands/index/tutorial001.py
diff --git a/docs/src/commands/tutorial002.py b/docs/src/commands/index/tutorial002.py
similarity index 100%
rename from docs/src/commands/tutorial002.py
rename to docs/src/commands/index/tutorial002.py
diff --git a/docs/src/commands/tutorial006.py b/docs/src/commands/name/tutorial001.py
similarity index 100%
rename from docs/src/commands/tutorial006.py
rename to docs/src/commands/name/tutorial001.py
diff --git a/docs/src/commands/tutorial004.py b/docs/src/commands/options/tutorial001.py
similarity index 100%
rename from docs/src/commands/tutorial004.py
rename to docs/src/commands/options/tutorial001.py
diff --git a/docs/src/options/tutorial001.py b/docs/src/options/help/tutorial001.py
similarity index 100%
rename from docs/src/options/tutorial001.py
rename to docs/src/options/help/tutorial001.py
diff --git a/docs/src/options/tutorial006.py b/docs/src/options/help/tutorial002.py
similarity index 100%
rename from docs/src/options/tutorial006.py
rename to docs/src/options/help/tutorial002.py
diff --git a/docs/src/options/name/tutorial001.py b/docs/src/options/name/tutorial001.py
new file mode 100644
index 0000000..7bc212f
--- /dev/null
+++ b/docs/src/options/name/tutorial001.py
@@ -0,0 +1,9 @@
+import typer
+
+
+def main(user_name: str = typer.Option(..., "--name")):
+ typer.echo(f"Hello {user_name}")
+
+
+if __name__ == "__main__":
+ typer.run(main)
diff --git a/docs/src/options/name/tutorial002.py b/docs/src/options/name/tutorial002.py
new file mode 100644
index 0000000..10eba64
--- /dev/null
+++ b/docs/src/options/name/tutorial002.py
@@ -0,0 +1,9 @@
+import typer
+
+
+def main(user_name: str = typer.Option(..., "--name", "-n")):
+ typer.echo(f"Hello {user_name}")
+
+
+if __name__ == "__main__":
+ typer.run(main)
diff --git a/docs/src/options/name/tutorial003.py b/docs/src/options/name/tutorial003.py
new file mode 100644
index 0000000..5a8325a
--- /dev/null
+++ b/docs/src/options/name/tutorial003.py
@@ -0,0 +1,9 @@
+import typer
+
+
+def main(user_name: str = typer.Option(..., "-n")):
+ typer.echo(f"Hello {user_name}")
+
+
+if __name__ == "__main__":
+ typer.run(main)
diff --git a/docs/src/options/name/tutorial004.py b/docs/src/options/name/tutorial004.py
new file mode 100644
index 0000000..fa31904
--- /dev/null
+++ b/docs/src/options/name/tutorial004.py
@@ -0,0 +1,9 @@
+import typer
+
+
+def main(user_name: str = typer.Option(..., "--user-name", "-n")):
+ typer.echo(f"Hello {user_name}")
+
+
+if __name__ == "__main__":
+ typer.run(main)
diff --git a/docs/src/options/name/tutorial005.py b/docs/src/options/name/tutorial005.py
new file mode 100644
index 0000000..d0a094d
--- /dev/null
+++ b/docs/src/options/name/tutorial005.py
@@ -0,0 +1,15 @@
+import typer
+
+
+def main(
+ name: str = typer.Option(..., "--name", "-n"),
+ formal: bool = typer.Option(False, "--formal", "-f"),
+):
+ if formal:
+ typer.echo(f"Good day Ms. {name}.")
+ else:
+ typer.echo(f"Hello {name}")
+
+
+if __name__ == "__main__":
+ typer.run(main)
diff --git a/docs/src/options/tutorial003.py b/docs/src/options/prompt/tutorial001.py
similarity index 100%
rename from docs/src/options/tutorial003.py
rename to docs/src/options/prompt/tutorial001.py
diff --git a/docs/src/options/tutorial004.py b/docs/src/options/prompt/tutorial002.py
similarity index 100%
rename from docs/src/options/tutorial004.py
rename to docs/src/options/prompt/tutorial002.py
diff --git a/docs/src/options/tutorial005.py b/docs/src/options/prompt/tutorial003.py
similarity index 100%
rename from docs/src/options/tutorial005.py
rename to docs/src/options/prompt/tutorial003.py
diff --git a/docs/src/options/tutorial002.py b/docs/src/options/required/tutorial001.py
similarity index 100%
rename from docs/src/options/tutorial002.py
rename to docs/src/options/required/tutorial001.py
diff --git a/docs/tutorial/arguments.md b/docs/tutorial/arguments.md
index adae09d..8915ed9 100644
--- a/docs/tutorial/arguments.md
+++ b/docs/tutorial/arguments.md
@@ -1,4 +1,4 @@
-The same way that you have `typer.Option()` to help you define things for *CLI options*, there's also the equivalent `typer.Argument()` for *CLI arguments*.
+Let's see how to configure *CLI arguments* with `typer.Argument()`.
## Optional *CLI arguments*
@@ -63,20 +63,15 @@ And then we changed it to:
name: str = typer.Argument(...)
```
-The same as with `typer.Option()`, there is a `typer.Argument()`.
+But now as `typer.Argument()` is the "default value" of the function's parameter, it would mean that "it is no longer required" (in Python terms).
-And now as `typer.Argument()` is the "default value" of the function's parameter, in Python terms, it would mean that "it is no longer required" (in Python terms).
+As we no longer have the Python function default value (or its absence) to tell if something is required or not and what is the default value, the first parameter to `typer.Argument()` serves the same purpose of defining that default value, or making it required.
-As we no longer have the Python function default value (or its absence) to tell it if something is required or not and what is the default value, the first parameter to `typer.Argument()` serves the same purpose of defining that default value, or making it required.
-
-To make it *required*, we pass `...` as that first parameter to the function.
+To make it *required*, we pass `...` as the first function argument passed to `typer.Argument(...)`.
!!! info
If you hadn't seen that `...` before: it is a a special single value, it is part of Python and is called "Ellipsis".
-!!! tip
- This works exactly the same way `typer.Option()` does.
-
All we did there achieves the same thing as before, a **required** *CLI argument*:
@@ -150,7 +145,7 @@ $ python main.py
Hello World!
// With one optional CLI argument
-$ python main.py
+$ python main.py Camila
Hello Camila
```
diff --git a/docs/tutorial/commands.md b/docs/tutorial/commands.md
deleted file mode 100644
index 446c1d8..0000000
--- a/docs/tutorial/commands.md
+++ /dev/null
@@ -1,555 +0,0 @@
-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:
-
-
-
-```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%
-```
-
-
-
-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 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:
-
-```Python hl_lines="9"
-{!./src/first_steps/tutorial002.py!}
-```
-
-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:
-
-```Python hl_lines="3 6 12"
-{!./src/commands/tutorial001.py!}
-```
-
-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 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:
-
-
-
-```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.
-```
-
-
-
-## 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:
-
-```Python hl_lines="6 11"
-{!./src/commands/tutorial002.py!}
-```
-
-Now we have a CLI application with 2 commands, `create` and `delete`:
-
-
-
-```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! š
-```
-
-
-
-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.
-
-## Command *CLI arguments*
-
-The same way as with a CLI application with a single command, subcommands (or just "commands") can also have their own *CLI arguments*:
-
-```Python hl_lines="7 12"
-{!./src/commands/tutorial003.py!}
-```
-
-
-
-```console
-// Check the help for create
-$ python main.py create --help
-
-Usage: main.py create [OPTIONS] USERNAME
-
-Options:
- --help Show this message and exit.
-
-// Call it with a CLI argument
-$ python main.py create Camila
-
-Creating user: Camila
-
-// The same for delete
-$ python main.py delete Camila
-
-Deleting user: Camila
-```
-
-
-
-!!! tip
- Everything to the *right* of the *command* are *CLI parameters* (*CLI arguments* and *CLI options*) for that command.
-
-!!! note "Technical Details"
- Actually, it's everything to the right of that command, *before any subcommand*.
-
- It's possible to have groups of *subcommands*, it's like if one *command* also had *subcommands*. And then those *subcommands* could have their own *CLI parameters*, taking their own *CLI parameters*.
-
- You will see about them later in another section.
-
-## Command *CLI options*
-
-Commands can also have their own *CLI options*.
-
-In fact, each command can have different *CLI arguments* and *CLI options*:
-
-```Python hl_lines="7 13 14 24 33"
-{!./src/commands/tutorial004.py!}
-```
-
-Here we have multiple commands, with different *CLI parameters*:
-
-* `create`:
- * `username`: a *CLI argument*.
-* `delete`:
- * `username`: a *CLI argument*.
- * `--force`: a *CLI option*, if not provided, it's prompted.
-* `delete-all`:
- * `--force`: a *CLI option*, if not provided, it's prompted.
-* `init`:
- * Doesn't take any *CLI parameters*.
-
-
-
-```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
- delete-all
- info
-```
-
-
-
-!!! tip
- Check the command `delete-all`, by default command names are generated from the function name, replacing `_` with `-`.
-
-Test it:
-
-
-
-```console
-// Check the command create
-$ python main.py create Camila
-
-Creating user: Camila
-
-// Now test the command delete
-$ python main.py delete Camila
-
-# Are you sure you want to delete the user? [y/N]: $ y
-
-Deleting user: Camila
-
-$ python main.py delete Wade
-
-# Are you sure you want to delete the user? [y/N]: $ n
-
-Operation cancelled
-
-// And finally, the command delete-all
-// Notice it doesn't have CLI arguments, only a CLI option
-
-$ python main.py delete-all
-
-# Are you sure you want to delete ALL users? [y/N]: $ y
-
-Deleting all users
-
-$ python main.py delete-all
-
-# Are you sure you want to delete ALL users? [y/N]: $ n
-
-Operation cancelled
-
-// And if you pass the --force CLI option, it doesn't need to confirm
-
-$ python main.py delete-all --force
-
-Deleting all users
-
-// And init that doesn't take any CLI parameter
-$ python main.py init
-
-Initializing user database
-```
-
-
-
-## Command help
-
-The same as before, you can add help for the commands in the docstrings and the *CLI options*.
-
-And the `typer.Typer()` application receives a parameter `help` that you can pass with the main help text for your CLI program:
-
-```Python hl_lines="3 8 9 10 20 23 24 25 26 27 39 42 43 44 45 46 55 56 57"
-{!./src/commands/tutorial005.py!}
-```
-
-Check it:
-
-
-
-```console
-// Check the new help
-$ python main.py --help
-
-Usage: main.py [OPTIONS] COMMAND [ARGS]...
-
- Awesome CLI user manager.
-
-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 Create a new user with USERNAME.
- delete Delete a user with USERNAME.
- delete-all Delete ALL users in the database.
- init Initialize the users database.
-
-// Now the commands have inline help š
-
-// Check the help for create
-$ python main.py create --help
-
-Usage: main.py create [OPTIONS] USERNAME
-
- Create a new user with USERNAME.
-
-Options:
- --help Show this message and exit.
-
-// Check the help for delete
-$ python main.py delete --help
-
-Usage: main.py delete [OPTIONS] USERNAME
-
- Delete a user with USERNAME.
-
- If --force is not used, will ask for confirmation.
-
-Options:
- --force / --no-force Force deletion without confirmation. [required]
- --help Show this message and exit.
-
-// Check the help for delete-all
-$ python main.py delete-all --help
-
-Usage: main.py delete-all [OPTIONS]
-
- Delete ALL users in the database.
-
- If --force is not used, will ask for confirmation.
-
-Options:
- --force / --no-force Force deletion without confirmation. [required]
- --help Show this message and exit.
-
-// Check the help for init
-$ python main.py init --help
-
-Usage: main.py init [OPTIONS]
-
- Initialize the users database.
-
-Options:
- --help Show this message and exit.
-```
-
-
-
-!!! tip
- `typer.Typer()` receives several other parameters for other things, we'll see that later.
-
- You will also see how to use "Callbacks" later, and those include a way to add this same help message in a function docstring.
-
-## Custom command name
-
-By default, the command names are generated from the function name.
-
-So, if your function is something like:
-
-```Python
-def create(username: str):
- ...
-```
-
-Then the command name will be `create`.
-
-But if you already had a function called `create()` somewhere in your code, you would have to name your CLI function differently.
-
-And what if you wanted the command to still be named `create`?
-
-For this, you can set the name of the command in the first parameter for the `@app.command()` decorator:
-
-```Python hl_lines="6 11"
-{!./src/commands/tutorial006.py!}
-```
-
-Now, even though the functions are named `cli_create_user()` and `cli_delete_user()`, the commands will still be named `create` and `delete`:
-
-
-
-```console
-$ 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 it
-$ python main.py create Camila
-
-Creating user: Camila
-```
-
-
-
-## One command vs multiple commands
-
-You might have noticed that if you create a single command, as in the first example:
-
-```Python hl_lines="3 6 12"
-{!./src/commands/tutorial001.py!}
-```
-
-**Typer** is smart enough to create a CLI application with that single function as the main CLI application, not as a command/subcommand:
-
-
-
-```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
-
-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.
-```
-
-
-
-!!! tip
- Notice that it doesn't show a command `main`, even though the function name is `main`.
-
-But if you add multiple commands, **Typer** will create one *CLI command* for each one of them:
-
-```Python hl_lines="6 11"
-{!./src/commands/tutorial002.py!}
-```
-
-Here we have 2 commands `create` and `delete`:
-
-
-
-```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 the commands
-$ python main.py create
-
-Creating user: Hiro Hamada
-
-$ python main.py delete
-
-Deleting user: Hiro Hamada
-```
-
-
-
-!!! tip
- This is **Typer**'s behavior by default, but you could still create a CLI application with one single command, like:
-
- ```
- $ python main.py main
- ```
-
- instead of just:
-
- ```
- $ python main.py
- ```
-
- You will learn more about this in the section about application Callbacks.
-
-## 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.
diff --git a/docs/tutorial/commands/arguments.md b/docs/tutorial/commands/arguments.md
new file mode 100644
index 0000000..10052ef
--- /dev/null
+++ b/docs/tutorial/commands/arguments.md
@@ -0,0 +1,39 @@
+The same way as with a CLI application with a single command, subcommands (or just "commands") can also have their own *CLI arguments*:
+
+```Python hl_lines="7 12"
+{!./src/commands/arguments/tutorial001.py!}
+```
+
+
+
+```console
+// Check the help for create
+$ python main.py create --help
+
+Usage: main.py create [OPTIONS] USERNAME
+
+Options:
+ --help Show this message and exit.
+
+// Call it with a CLI argument
+$ python main.py create Camila
+
+Creating user: Camila
+
+// The same for delete
+$ python main.py delete Camila
+
+Deleting user: Camila
+```
+
+
+
+!!! tip
+ Everything to the *right* of the *command* are *CLI parameters* (*CLI arguments* and *CLI options*) for that command.
+
+!!! note "Technical Details"
+ Actually, it's everything to the right of that command, *before any subcommand*.
+
+ It's possible to have groups of *subcommands*, it's like if one *command* also had *subcommands*. And then those *subcommands* could have their own *CLI parameters*, taking their own *CLI parameters*.
+
+ You will see about them later in another section.
diff --git a/docs/tutorial/commands/help.md b/docs/tutorial/commands/help.md
new file mode 100644
index 0000000..e3e811b
--- /dev/null
+++ b/docs/tutorial/commands/help.md
@@ -0,0 +1,120 @@
+The same as before, you can add help for the commands in the docstrings and the *CLI options*.
+
+And the `typer.Typer()` application receives a parameter `help` that you can pass with the main help text for your CLI program:
+
+```Python hl_lines="3 8 9 10 20 23 24 25 26 27 39 42 43 44 45 46 55 56 57"
+{!./src/commands/help/tutorial001.py!}
+```
+
+Check it:
+
+
+
+```console
+// Check the new help
+$ python main.py --help
+
+Usage: main.py [OPTIONS] COMMAND [ARGS]...
+
+ Awesome CLI user manager.
+
+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 Create a new user with USERNAME.
+ delete Delete a user with USERNAME.
+ delete-all Delete ALL users in the database.
+ init Initialize the users database.
+
+// Now the commands have inline help š
+
+// Check the help for create
+$ python main.py create --help
+
+Usage: main.py create [OPTIONS] USERNAME
+
+ Create a new user with USERNAME.
+
+Options:
+ --help Show this message and exit.
+
+// Check the help for delete
+$ python main.py delete --help
+
+Usage: main.py delete [OPTIONS] USERNAME
+
+ Delete a user with USERNAME.
+
+ If --force is not used, will ask for confirmation.
+
+Options:
+ --force / --no-force Force deletion without confirmation. [required]
+ --help Show this message and exit.
+
+// Check the help for delete-all
+$ python main.py delete-all --help
+
+Usage: main.py delete-all [OPTIONS]
+
+ Delete ALL users in the database.
+
+ If --force is not used, will ask for confirmation.
+
+Options:
+ --force / --no-force Force deletion without confirmation. [required]
+ --help Show this message and exit.
+
+// Check the help for init
+$ python main.py init --help
+
+Usage: main.py init [OPTIONS]
+
+ Initialize the users database.
+
+Options:
+ --help Show this message and exit.
+```
+
+
+
+!!! tip
+ `typer.Typer()` receives several other parameters for other things, we'll see that later.
+
+ You will also see how to use "Callbacks" later, and those include a way to add this same help message in a function docstring.
+
+## Overwrite command help
+
+You will probably be better adding the help text as a docstring to your functions, but if for some reason you wanted to overwrite it, you can use the `help` function argument passed to `@app.command()`:
+
+```Python hl_lines="6 14"
+{!./src/commands/help/tutorial002.py!}
+```
+
+Check it:
+
+
+
+```console
+// Check the help
+$ python main.py --help
+
+// Notice it uses the help passed to @app.command()
+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 Create a new user with USERNAME.
+ delete Delete a user with USERNAME.
+
+// It uses "Create a new user with USERNAME." instead of "Some internal utility function to create."
+```
+
+
diff --git a/docs/tutorial/commands/index.md b/docs/tutorial/commands/index.md
new file mode 100644
index 0000000..f06a5d2
--- /dev/null
+++ b/docs/tutorial/commands/index.md
@@ -0,0 +1,182 @@
+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:
+
+
+
+```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%
+```
+
+
+
+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 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:
+
+```Python hl_lines="9"
+{!./src/first_steps/tutorial002.py!}
+```
+
+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:
+
+```Python hl_lines="3 6 12"
+{!./src/commands/index/tutorial001.py!}
+```
+
+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 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:
+
+
+
+```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.
+```
+
+
+
+## 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:
+
+```Python hl_lines="6 11"
+{!./src/commands/index/tutorial002.py!}
+```
+
+Now we have a CLI application with 2 commands, `create` and `delete`:
+
+
+
+```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! š
+```
+
+
+
+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.
+
+## 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.
diff --git a/docs/tutorial/commands/name.md b/docs/tutorial/commands/name.md
new file mode 100644
index 0000000..00011fa
--- /dev/null
+++ b/docs/tutorial/commands/name.md
@@ -0,0 +1,46 @@
+By default, the command names are generated from the function name.
+
+So, if your function is something like:
+
+```Python
+def create(username: str):
+ ...
+```
+
+Then the command name will be `create`.
+
+But if you already had a function called `create()` somewhere in your code, you would have to name your CLI function differently.
+
+And what if you wanted the command to still be named `create`?
+
+For this, you can set the name of the command in the first parameter for the `@app.command()` decorator:
+
+```Python hl_lines="6 11"
+{!./src/commands/name/tutorial001.py!}
+```
+
+Now, even though the functions are named `cli_create_user()` and `cli_delete_user()`, the commands will still be named `create` and `delete`:
+
+
+
+```console
+$ 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 it
+$ python main.py create Camila
+
+Creating user: Camila
+```
+
+
diff --git a/docs/tutorial/commands/one-or-multiple.md b/docs/tutorial/commands/one-or-multiple.md
new file mode 100644
index 0000000..cab0da3
--- /dev/null
+++ b/docs/tutorial/commands/one-or-multiple.md
@@ -0,0 +1,91 @@
+You might have noticed that if you create a single command, as in the first example:
+
+```Python hl_lines="3 6 12"
+{!./src/commands/tutorial001.py!}
+```
+
+**Typer** is smart enough to create a CLI application with that single function as the main CLI application, not as a command/subcommand:
+
+
+
+```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
+
+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.
+```
+
+
+
+!!! tip
+ Notice that it doesn't show a command `main`, even though the function name is `main`.
+
+But if you add multiple commands, **Typer** will create one *CLI command* for each one of them:
+
+```Python hl_lines="6 11"
+{!./src/commands/tutorial002.py!}
+```
+
+Here we have 2 commands `create` and `delete`:
+
+
+
+```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 the commands
+$ python main.py create
+
+Creating user: Hiro Hamada
+
+$ python main.py delete
+
+Deleting user: Hiro Hamada
+```
+
+
+
+!!! tip
+ This is **Typer**'s behavior by default, but you could still create a CLI application with one single command, like:
+
+ ```
+ $ python main.py main
+ ```
+
+ instead of just:
+
+ ```
+ $ python main.py
+ ```
+
+ You will learn more about this in the section about application Callbacks.
diff --git a/docs/tutorial/commands/options.md b/docs/tutorial/commands/options.md
new file mode 100644
index 0000000..40e4ca9
--- /dev/null
+++ b/docs/tutorial/commands/options.md
@@ -0,0 +1,96 @@
+Commands can also have their own *CLI options*.
+
+In fact, each command can have different *CLI arguments* and *CLI options*:
+
+```Python hl_lines="7 13 14 24 33"
+{!./src/commands/options/tutorial001.py!}
+```
+
+Here we have multiple commands, with different *CLI parameters*:
+
+* `create`:
+ * `username`: a *CLI argument*.
+* `delete`:
+ * `username`: a *CLI argument*.
+ * `--force`: a *CLI option*, if not provided, it's prompted.
+* `delete-all`:
+ * `--force`: a *CLI option*, if not provided, it's prompted.
+* `init`:
+ * Doesn't take any *CLI parameters*.
+
+
+
+```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
+ delete-all
+ info
+```
+
+
+
+!!! tip
+ Check the command `delete-all`, by default command names are generated from the function name, replacing `_` with `-`.
+
+Test it:
+
+
+
+```console
+// Check the command create
+$ python main.py create Camila
+
+Creating user: Camila
+
+// Now test the command delete
+$ python main.py delete Camila
+
+# Are you sure you want to delete the user? [y/N]: $ y
+
+Deleting user: Camila
+
+$ python main.py delete Wade
+
+# Are you sure you want to delete the user? [y/N]: $ n
+
+Operation cancelled
+
+// And finally, the command delete-all
+// Notice it doesn't have CLI arguments, only a CLI option
+
+$ python main.py delete-all
+
+# Are you sure you want to delete ALL users? [y/N]: $ y
+
+Deleting all users
+
+$ python main.py delete-all
+
+# Are you sure you want to delete ALL users? [y/N]: $ n
+
+Operation cancelled
+
+// And if you pass the --force CLI option, it doesn't need to confirm
+
+$ python main.py delete-all --force
+
+Deleting all users
+
+// And init that doesn't take any CLI parameter
+$ python main.py init
+
+Initializing user database
+```
+
+
diff --git a/docs/tutorial/options.md b/docs/tutorial/options.md
deleted file mode 100644
index a1dc05d..0000000
--- a/docs/tutorial/options.md
+++ /dev/null
@@ -1,263 +0,0 @@
-## *CLI options* with help
-
-In the *First Steps* section you saw how to add help for a CLI app/command by adding it to a function's
docstring.
-
-Here's how that last example looked like:
-
-```Python
-{!./src/first_steps/tutorial006.py!}
-```
-
-Now we'll add a *help* section to the *CLI options*:
-
-```Python hl_lines="6 7"
-{!./src/options/tutorial001.py!}
-```
-
-We are replacing the default values we had before with `typer.Option()`.
-
-As we no longer have a default value there, the first parameter to `typer.Option()` serves the same purpose of defining that default value.
-
-So, if we had:
-
-```Python
-lastname: str = ""
-```
-
-now we write:
-
-```Python
-lastname: str = typer.Option("")
-```
-
-And both forms achieve the same: a *CLI option* with a default value of an empty string (`""`).
-
-And then we can pass the `help` keyword parameter:
-
-```Python
-lastname: str = typer.Option("", help="this option does this and that")
-```
-
-to create the help for that *CLI option*.
-
-Copy that example from above to a file `main.py`.
-
-Test it:
-
-
-
-```console
-$ python main.py --help
-
-Usage: main.py [OPTIONS] NAME
-
- Say hi to NAME, optionally with a --lastname.
-
- If --formal is used, say hi very formally.
-
-Options:
- --lastname TEXT Last name of person to greet.
- --formal / --no-formal Say hi formally.
- --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.
-
-// Now you have a help text for the --lastname and --formal CLI options š
-```
-
-
-
-## Make a *CLI option* required
-
-We said before that *by default*:
-
-* *CLI options* are **optional**
-* *CLI arguments* are **required**
-
-Well, that's how they work *by default*, and that's the convention in many CLI programs and systems.
-
-But if you really want, you can change that.
-
-To make a *CLI option* required, pass `...` to `typer.Option()`.
-
-!!! info
- If you hadn't seen that `...` before: it is a a special single value, it is
part of Python and is called "Ellipsis".
-
-That will tell **Typer** that it's still a *CLI option*, but it doesn't have a default value, and it's required.
-
-Let's make the `--lastname` a required *CLI option*.
-
-We'll also simplify the example to focus on the new parts:
-
-```Python hl_lines="4"
-{!./src/options/tutorial002.py!}
-```
-
-!!! tip
- You could still add `help` to `typer.Option()` as before, but we are omitting it here to simplify the example.
-
-And test it:
-
-
-
-```console
-// Pass the NAME CLI argument
-$ python main.py Camila
-
-// We didn't pass the now required --lastname CLI option
-Usage: main.py [OPTIONS] NAME
-Try "main.py --help" for help.
-
-Error: Missing option "--lastname".
-
-// Now update it to pass the required --lastname CLI option
-$ python main.py Camila --lastname GutiƩrrez
-
-Hello Camila GutiƩrrez
-
-// And if you check the help
-$ python main.py --help
-
-Usage: main.py [OPTIONS] NAME
-
-Options:
- --lastname TEXT [required]
- --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.
-
-// It now tells you that --lastname is required š
-```
-
-
-
-## Prompt for a *CLI option*
-
-It's also possible to, instead of just showing an error, ask for the missing value with `prompt=True`:
-
-```Python hl_lines="4"
-{!./src/options/tutorial003.py!}
-```
-
-And then your program will ask the user for it in the terminal:
-
-
-
-```console
-// Call it with the NAME CLI argument
-$ python main.py Camila
-
-// It asks for the missing CLI option --lastname
-# Lastname: $ GutiƩrrez
-
-Hello Camila GutiƩrrez
-```
-
-
-
-### Customize the prompt
-
-You can also set a custom prompt, passing the string that you want to use instead of just `True`:
-
-```Python hl_lines="6"
-{!./src/options/tutorial004.py!}
-```
-
-And then your program will ask for it using with your custom prompt:
-
-
-
-```console
-// Call it with the NAME CLI argument
-$ python main.py Camila
-
-// It uses the custom prompt
-# Please tell me your last name: $ GutiƩrrez
-
-Hello Camila GutiƩrrez
-```
-
-
-
-## Confirmation prompt
-
-In some cases you could want to prompt for something and then ask the user to confirm it by typing it twice.
-
-You can do it passing the parameter `confirmation_prompt=True`.
-
-Let's say it's a CLI app to delete a project:
-
-```Python hl_lines="4"
-{!./src/options/tutorial005.py!}
-```
-
-And it will prompt the user for a value and then for the confirmation:
-
-
-
-```console
-$ python main.py
-
-// Your app will first prompt for the project name, and then for the confirmation
-# Project name: $ Old Project
-# Repeat for confirmation: $ Old Project
-
-Deleting project Old Project
-
-// If the user doesn't type the same, receives an error and a new prompt
-$ python main.py
-
-# Project name: $ Old Project
-# Repeat for confirmation: $ New Spice
-
-Error: the two entered values do not match
-
-# Project name: $ Old Project
-# Repeat for confirmation: $ Old Project
-
-Deleting project Old Project
-
-// Now it works š
-```
-
-
-
-## Show default in help
-
-You can tell Typer to show the default value in the help text with `show_default=True`:
-
-```Python hl_lines="4"
-{!./src/options/tutorial006.py!}
-```
-
-And it will show up in the help text:
-
-
-
-```console
-$ python main.py
-
-Hello Wade Wilson
-
-// Show the help
-$ python main.py --help
-
-Usage: main.py [OPTIONS]
-
-Options:
- --fullname TEXT [default: Wade Wilson]
- --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.
-```
-
-
-
-!!! tip
- Notice the `[default: Wade Wilson]` in the help text.
-
-## Other uses
-
-`typer.Option()` has several other users. For data validation, to enable other features, etc.
-
-But you will see about that later in the docs.
diff --git a/docs/tutorial/options/help.md b/docs/tutorial/options/help.md
new file mode 100644
index 0000000..688da78
--- /dev/null
+++ b/docs/tutorial/options/help.md
@@ -0,0 +1,100 @@
+In the *First Steps* section you saw how to add help for a CLI app/command by adding it to a function's
docstring.
+
+Here's how that last example looked like:
+
+```Python
+{!./src/first_steps/tutorial006.py!}
+```
+
+Now we'll add a *help* section to the *CLI options*:
+
+```Python hl_lines="6 7"
+{!./src/options/help/tutorial001.py!}
+```
+
+We are replacing the default values we had before with `typer.Option()`.
+
+As we no longer have a default value there, the first parameter to `typer.Option()` serves the same purpose of defining that default value.
+
+So, if we had:
+
+```Python
+lastname: str = ""
+```
+
+now we write:
+
+```Python
+lastname: str = typer.Option("")
+```
+
+And both forms achieve the same: a *CLI option* with a default value of an empty string (`""`).
+
+And then we can pass the `help` keyword parameter:
+
+```Python
+lastname: str = typer.Option("", help="this option does this and that")
+```
+
+to create the help for that *CLI option*.
+
+Copy that example from above to a file `main.py`.
+
+Test it:
+
+
+
+```console
+$ python main.py --help
+
+Usage: main.py [OPTIONS] NAME
+
+ Say hi to NAME, optionally with a --lastname.
+
+ If --formal is used, say hi very formally.
+
+Options:
+ --lastname TEXT Last name of person to greet.
+ --formal / --no-formal Say hi formally.
+ --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.
+
+// Now you have a help text for the --lastname and --formal CLI options š
+```
+
+
+
+## Show default in help
+
+You can tell Typer to show the default value in the help text with `show_default=True`:
+
+```Python hl_lines="4"
+{!./src/options/help/tutorial002.py!}
+```
+
+And it will show up in the help text:
+
+
+
+```console
+$ python main.py
+
+Hello Wade Wilson
+
+// Show the help
+$ python main.py --help
+
+Usage: main.py [OPTIONS]
+
+Options:
+ --fullname TEXT [default: Wade Wilson]
+ --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.
+```
+
+
+
+!!! tip
+ Notice the `[default: Wade Wilson]` in the help text.
diff --git a/docs/tutorial/options/index.md b/docs/tutorial/options/index.md
new file mode 100644
index 0000000..13c4505
--- /dev/null
+++ b/docs/tutorial/options/index.md
@@ -0,0 +1,3 @@
+In the next short sections we will see how to modify *CLI options* using `typer.Option()`.
+
+`typer.Option()` works very similarly to `typer.Argument()`, but has some extra features that we'll see next.
diff --git a/docs/tutorial/options/name.md b/docs/tutorial/options/name.md
new file mode 100644
index 0000000..3e61137
--- /dev/null
+++ b/docs/tutorial/options/name.md
@@ -0,0 +1,328 @@
+By default **Typer** will create a *CLI option* name from the function parameter.
+
+So, if you have a function with:
+
+```Python
+def main(user_name: str = None):
+ pass
+```
+
+or
+
+```Python
+def main(user_name: str = typer.Option(None)):
+ pass
+```
+
+**Typer** will create a *CLI option*:
+
+```
+--user-name
+```
+
+But you can customize it if you want to.
+
+Let's say the function parameter name is `user_name` as above, but you want the *CLI option* to be just `--name`.
+
+You can pass the *CLI option* name that you want to have in the next positional argument passed to `typer.Option()`:
+
+```Python hl_lines="4"
+{!./src/options/name/tutorial001.py!}
+```
+
+Here you are passing the string `"--name"` as the second positional argument to `typer.Option()`.
+
+!!! info
+ "
Positional" means that it's not a function argument with a keyword name.
+
+ For example `show_default=True` is a keyword argument. "`show_default`" is the keyword.
+
+ But in `"--name"` there's no `option_name="--name"` or something similar, it's just the string value `"--name"` that goes in `typer.Option()` after the `...` value passed in the first position.
+
+ That's a "positional argument" in a function.
+
+Check it:
+
+
+
+```console
+$ python main.py --help
+
+// Notice the --name instead of --user-name
+Usage: main.py [OPTIONS]
+
+Options:
+ --name TEXT [required]
+ --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.
+
+// Try it
+$ python --name Camila
+
+Hello Camila
+```
+
+
+
+## *CLI option* short names
+
+A short name is a *CLI option* name with a single dash (`-`) instead of 2 (`--`) and a single letter, like `-n` instead of `--name`.
+
+For example, the `ls` program has a *CLI option* named `--size`, and the same *CLI option* also has a short name `-s`:
+
+
+
+```console
+// With the long name --size
+$ ls ./myproject --size
+
+12 first-steps.md 4 intro.md
+
+// With the short name -s
+$ ls ./myproject -s
+
+12 first-steps.md 4 intro.md
+
+// Both CLI option names do the same
+```
+
+
+
+### *CLI option* short names together
+
+Short names have another feature, when they have a single letter, as in `-s`, you can put several of these *CLI options* together, with a single dash.
+
+For example, the `ls` program has these 2 *CLI options* (among others):
+
+* `--size`: show the sizes of the listed files.
+* `--human`: show a human-readable format, like `1MB` instead of just `1024`.
+
+And these 2 *CLI options* have short versions too:
+
+* `--size`: short version `-s`.
+* `--human`: short version `-h`.
+
+So, you can put them together with `-sh` or `-hs`:
+
+
+
+```console
+// Call ls with long CLI options
+$ ls --size --human
+
+12K first-steps.md 4.0K intro.md
+
+// Now with short versions
+$ ls -s -h
+
+12K first-steps.md 4.0K intro.md
+
+// And with short versions together
+$ ls -sh
+
+12K first-steps.md 4.0K intro.md
+
+// Order in short versions doesn't matter
+$ ls -hs
+
+12K first-steps.md 4.0K intro.md
+
+// They all work the same š
+```
+
+
+
+### *CLI option* short names with values
+
+When you use *CLI options* with short names, you can put them together if they are just boolean flags, like `--size` or `--human`.
+
+But if you have a *CLI option* `--file` with a short name `-f` that takes a value, if you put it with other short names for *CLI options*, you have to put it as the last letter, so that it can receive the value that comes right after.
+
+For example, let's say you are decompressing/extracting a file `myproject.tar.gz` with the program `tar`.
+
+You can pass these *CLI option* short names to `tar`:
+
+* `-x`: means "e`X`tract", to decompress and extract the contents.
+* `-v`: means "`V`erbose", to print on the screen what it is doing, so you can know that it's decompressing each file and can entertain yourself while you wait.
+* `-f`: means "`F`ile", this one requires a value, the compressed file to extract (in our example, this is `myproject.tar.gz`).
+ * So if you use all the short names together, this `-f` has to come last, to receive the value that comes next to it.
+
+For example:
+
+
+
+```console
+$ tar -xvf myproject.tar.gz
+
+myproject/
+myproject/first-steps.md
+myproject/intro.md
+
+// But if you put the -f before
+$ tar -fxv myproject.tar.gz
+
+// You get an ugly error
+tar: You must specify one of the blah, blah, error, error
+```
+
+
+
+### Defining *CLI option* short names
+
+In **Typer** you can also define *CLI option* short names the same way you can customize the long names.
+
+`typer.Option()` receives as a first function argument the default value, e.g. `None`, and all the next *positional* values are to define the *CLI option* name(s).
+
+!!! tip
+ Remember the *positional* function arguments are those that don't have a keyword.
+
+ All the other function arguments/parameters you pass to `typer.Option()` like `prompt=True` and `help="This option blah, blah"` require the keyword.
+
+You can overwrite the *CLI option* name to use as in the previous example, but you can also declare extra alternatives, including short names.
+
+For example, extending the previous example, let's add a *CLI option* short name `-n`:
+
+```Python hl_lines="4"
+{!./src/options/name/tutorial002.py!}
+```
+
+Here we are overwriting the *CLI option* name that by default would be `--user-name`, and we are defining it to be `--name`. And we are also declaring a *CLI option* short name of `-n`.
+
+Check it:
+
+
+
+```console
+// Check the help
+$ python main.py --help
+
+// Notice the two CLI option names -n and --name
+Usage: main.py [OPTIONS]
+
+Options:
+ -n, --name TEXT [required]
+ --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.
+
+// Try the short version
+$ python main.py -n Camila
+
+Hello Camila
+```
+
+
+
+### *CLI option* only short name
+
+If you only declare a short name like `-n` then that will be the only *CLI option* name. And neither `--name` nor `--user-name` will be available.
+
+```Python hl_lines="4"
+{!./src/options/name/tutorial003.py!}
+```
+
+Check it:
+
+
+
+```console
+$ python main.py --help
+
+// Notice there's no --name nor --user-name, only -n
+Usage: main.py [OPTIONS]
+
+Options:
+ -n TEXT [required]
+ --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.
+
+// Try it
+$ python main.py -n Camila
+
+Hello Camila
+```
+
+
+
+### *CLI option* short name and default
+
+Continuing with the example above, as **Typer** allows you to declare a *CLI option* as having only a short name, if you want to have the default long name plus a short name, you have to declare both explicitly:
+
+```Python hl_lines="4"
+{!./src/options/name/tutorial004.py!}
+```
+
+Check it:
+
+
+
+```console
+$ python main.py --help
+
+// Notice that we have the long version --user-name back
+// and we also have the short version -n
+Usage: main.py [OPTIONS]
+
+Options:
+ -n, --user-name TEXT [required]
+ --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.
+
+// Try it
+$ python main.py --user-name Camila
+
+Hello Camila
+
+// And try the short version
+$ python main.py -n Camila
+```
+
+
+
+### *CLI option* short names together
+
+You can create multiple short names and use them together.
+
+You don't have to do anything special for it to work (apart from declaring those short versions):
+
+```Python hl_lines="5 6"
+{!./src/options/name/tutorial005.py!}
+```
+
+!!! tip
+ Notice that, again, we are declaring the long and short version of the *CLI option* names.
+
+Check it:
+
+
+
+```console
+$ python main.py --help
+
+// We now have short versions -n and -f
+// And also long versions --name and --formal
+Usage: main.py [OPTIONS]
+
+Options:
+ -n, --name TEXT [required]
+ -f, --formal
+ --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.
+
+// Try the short versions
+$ python main.py -n Camila -f
+
+Good day Ms. Camila.
+
+// And try the 2 short versions together
+// See how -n has to go last, to be able to get the value
+$ python main.py -fn Camila
+
+Good day Ms. Camila.
+```
+
+
diff --git a/docs/tutorial/options/prompt.md b/docs/tutorial/options/prompt.md
new file mode 100644
index 0000000..70a1c43
--- /dev/null
+++ b/docs/tutorial/options/prompt.md
@@ -0,0 +1,88 @@
+It's also possible to, instead of just showing an error, ask for the missing value with `prompt=True`:
+
+```Python hl_lines="4"
+{!./src/options/prompt/tutorial001.py!}
+```
+
+And then your program will ask the user for it in the terminal:
+
+
+
+```console
+// Call it with the NAME CLI argument
+$ python main.py Camila
+
+// It asks for the missing CLI option --lastname
+# Lastname: $ GutiƩrrez
+
+Hello Camila GutiƩrrez
+```
+
+
+
+## Customize the prompt
+
+You can also set a custom prompt, passing the string that you want to use instead of just `True`:
+
+```Python hl_lines="6"
+{!./src/options/prompt/tutorial002.py!}
+```
+
+And then your program will ask for it using with your custom prompt:
+
+
+
+```console
+// Call it with the NAME CLI argument
+$ python main.py Camila
+
+// It uses the custom prompt
+# Please tell me your last name: $ GutiƩrrez
+
+Hello Camila GutiƩrrez
+```
+
+
+
+## Confirmation prompt
+
+In some cases you could want to prompt for something and then ask the user to confirm it by typing it twice.
+
+You can do it passing the parameter `confirmation_prompt=True`.
+
+Let's say it's a CLI app to delete a project:
+
+```Python hl_lines="4"
+{!./src/options/prompt/tutorial003.py!}
+```
+
+And it will prompt the user for a value and then for the confirmation:
+
+
+
+```console
+$ python main.py
+
+// Your app will first prompt for the project name, and then for the confirmation
+# Project name: $ Old Project
+# Repeat for confirmation: $ Old Project
+
+Deleting project Old Project
+
+// If the user doesn't type the same, receives an error and a new prompt
+$ python main.py
+
+# Project name: $ Old Project
+# Repeat for confirmation: $ New Spice
+
+Error: the two entered values do not match
+
+# Project name: $ Old Project
+# Repeat for confirmation: $ Old Project
+
+Deleting project Old Project
+
+// Now it works š
+```
+
+
diff --git a/docs/tutorial/options/required.md b/docs/tutorial/options/required.md
new file mode 100644
index 0000000..39d52ab
--- /dev/null
+++ b/docs/tutorial/options/required.md
@@ -0,0 +1,56 @@
+We said before that *by default*:
+
+* *CLI options* are **optional**
+* *CLI arguments* are **required**
+
+Well, that's how they work *by default*, and that's the convention in many CLI programs and systems.
+
+But if you really want, you can change that.
+
+To make a *CLI option* required, pass `...` to `typer.Option()`.
+
+!!! info
+ If you hadn't seen that `...` before: it is a a special single value, it is
part of Python and is called "Ellipsis".
+
+That will tell **Typer** that it's still a *CLI option*, but it doesn't have a default value, and it's required.
+
+Let's make `--lastname` a required *CLI option*:
+
+```Python hl_lines="4"
+{!./src/options/required/tutorial001.py!}
+```
+
+And test it:
+
+
+
+```console
+// Pass the NAME CLI argument
+$ python main.py Camila
+
+// We didn't pass the now required --lastname CLI option
+Usage: main.py [OPTIONS] NAME
+Try "main.py --help" for help.
+
+Error: Missing option "--lastname".
+
+// Now update it to pass the required --lastname CLI option
+$ python main.py Camila --lastname GutiƩrrez
+
+Hello Camila GutiƩrrez
+
+// And if you check the help
+$ python main.py --help
+
+Usage: main.py [OPTIONS] NAME
+
+Options:
+ --lastname TEXT [required]
+ --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.
+
+// It now tells you that --lastname is required š
+```
+
+
diff --git a/mkdocs.yml b/mkdocs.yml
index 12a20e2..fe4b480 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -23,9 +23,20 @@ nav:
- Tutorial - User Guide:
- Tutorial - User Guide - Intro: 'tutorial/index.md'
- First Steps: 'tutorial/first-steps.md'
- - CLI Options: 'tutorial/options.md'
- CLI Arguments: 'tutorial/arguments.md'
- - Commands: 'tutorial/commands.md'
+ - CLI Options:
+ - CLI Options Intro: 'tutorial/options/index.md'
+ - CLI Options with Help: 'tutorial/options/help.md'
+ - Required CLI Options: 'tutorial/options/required.md'
+ - CLI Option Prompt: 'tutorial/options/prompt.md'
+ - CLI Option Name: 'tutorial/options/name.md'
+ - Commands:
+ - Commands Intro: 'tutorial/commands/index.md'
+ - Command CLI Arguments: 'tutorial/commands/arguments.md'
+ - Command CLI Options: 'tutorial/commands/options.md'
+ - Command Help: 'tutorial/commands/help.md'
+ - Custom Command Name: 'tutorial/commands/name.md'
+ - One or Multiple Commands: 'tutorial/commands/one-or-multiple.md'
- CLI Parameter Types:
- CLI Parameter Types Intro: 'tutorial/parameter-types/index.md'
- Number: 'tutorial/parameter-types/number.md'
diff --git a/tests/test_tutorial/test_commands/test_arguments/__init__.py b/tests/test_tutorial/test_commands/test_arguments/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/test_tutorial/test_commands/test_tutorial003.py b/tests/test_tutorial/test_commands/test_arguments/test_tutorial001.py
similarity index 95%
rename from tests/test_tutorial/test_commands/test_tutorial003.py
rename to tests/test_tutorial/test_commands/test_arguments/test_tutorial001.py
index 3a5b379..cced821 100644
--- a/tests/test_tutorial/test_commands/test_tutorial003.py
+++ b/tests/test_tutorial/test_commands/test_arguments/test_tutorial001.py
@@ -1,5 +1,5 @@
import subprocess
-from commands import tutorial003 as mod
+from commands.arguments import tutorial001 as mod
from typer.testing import CliRunner
diff --git a/tests/test_tutorial/test_commands/test_help/__init__.py b/tests/test_tutorial/test_commands/test_help/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/test_tutorial/test_commands/test_tutorial005.py b/tests/test_tutorial/test_commands/test_help/test_tutorial001.py
similarity index 98%
rename from tests/test_tutorial/test_commands/test_tutorial005.py
rename to tests/test_tutorial/test_commands/test_help/test_tutorial001.py
index 8771766..eac6950 100644
--- a/tests/test_tutorial/test_commands/test_tutorial005.py
+++ b/tests/test_tutorial/test_commands/test_help/test_tutorial001.py
@@ -1,5 +1,5 @@
import subprocess
-from commands import tutorial005 as mod
+from commands.help import tutorial001 as mod
from typer.testing import CliRunner
diff --git a/tests/test_tutorial/test_commands/test_help/test_tutorial002.py b/tests/test_tutorial/test_commands/test_help/test_tutorial002.py
new file mode 100644
index 0000000..1413247
--- /dev/null
+++ b/tests/test_tutorial/test_commands/test_help/test_tutorial002.py
@@ -0,0 +1,55 @@
+import subprocess
+from commands.help import tutorial002 as mod
+
+from typer.testing import CliRunner
+
+app = mod.app
+
+runner = CliRunner()
+
+
+def test_help():
+ result = runner.invoke(app, ["--help"])
+ assert result.exit_code == 0
+ assert "create" in result.output
+ assert "Create a new user with USERNAME." in result.output
+ assert "delete" in result.output
+ assert "Delete a user with USERNAME." in result.output
+ assert "Some internal utility function to create." not in result.output
+ assert "Some internal utility function to delete." not in result.output
+
+
+def test_help_create():
+ result = runner.invoke(app, ["create", "--help"])
+ assert result.exit_code == 0
+ assert "Create a new user with USERNAME." in result.output
+ assert "Some internal utility function to create." not in result.output
+
+
+def test_help_create():
+ result = runner.invoke(app, ["delete", "--help"])
+ assert result.exit_code == 0
+ assert "Delete a user with USERNAME." in result.output
+ assert "Some internal utility function to delete." not in result.output
+
+
+def test_create():
+ result = runner.invoke(app, ["create", "Camila"])
+ assert result.exit_code == 0
+ assert "Creating user: Camila" in result.output
+
+
+def test_delete():
+ result = runner.invoke(app, ["delete", "Camila"])
+ assert result.exit_code == 0
+ assert "Deleting user: Camila" in result.output
+
+
+def test_script():
+ result = subprocess.run(
+ ["coverage", "run", mod.__file__, "--help"],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ encoding="utf-8",
+ )
+ assert "Usage" in result.stdout
diff --git a/tests/test_tutorial/test_commands/test_index/__init__.py b/tests/test_tutorial/test_commands/test_index/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/test_tutorial/test_commands/test_tutorial001.py b/tests/test_tutorial/test_commands/test_index/test_tutorial001.py
similarity index 93%
rename from tests/test_tutorial/test_commands/test_tutorial001.py
rename to tests/test_tutorial/test_commands/test_index/test_tutorial001.py
index 33135f7..9bf27ce 100644
--- a/tests/test_tutorial/test_commands/test_tutorial001.py
+++ b/tests/test_tutorial/test_commands/test_index/test_tutorial001.py
@@ -1,5 +1,5 @@
import subprocess
-from commands import tutorial001 as mod
+from commands.index import tutorial001 as mod
from typer.testing import CliRunner
diff --git a/tests/test_tutorial/test_commands/test_tutorial002.py b/tests/test_tutorial/test_commands/test_index/test_tutorial002.py
similarity index 95%
rename from tests/test_tutorial/test_commands/test_tutorial002.py
rename to tests/test_tutorial/test_commands/test_index/test_tutorial002.py
index 0a5fc31..c87e404 100644
--- a/tests/test_tutorial/test_commands/test_tutorial002.py
+++ b/tests/test_tutorial/test_commands/test_index/test_tutorial002.py
@@ -1,5 +1,5 @@
import subprocess
-from commands import tutorial002 as mod
+from commands.index import tutorial002 as mod
from typer.testing import CliRunner
diff --git a/tests/test_tutorial/test_commands/test_name/__init__.py b/tests/test_tutorial/test_commands/test_name/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/test_tutorial/test_commands/test_tutorial006.py b/tests/test_tutorial/test_commands/test_name/test_tutorial001.py
similarity index 95%
rename from tests/test_tutorial/test_commands/test_tutorial006.py
rename to tests/test_tutorial/test_commands/test_name/test_tutorial001.py
index 3630db1..9dda088 100644
--- a/tests/test_tutorial/test_commands/test_tutorial006.py
+++ b/tests/test_tutorial/test_commands/test_name/test_tutorial001.py
@@ -1,5 +1,5 @@
import subprocess
-from commands import tutorial006 as mod
+from commands.name import tutorial001 as mod
from typer.testing import CliRunner
diff --git a/tests/test_tutorial/test_commands/test_options/__init__.py b/tests/test_tutorial/test_commands/test_options/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/test_tutorial/test_commands/test_tutorial004.py b/tests/test_tutorial/test_commands/test_options/test_tutorial001.py
similarity index 97%
rename from tests/test_tutorial/test_commands/test_tutorial004.py
rename to tests/test_tutorial/test_commands/test_options/test_tutorial001.py
index a2e26c7..573725b 100644
--- a/tests/test_tutorial/test_commands/test_tutorial004.py
+++ b/tests/test_tutorial/test_commands/test_options/test_tutorial001.py
@@ -1,5 +1,5 @@
import subprocess
-from commands import tutorial004 as mod
+from commands.options import tutorial001 as mod
from typer.testing import CliRunner
diff --git a/tests/test_tutorial/test_options/test_help/__init__.py b/tests/test_tutorial/test_options/test_help/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/test_tutorial/test_options/test_tutorial001.py b/tests/test_tutorial/test_options/test_help/test_tutorial001.py
similarity index 96%
rename from tests/test_tutorial/test_options/test_tutorial001.py
rename to tests/test_tutorial/test_options/test_help/test_tutorial001.py
index 835ed58..28b77e2 100644
--- a/tests/test_tutorial/test_options/test_tutorial001.py
+++ b/tests/test_tutorial/test_options/test_help/test_tutorial001.py
@@ -3,7 +3,7 @@ import subprocess
import typer
from typer.testing import CliRunner
-from options import tutorial001 as mod
+from options.help import tutorial001 as mod
runner = CliRunner()
diff --git a/tests/test_tutorial/test_options/test_tutorial006.py b/tests/test_tutorial/test_options/test_help/test_tutorial002.py
similarity index 94%
rename from tests/test_tutorial/test_options/test_tutorial006.py
rename to tests/test_tutorial/test_options/test_help/test_tutorial002.py
index df08073..1a9aa44 100644
--- a/tests/test_tutorial/test_options/test_tutorial006.py
+++ b/tests/test_tutorial/test_options/test_help/test_tutorial002.py
@@ -3,7 +3,7 @@ import subprocess
import typer
from typer.testing import CliRunner
-from options import tutorial006 as mod
+from options.help import tutorial002 as mod
runner = CliRunner()
diff --git a/tests/test_tutorial/test_options/test_name/__init__.py b/tests/test_tutorial/test_options/test_name/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/test_tutorial/test_options/test_name/test_tutorial001.py b/tests/test_tutorial/test_options/test_name/test_tutorial001.py
new file mode 100644
index 0000000..0095441
--- /dev/null
+++ b/tests/test_tutorial/test_options/test_name/test_tutorial001.py
@@ -0,0 +1,34 @@
+import subprocess
+
+import typer
+from typer.testing import CliRunner
+
+from options.name import tutorial001 as mod
+
+runner = CliRunner()
+
+app = typer.Typer()
+app.command()(mod.main)
+
+
+def test_option_help():
+ result = runner.invoke(app, ["--help"])
+ assert result.exit_code == 0
+ assert "--name TEXT" in result.output
+ assert "--user-name" not in result.output
+
+
+def test_call():
+ result = runner.invoke(app, ["--name", "Camila"])
+ assert result.exit_code == 0
+ assert "Hello Camila" in result.output
+
+
+def test_script():
+ result = subprocess.run(
+ ["coverage", "run", mod.__file__, "--help"],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ encoding="utf-8",
+ )
+ assert "Usage" in result.stdout
diff --git a/tests/test_tutorial/test_options/test_name/test_tutorial002.py b/tests/test_tutorial/test_options/test_name/test_tutorial002.py
new file mode 100644
index 0000000..7e3ca59
--- /dev/null
+++ b/tests/test_tutorial/test_options/test_name/test_tutorial002.py
@@ -0,0 +1,40 @@
+import subprocess
+
+import typer
+from typer.testing import CliRunner
+
+from options.name import tutorial002 as mod
+
+runner = CliRunner()
+
+app = typer.Typer()
+app.command()(mod.main)
+
+
+def test_option_help():
+ result = runner.invoke(app, ["--help"])
+ assert result.exit_code == 0
+ assert "-n, --name TEXT" in result.output
+ assert "--user-name" not in result.output
+
+
+def test_call():
+ result = runner.invoke(app, ["-n", "Camila"])
+ assert result.exit_code == 0
+ assert "Hello Camila" in result.output
+
+
+def test_call_long():
+ result = runner.invoke(app, ["--name", "Camila"])
+ assert result.exit_code == 0
+ assert "Hello Camila" in result.output
+
+
+def test_script():
+ result = subprocess.run(
+ ["coverage", "run", mod.__file__, "--help"],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ encoding="utf-8",
+ )
+ assert "Usage" in result.stdout
diff --git a/tests/test_tutorial/test_options/test_name/test_tutorial003.py b/tests/test_tutorial/test_options/test_name/test_tutorial003.py
new file mode 100644
index 0000000..647801e
--- /dev/null
+++ b/tests/test_tutorial/test_options/test_name/test_tutorial003.py
@@ -0,0 +1,35 @@
+import subprocess
+
+import typer
+from typer.testing import CliRunner
+
+from options.name import tutorial003 as mod
+
+runner = CliRunner()
+
+app = typer.Typer()
+app.command()(mod.main)
+
+
+def test_option_help():
+ result = runner.invoke(app, ["--help"])
+ assert result.exit_code == 0
+ assert "-n TEXT" in result.output
+ assert "--user-name" not in result.output
+ assert "--name" not in result.output
+
+
+def test_call():
+ result = runner.invoke(app, ["-n", "Camila"])
+ assert result.exit_code == 0
+ assert "Hello Camila" in result.output
+
+
+def test_script():
+ result = subprocess.run(
+ ["coverage", "run", mod.__file__, "--help"],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ encoding="utf-8",
+ )
+ assert "Usage" in result.stdout
diff --git a/tests/test_tutorial/test_options/test_name/test_tutorial004.py b/tests/test_tutorial/test_options/test_name/test_tutorial004.py
new file mode 100644
index 0000000..a4c4602
--- /dev/null
+++ b/tests/test_tutorial/test_options/test_name/test_tutorial004.py
@@ -0,0 +1,40 @@
+import subprocess
+
+import typer
+from typer.testing import CliRunner
+
+from options.name import tutorial004 as mod
+
+runner = CliRunner()
+
+app = typer.Typer()
+app.command()(mod.main)
+
+
+def test_option_help():
+ result = runner.invoke(app, ["--help"])
+ assert result.exit_code == 0
+ assert "-n, --user-name TEXT" in result.output
+ assert "--name" not in result.output
+
+
+def test_call():
+ result = runner.invoke(app, ["-n", "Camila"])
+ assert result.exit_code == 0
+ assert "Hello Camila" in result.output
+
+
+def test_call_long():
+ result = runner.invoke(app, ["--user-name", "Camila"])
+ assert result.exit_code == 0
+ assert "Hello Camila" in result.output
+
+
+def test_script():
+ result = subprocess.run(
+ ["coverage", "run", mod.__file__, "--help"],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ encoding="utf-8",
+ )
+ assert "Usage" in result.stdout
diff --git a/tests/test_tutorial/test_options/test_name/test_tutorial005.py b/tests/test_tutorial/test_options/test_name/test_tutorial005.py
new file mode 100644
index 0000000..fd30298
--- /dev/null
+++ b/tests/test_tutorial/test_options/test_name/test_tutorial005.py
@@ -0,0 +1,51 @@
+import subprocess
+
+import typer
+from typer.testing import CliRunner
+
+from options.name import tutorial005 as mod
+
+runner = CliRunner()
+
+app = typer.Typer()
+app.command()(mod.main)
+
+
+def test_option_help():
+ result = runner.invoke(app, ["--help"])
+ assert result.exit_code == 0
+ assert "-n, --name TEXT" in result.output
+ assert "-f, --formal" in result.output
+
+
+def test_call():
+ result = runner.invoke(app, ["-n", "Camila"])
+ assert result.exit_code == 0
+ assert "Hello Camila" in result.output
+
+
+def test_call_formal():
+ result = runner.invoke(app, ["-n", "Camila", "-f"])
+ assert result.exit_code == 0
+ assert "Good day Ms. Camila." in result.output
+
+
+def test_call_formal_condensed():
+ result = runner.invoke(app, ["-fn", "Camila"])
+ assert result.exit_code == 0
+ assert "Good day Ms. Camila." in result.output
+
+
+def test_call_condensed_wrong_order():
+ result = runner.invoke(app, ["-nf", "Camila"])
+ assert result.exit_code != 0
+
+
+def test_script():
+ result = subprocess.run(
+ ["coverage", "run", mod.__file__, "--help"],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ encoding="utf-8",
+ )
+ assert "Usage" in result.stdout
diff --git a/tests/test_tutorial/test_options/test_prompt/__init__.py b/tests/test_tutorial/test_options/test_prompt/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/test_tutorial/test_options/test_tutorial003.py b/tests/test_tutorial/test_options/test_prompt/test_tutorial001.py
similarity index 95%
rename from tests/test_tutorial/test_options/test_tutorial003.py
rename to tests/test_tutorial/test_options/test_prompt/test_tutorial001.py
index 129e5d0..96a6adb 100644
--- a/tests/test_tutorial/test_options/test_tutorial003.py
+++ b/tests/test_tutorial/test_options/test_prompt/test_tutorial001.py
@@ -3,7 +3,7 @@ import subprocess
import typer
from typer.testing import CliRunner
-from options import tutorial003 as mod
+from options.prompt import tutorial001 as mod
runner = CliRunner()
diff --git a/tests/test_tutorial/test_options/test_tutorial004.py b/tests/test_tutorial/test_options/test_prompt/test_tutorial002.py
similarity index 95%
rename from tests/test_tutorial/test_options/test_tutorial004.py
rename to tests/test_tutorial/test_options/test_prompt/test_tutorial002.py
index 7946248..95fe480 100644
--- a/tests/test_tutorial/test_options/test_tutorial004.py
+++ b/tests/test_tutorial/test_options/test_prompt/test_tutorial002.py
@@ -3,7 +3,7 @@ import subprocess
import typer
from typer.testing import CliRunner
-from options import tutorial004 as mod
+from options.prompt import tutorial002 as mod
runner = CliRunner()
diff --git a/tests/test_tutorial/test_options/test_tutorial005.py b/tests/test_tutorial/test_options/test_prompt/test_tutorial003.py
similarity index 96%
rename from tests/test_tutorial/test_options/test_tutorial005.py
rename to tests/test_tutorial/test_options/test_prompt/test_tutorial003.py
index 3b4557a..076b4ee 100644
--- a/tests/test_tutorial/test_options/test_tutorial005.py
+++ b/tests/test_tutorial/test_options/test_prompt/test_tutorial003.py
@@ -3,7 +3,7 @@ import subprocess
import typer
from typer.testing import CliRunner
-from options import tutorial005 as mod
+from options.prompt import tutorial003 as mod
runner = CliRunner()
diff --git a/tests/test_tutorial/test_options/test_required/__init__.py b/tests/test_tutorial/test_options/test_required/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/test_tutorial/test_options/test_tutorial002.py b/tests/test_tutorial/test_options/test_required/test_tutorial002.py
similarity index 94%
rename from tests/test_tutorial/test_options/test_tutorial002.py
rename to tests/test_tutorial/test_options/test_required/test_tutorial002.py
index 77c880a..b9ed2e9 100644
--- a/tests/test_tutorial/test_options/test_tutorial002.py
+++ b/tests/test_tutorial/test_options/test_required/test_tutorial002.py
@@ -3,7 +3,7 @@ import subprocess
import typer
from typer.testing import CliRunner
-from options import tutorial002 as mod
+from options.required import tutorial001 as mod
runner = CliRunner()
diff --git a/typer/params.py b/typer/params.py
index 820a0c5..3ee37de 100644
--- a/typer/params.py
+++ b/typer/params.py
@@ -106,7 +106,7 @@ def Option(
def Argument(
# Parameter
default: Optional[Any],
- *param_decls: str,
+ *,
callback: Optional[Callable[[click.Context, click.Parameter, str], Any]] = None,
metavar: Optional[str] = None,
expose_value: bool = True,
@@ -142,7 +142,9 @@ def Argument(
return ArgumentInfo(
# Parameter
default=default,
- param_decls=param_decls,
+ # Arguments can only have one param declaration
+ # it will be generated from the param name
+ param_decls=None,
callback=callback,
metavar=metavar,
expose_value=expose_value,