Engineer production-grade Python CLI tools with UV for package management, Ruff for linting, Pyright for strict typing, Typer for commands, and Rich for polished output. Addresses fail-fast patterns, pydantic-settings configuration, modular code organization, and professional UX conventions. Apply when creating admin utilities, data processors, or developer tooling.
Engineer production-grade Python CLI tools with UV for package management, Ruff for linting, Pyright for strict typing, Typer for commands, and Rich for polished output. Addresses fail-fast patterns, pydantic-settings configuration, modular code organization, and professional UX conventions. Apply when creating admin utilities, data processors, or developer tooling.
Python CLI Engineering
Modern patterns for building production-grade Python command-line applications.
When to Use This Skill
Use when building:
Command-line tools and utilities
Data processing applications
Admin/operations tooling
Developer utilities
ETL/sync scripts with user interaction
Analysis tools with formatted output
Database management CLIs
Technology Stack Overview
Core Tools
UV - Package manager (10-100x faster than pip/poetry)
# Custom exceptions (core/exceptions.py)classAppError(Exception):
"""Base exception - let it bubble up."""pass# CLI main entry (cli/main.py)defmain() -> None:
"""Main entry point - ONLY catch at top level."""try:
app()
except AppError as e:
console.print(f"[red]ERROR: {e}[/red]")
raise typer.Exit(code=1) from e
Key Rules:
❌ Never: except Exception: pass or except: return None
✅ Always: Let exceptions bubble to main entry point
✅ Always: Use specific exception types for different failure modes
@app.command()defsync(
config_file: Path = typer.Option("config.yaml"),
verbose: bool = typer.Option(False, "--verbose", "-v"),
) -> None:
"""Sync data from source."""
settings = Settings(_env_file=".env")
if verbose:
console.print(f"[yellow]Connecting to {settings.db_host}[/yellow]")
# Implementation
Database Connection with SQLAlchemy
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, declarative_base
Base = declarative_base()
engine = create_engine(f"postgresql://{settings.db_user}:{settings.db_password}@{settings.db_host}/{settings.db_name}")
Session = sessionmaker(bind=engine)
Rich Progress Bar
from rich.progress import track
for item in track(items, description="Processing..."):
process(item)
Error Handling
Custom Exception Hierarchy
classAppError(Exception):
"""Base for all app exceptions."""passclassConfigurationError(AppError):
"""Config missing/invalid."""passclassDatabaseError(AppError):
"""DB operation failed."""passclassExternalServiceError(AppError):
"""External API failed."""pass
Usage in Commands
@app.command()defprocess() -> None:
"""Process data."""ifnot settings.api_key:
raise ConfigurationError("API_KEY not set in .env")
try:
response = api.fetch_data()
except Exception as e:
raise ExternalServiceError(f"API fetch failed: {e}") from e
# Process response (let exceptions bubble)