Migrating from Typer¶
When this path fits¶
Use this path when typed Typer commands and dependency injection are already valuable. Install the optional adapter so Typer remains optional for Click-only consumers:
python -m pip install 'base-cli[typer]'
Incremental change¶
Before, Typer owns the process entry point:
import typer
app = typer.Typer()
@app.command()
def status(verbose: bool = typer.Option(False, "--verbose")) -> None:
typer.echo("ready" if not verbose else "ready (verbose)")
if __name__ == "__main__":
app()
After, retain the Typer declarations and attach the materialized command:
import base_cli
import typer
app = typer.Typer()
@app.command()
def status(verbose: bool = typer.Option(False, "--verbose")) -> None:
context = base_cli.get_current_context()
context.log.info("status requested")
typer.echo("ready" if not verbose else "ready (verbose)")
command = base_cli.attach_typer(app, name="example")
if __name__ == "__main__":
raise SystemExit(base_cli.run_app(command))
Typer remains responsible for decorators, type-driven parameters, nested apps,
help, completion, dependency injection, and Typer/Click exceptions. The
adapter returns Typer's generated Click command; base-cli owns the lifecycle,
context, logging, redaction, runtime state, cleanup, history hooks, and output
contracts. For custom configuration use base_cli.attach_typer(app,
app=base_cli.App(...)).
Verification and rollback¶
- [ ] Test the supported Typer and Python matrix, including the vendored Click boundary used by newer Typer releases.
- [ ] Compare typed defaults, callback injection, help, completion, and exit codes with the original app.
- [ ] Add contract fixtures and exercise debug, quiet, JSON, and retained diagnostics.
- [ ] Keep the old Typer entry point available until the wheel-first pilot is green; restore it to roll back without rewriting command code.
See typer-adapter.md for adapter details.