Command-line tools are a developer's bread and butter. Python's typer and rich libraries make building them surprisingly pleasant.
Setup
pip install typer rich
Your First CLI with Typer
# cli.py
import typer
app = typer.Typer()
@app.command()
def hello(name: str, count: int = 1):
"""Greet someone multiple times."""
for _ in range(count):
print(f"Hello, {name}!")
if __name__ == "__main__":
app()
python cli.py Alice --count 3
# Hello, Alice!
# Hello, Alice!
# Hello, Alice!
python cli.py --help
# Usage: cli.py [OPTIONS] NAME
# Greet someone multiple times.
Typer automatically generates help text, validates types, and handles errors.
Multiple Commands
import typer
from typing import Optional
app = typer.Typer(help="Project management CLI")
@app.command()
def init(name: str, template: str = "default"):
"""Initialize a new project."""
print(f"Creating project '{name}' with template '{template}'")
@app.command()
def build(target: str = "all", verbose: bool = False):
"""Build the project."""
if verbose:
print(f"Building target: {target} (verbose mode)")
else:
print(f"Building {target}...")
@app.command()
def deploy(env: str = typer.Option("staging", help="Target environment")):
"""Deploy to an environment."""
confirm = typer.confirm(f"Deploy to {env}?")
if confirm:
print(f"Deploying to {env}...")
if __name__ == "__main__":
app()
Adding Rich for Beautiful Output
import typer
from rich.console import Console
from rich.table import Table
from rich.progress import track
from rich import print as rprint
import time
console = Console()
app = typer.Typer()
@app.command()
def status():
"""Show project status."""
table = Table(title="Service Status")
table.add_column("Service", style="cyan")
table.add_column("Status", style="green")
table.add_column("Uptime", style="yellow")
table.add_row("API", "✅ Running", "14d 3h")
table.add_row("Database", "✅ Running", "14d 3h")
table.add_row("Cache", "⚠️ Degraded", "2h 15m")
table.add_row("Worker", "❌ Down", "0m")
console.print(table)
@app.command()
def process(files: list[str]):
"""Process files with a progress bar."""
for f in track(files, description="Processing..."):
time.sleep(0.5) # simulate work
rprint("[bold green]All files processed![/bold green]")
if __name__ == "__main__":
app()
Interactive Prompts
@app.command()
def configure():
"""Interactive configuration setup."""
name = typer.prompt("Project name")
version = typer.prompt("Version", default="0.1.0")
use_docker = typer.confirm("Use Docker?", default=True)
console.print(f"[bold]Configuration:[/bold]")
console.print(f" Name: [cyan]{name}[/cyan]")
console.print(f" Version: [cyan]{version}[/cyan]")
console.print(f" Docker: [cyan]{use_docker}[/cyan]")
Error Handling with Style
@app.command()
def deploy(env: str):
"""Deploy with proper error handling."""
valid_envs = ["staging", "production", "dev"]
if env not in valid_envs:
console.print(f"[bold red]Error:[/bold red] Invalid environment '{env}'")
console.print(f"Valid options: {', '.join(valid_envs)}")
raise typer.Exit(code=1)
with console.status(f"[bold green]Deploying to {env}..."):
time.sleep(3) # simulate deployment
console.print(f"[bold green]✅ Deployed to {env} successfully![/bold green]")
Packaging Your CLI
# pyproject.toml
[project.scripts]
mycli = "mycli.main:app"
pip install -e .
mycli status
mycli deploy --env production
Key Takeaways
- Typer gives you argument parsing, validation, and help text for free
- Rich makes terminal output beautiful with tables, progress bars, and colors
- Use
typer.confirm()for dangerous operations - Package with
pyproject.tomlfor easy distribution - Combine both libraries for professional-grade CLI tools
Your CLI tools deserve to look as good as your web apps.
🚀 Level up your AI workflow! Check out my AI Developer Mega Prompt Pack — 80 battle-tested prompts for developers. $9.99
Top comments (0)