Creating Your First Parameterized Command
In this tutorial, you will build a command-line tool for managing user accounts. You will learn how to use positional arguments for required data and options for optional settings, while enhancing the user experience with help text, environment variables, and interactive prompts.
By the end of this guide, you will have a functional user-create command that demonstrates the power of Typer's parameter system.
Prerequisites
To follow this tutorial, you need:
- Python 3.10 or later (to use the
Annotatedsyntax). - The
typerpackage installed in your environment.
Step 1: Define a Basic Positional Argument
In Typer, positional arguments are parameters that the user must provide in a specific order. You define these using typer.Argument, which internally creates an ArgumentInfo object to store your configuration.
Create a file named main.py:
import typer
from typing import Annotated
app = typer.Typer()
@app.command()
def create(
username: Annotated[str, typer.Argument(help="The unique username for the new account")]
):
print(f"Creating user: {username}")
if __name__ == "__main__":
app()
When you run python main.py create --help, Typer uses the TyperArgument class to generate a help screen that includes your custom help text. The username parameter is required because it has no default value.
Step 2: Add Optional Parameters with CLI Options
Options are parameters prefixed with -- (or -). They are typically optional. You define them using typer.Option, which creates an OptionInfo model.
Update your create function:
@app.command()
def create(
username: Annotated[str, typer.Argument(help="The unique username for the new account")],
fullname: Annotated[str, typer.Option(help="The user's full name")] = "Anonymous",
):
print(f"Creating user: {username} (Full Name: {fullname})")
By setting a default value of "Anonymous", you make fullname optional. If the user doesn't provide --fullname, Typer uses the default.
Step 3: Use Environment Variables and Rich Help Panels
You can configure parameters to read from environment variables if they aren't provided on the command line. You can also group parameters in the help output using rich_help_panel.
@app.command()
def create(
username: Annotated[
str,
typer.Argument(
help="The unique username",
envvar="USER_MANAGER_NAME"
)
],
fullname: Annotated[str, typer.Option(help="The user's full name")] = "Anonymous",
organization: Annotated[
str,
typer.Option(
help="The department or org name",
rich_help_panel="Metadata"
)
] = "Internal",
):
print(f"Creating user: {username} at {organization}")
The TyperArgument class handles the envvar logic, allowing you to run USER_MANAGER_NAME=jdoe python main.py create without passing the username as a CLI argument. The rich_help_panel attribute in OptionInfo tells Typer to group the "organization" option under a "Metadata" header in the help text.
Step 4: Implement Interactive Prompts and Boolean Flags
For sensitive data like passwords, you don't want users typing them in plain sight. TyperOption supports interactive prompts and input hiding. Additionally, Typer handles boolean flags automatically.
@app.command()
def create(
username: Annotated[str, typer.Argument(help="The unique username")],
password: Annotated[
str,
typer.Option(
prompt=True,
confirmation_prompt=True,
hide_input=True,
help="The account password"
)
],
admin: Annotated[
bool,
typer.Option("--admin/--no-admin", help="Grant administrator privileges")
] = False,
):
role = "Admin" if admin else "User"
print(f"Creating {role}: {username}")
In this step:
- Prompts:
prompt=Truetells Typer to ask for the password if it's missing.hide_input=Trueensures the characters aren't echoed to the terminal. - Confirmation:
confirmation_prompt=Trueforces the user to type the password twice to prevent typos. - Boolean Flags: The string
"--admin/--no-admin"allows the user to explicitly set the flag to true or false.
Final Result
Your complete main.py should look like this:
import typer
from typing import Annotated
app = typer.Typer()
@app.command()
def create(
username: Annotated[
str,
typer.Argument(help="The unique username", envvar="USER_MANAGER_NAME")
],
password: Annotated[
str,
typer.Option(
prompt=True,
confirmation_prompt=True,
hide_input=True,
help="The account password"
)
],
fullname: Annotated[str, typer.Option(help="The user's full name")] = "Anonymous",
admin: Annotated[
bool,
typer.Option("--admin/--no-admin", help="Grant administrator privileges")
] = False,
organization: Annotated[
str,
typer.Option(help="The department or org name", rich_help_panel="Metadata")
] = "Internal",
):
"""
Create a new user in the system with the specified configuration.
"""
role = "Admin" if admin else "User"
print(f"Creating {role}: {username} ({fullname})")
print(f"Organization: {organization}")
if __name__ == "__main__":
app()
How it Works
ArgumentInfo&OptionInfo: When you calltyper.Argument()ortyper.Option(), you are creating these model objects. They hold all the metadata (help text, default values, prompts) that you defined.TyperArgument&TyperOption: During execution, Typer inspects your function signature and converts theArgumentInfo/OptionInfodata intoTyperArgumentandTyperOptioninstances. These classes extend Click's core functionality to handle Typer-specific features like Rich formatting and enhanced boolean logic.
Next Steps
Now that you've mastered basic parameters, try exploring Parameter Callbacks to validate input data or Custom Types to parse complex strings into Python objects.