Python
Quick reference for Python patterns and tools
Building CLI Tools with Typer
Typer is the default choice for CLI tools. Built on Click, but with type hints instead of decorators for arguments.
uv add typer
import typer
app = typer.Typer(help="My CLI tool")
@app.command()
def greet(
name: str = typer.Argument(..., help="Name to greet"),
loud: bool = typer.Option(False, "--loud", "-l", help="Shout it"),
):
"""Greet someone."""
msg = f"Hello, {name}!"
print(msg.upper() if loud else msg)
if __name__ == "__main__":
app()
For subcommands, create separate Typer apps and register them:
# main.py
from myapp.db.cli import app as db_app
app.add_typer(db_app, name="db")
# myapp/db/cli.py
app = typer.Typer(help="Database commands")
@app.command("ping")
def ping():
print("PONG")
Typer over argparse: less boilerplate. Typer over Click: type hints are the API.
Joblib Memory for Disk-Based Caching
Use joblib.Memory for caching expensive function results to disk. Automatic invalidation based on function arguments. Cache persists across process restarts.
Cache Location
Cache directory MUST use Path for portability. Hardcoded strings break across environments.
# Bad
memory = Memory("cache")
memory = Memory("/tmp/cache")
# Good
from pathlib import Path
CACHE_DIR = Path.home() / ".cache" / "myapp"
memory = Memory(CACHE_DIR, verbose=0)
Function Arguments
All arguments to cached functions MUST be hashable. Strings, numbers, tuples work. Lists and dicts do not.
# Bad - list is not hashable
@memory.cache
def process(items: list) -> dict:
...
# Good - tuple is hashable
@memory.cache
def process(items: tuple) -> dict:
...
Joblib vs lru_cache
Use joblib.Memory when cache MUST persist across runs. Use functools.lru_cache for in-memory caching within a single process.
| Requirement | Tool |
|---|---|
| Persist across runs | joblib.Memory |
| In-memory only | functools.lru_cache |
| Large return values (pickled to disk) | joblib.Memory |
| Fast, small results | functools.lru_cache |
Cache Invalidation
Clear cache explicitly when source data changes. Joblib does not detect external changes.
memory.clear() # Clear all cached functions
fetch_data.clear() # Clear specific function only