Overview
Typer is a library for building CLI applications that users will love using and developers will love creating. It leverages Python type hints to define the interface of your command-line tools, providing automatic validation, serialization, and beautiful help documentation with minimal boilerplate.
Built on top of Click, Typer provides a more modern, type-safe API while remaining compatible with the vast Click ecosystem.
The Problem
Creating command-line interfaces in Python often involves a trade-off:
- Manual parsing (using
sys.argv): Error-prone, tedious, and lacks documentation. - Standard libraries (like
argparse): Verbose, requires repetitive definitions of types and help strings, and doesn't leverage modern Python type safety. - Complex frameworks: High learning curve and often separate the CLI definition from the actual logic.
Typer solves this by making your code the definition. By using type hints, you define your logic and your CLI interface simultaneously.
Core Mental Model: Your Function is Your CLI
In Typer, a CLI command is just a regular Python function.
- Function Name: Becomes the command name.
- Parameters: Become CLI arguments or options.
- Type Hints: Handle data conversion (e.g., string to
intorPath) and validation. - Docstrings: Automatically become the help documentation.
Think of Typer as a "translator" that maps terminal input directly to your function's arguments.
How It Works
- Declaration: you create a
typer.Typerinstance or usetyper.run(). - Registration: you decorate functions with
@app.command()to register them as subcommands. - Configuration: you use
typing.Annotatedwithtyper.Argumentortyper.Optionto add metadata like help text or environment variable overrides. - Execution: when the app runs, Typer parses the command line, validates the inputs against your type hints, and calls your function with the parsed values.
- Rich Output: if Rich Help Formatting is installed, Typer automatically formats help screens and error messages with colors and panels.
Use Cases
Simple One-Command Script
For quick scripts, typer.run turns a function into a CLI immediately.
import typer
def main(name: str, count: int = 1):
for _ in range(count):
print(f"Hello {name}")
if __name__ == "__main__":
typer.run(main)
Multi-Command Application
Use the Typer class to build complex tools with subcommands.
import typer
app = typer.Typer()
@app.command()
def create(username: str):
print(f"Creating user: {username}")
@app.command()
def delete(username: str, force: bool = False):
if force:
print(f"Forcibly deleting user: {username}")
else:
print(f"Deleting user: {username}")
if __name__ == "__main__":
app()
Advanced Parameter Configuration
Use Annotated to define complex requirements like environment variables or numeric ranges.
from typing import Annotated
import typer
app = typer.Typer()
@app.command()
def process(
items: Annotated[int, typer.Argument(help="Number of items", min=1, max=100)],
config: Annotated[str, typer.Option(envvar="APP_CONFIG")] = "default.ini"
):
print(f"Processing {items} items with config {config}")
When to Use Typer
- Use it when you want to build professional, user-friendly CLIs quickly.
- Use it when you are already using type hints and want to avoid duplicating definitions.
- Use it when you need subcommands, automatic shell completion, or integration with environment variables.
When Not to Use Typer
- Avoid it if you are targeting environments with extremely strict dependency limits (Typer depends on Click and others).
- Avoid it if you need to build a non-standard CLI that doesn't follow the "command/subcommand" pattern (though Click's flexibility usually covers this).
Integration & Stack Compatibility
- Click: Typer is built on Click. You can mix Typer apps with Click objects.
- Rich: Automatically used for beautiful terminal formatting if installed.
- Shellingham: Used to detect the current shell for automatic completion installation.
- Pydantic: Often used alongside Typer for complex data validation within the command logic.
Getting Started Pointers
- Getting Started: Learn how to create your first Typer app.
- Organizing Subcommands and Groups: Organize your CLI into multiple commands.
- Parameters and Inputs: See how Typer handles Enums, Paths, and UUIDs.
- Testing: Use the
typer.testing.CliRunnerto test your CLI.
Limitations
- Type Hint Dependency: Typer relies heavily on type hints; if your codebase doesn't use them, you won't get the full benefits.
- Startup Overhead: While minimal, the inspection of functions at runtime adds a small overhead compared to raw
argparse.
FAQ
- Is Typer faster than Click? It's built on Click, so the execution speed is nearly identical. The development speed is where Typer shines.
- Can I use it with async functions? Typer itself is synchronous. To run async code, you typically call
anyio.run()orasyncio.run()inside your command function. - How do I disable Rich formatting? Set the environment variable
TYPER_USE_RICH=0. - Does it support shell completion? Yes, Typer can automatically install completion for Bash, Zsh, Fish, and PowerShell.