CLI Arguments
In Typer, positional arguments are defined by the parameters of your command functions. While standard Python function parameters provide the basic structure, Typer uses the typer.Argument function and the TyperArgument class to extend these parameters with CLI-specific metadata like help text, environment variable support, and custom help formatting.
Core Concepts
The implementation of CLI arguments in this codebase relies on three primary classes:
ArgumentInfo: A metadata container (defined intyper.models) that stores the configuration for an argument. When you calltyper.Argument(), it returns an instance of this class.ParameterInfo: The base class forArgumentInfo, containing shared logic for defaults, callbacks, and environment variables.TyperArgument: The actual implementation class (defined intyper.core) that inherits fromclick.core.Argument. It translates the metadata inArgumentInfointo a format that the underlying Click engine can execute.
Defining Arguments
The most common way to define an argument with metadata is using Python's Annotated type hint. This allows you to keep the function signature clean while providing extra information to Typer.
from typing import Annotated
import typer
app = typer.Typer()
@app.command()
def main(name: Annotated[str, typer.Argument(help="The name of the user to greet")]):
print(f"Hello {name}")
In this example, name becomes a required positional argument. The help string is stored in ArgumentInfo and later used by TyperArgument to generate the help output.
Help Text and Metavars
Standard Click positional arguments do not support help text. Typer solves this by overriding the get_help_record method in the TyperArgument class.
Customizing the Help Name
By default, Typer uses the parameter name in uppercase as the "metavar" (the placeholder name in help text). You can customize this using the metavar parameter in typer.Argument:
@app.command()
def main(name: Annotated[str, typer.Argument(metavar="✨username✨")]):
print(f"Hello {name}")
How Help is Generated
The TyperArgument.get_help_record method in typer/core.py is responsible for constructing the help line. It combines the metavar, the help description, and any "extra" information like default values or environment variables:
# From typer/core.py: TyperArgument.get_help_record
name = self.make_metavar(ctx=ctx)
help = self.help or ""
# ... logic to append [default: X] or [env var: Y] ...
return name, help
Optional and Required Arguments
Arguments are required by default. An argument becomes optional if you provide a default value, either through the function signature or the default parameter in typer.Argument.
@app.command()
def main(
# Required argument
lastname: str,
# Optional argument with a default
name: Annotated[str, typer.Argument()] = "World"
):
print(f"Hello {name} {lastname}")
Internally, TyperArgument.make_metavar (in typer/core.py) automatically wraps optional arguments in square brackets (e.g., [NAME]) in the help output to indicate they are not required.
Environment Variables
You can configure an argument to pull its value from an environment variable using the envvar parameter. This is handled by the ParameterInfo base class.
@app.command()
def main(name: Annotated[str, typer.Argument(envvar="AWESOME_NAME")] = "World"):
print(f"Hello Mr. {name}")
If the user does not provide the argument on the command line, Typer will check the AWESOME_NAME environment variable before falling back to the default value "World".
Advanced Help Formatting
Typer provides additional tools for organizing how arguments appear in the terminal, especially when using the Rich-enhanced help output.
Help Panels
The rich_help_panel parameter allows you to group arguments into distinct sections in the help output. This is useful for commands with many positional arguments.
@app.command()
def main(
name: Annotated[str, typer.Argument(help="Who to greet")],
lastname: Annotated[
str, typer.Argument(help="The last name", rich_help_panel="Secondary Arguments")
] = "",
):
print(f"Hello {name} {lastname}")
Hidden Arguments
If you need an argument to exist but not show up in the --help output, you can set hidden=True. The TyperArgument.get_help_record method checks this attribute and returns None if the argument is hidden, effectively removing it from the help text.
@app.command()
def main(
name: Annotated[str, typer.Argument(hidden=True)] = "secret-user"
):
print(f"Hello {name}")