Skip to main content

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:

  1. ArgumentInfo: A metadata container (defined in typer.models) that stores the configuration for an argument. When you call typer.Argument(), it returns an instance of this class.
  2. ParameterInfo: The base class for ArgumentInfo, containing shared logic for defaults, callbacks, and environment variables.
  3. TyperArgument: The actual implementation class (defined in typer.core) that inherits from click.core.Argument. It translates the metadata in ArgumentInfo into 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}")