[cli] refactor: migrate to typer 0.26 (vendored click)#248
Conversation
Typer 0.26 vendors Click, so exceptions raised by Typer are no longer instances of the external click package's classes. Instead of pinning typer <0.26 forever, migrate the CLI off direct `import click` entirely: - All vendored symbols without a public typer re-export yet (fastapi/typer#1868) go through one module: cli/_click_compat.py. Everything else uses public typer API (typer.Exit, typer.Abort). - main(): replace the empty-message UsageError hack for no_args_is_help with an explicit `except NoArgsIsHelpError`. - get_output_context(): the ContextVar set by install_output_context() is now the source of truth; no more reaching into Click's threadlocal. - OsmosisGroup stays: tests pin its single-candidate suggestions, hidden command filtering, and the 'help' nudge. Native suggest_commands is disabled so it cannot double-append suggestions. - Tests construct vendored objects (typer.core.TyperCommand) and use typer.testing.CliRunner instead of external click equivalents. CLI code no longer depends on the external click package at all, which also ends click version conflicts with other click-dependent deps in user environments.
typer-slim <0.22 ships its own copy of the typer/ package; the lock had 0.21.1 (via huggingface-hub), which overlay-corrupted the typer 0.26.8 install nondeterministically (install-order dependent) — green locally, mixed 0.21/0.26 module tree on CI. typer-slim 0.22+ is an empty shim that just requires typer, so pinning >=0.22 removes the file collision for our CI and for downstream installs alike.
|
Why the first CI run failed (and passed locally): the lockfile carried
Note this collision predates this PR: main's lock has |
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
The typer-slim>=0.22 floor protects new resolutions, but an environment that was already overlay-corrupted (stale typer-slim files over typer's) degrades silently: exceptions stop matching, exit codes drift, and suggestions vanish. Assert the vendored-class lineage at import time and raise an actionable ImportError instead.
Summary
Typer 0.26 vendors Click (breaking change), so exceptions raised by Typer are no longer instances of the external
clickpackage's classes — the reason for our previoustyper <0.26cap. Instead of staying frozen on 0.25 forever, this migrates the CLI off directimport clickentirely and moves totyper>=0.26,<0.27.Closes #243 (supersedes the Dependabot range widening; that PR would have broken the CLI's top-level error handling).
Changes
cli/_click_compat.py— single import point for the vendored symbols Typer does not publicly re-export yet (UsageError,ClickException,NoArgsIsHelpError,Context,Command,get_current_context; see Expose ClickException and UsageError from the typer namespace fastapi/typer#1868). The<0.27cap keeps these private paths stable; the module is deleted once upstream exports them. Everything else uses public typer API (typer.Exit,typer.Abort).main()error handling — the fragile "empty-message UsageError means help was already printed" hack is replaced with an explicitexcept NoArgsIsHelpError.get_output_context()— the ContextVar set byinstall_output_context()is now the source of truth; the redundant reach into Click's threadlocal context stack is gone.OsmosisGroupis kept unchanged — tests pin its deliberate behaviors (single-candidate suggestion format, hidden-command filtering, theosmosis helpnudge). Typer's nativesuggest_commandsis explicitly disabled so it cannot double-append its own (unfiltered, multi-candidate) suggestions.typer.core.TyperCommand; the vendoredCommandis now abstract) and usetyper.testing.CliRunner; the removed ctx.obj resolution layer's test is replaced by two tests asserting the new ContextVar contract.Behavior
No user-facing behavior changes. Verified via the full suite plus real-process smoke tests: bare
osmosis(rich help, exit 0), typo suggestions,osmosis helpnudge,--version, and the--jsonstructured error envelope.CLI code no longer depends on the external
clickpackage at all, which also ends click version conflicts with other click-dependent dependencies in user environments.Verification
pytest: 1721 passedruff check/ruff format --check: cleanpyright osmosis_ai/: 0 errors;--verifytypes: no unexpected public API errorsNote for local editable installs
Dependencies of editable installs do not auto-update: run
pip install -e ".[dev]"(oruv sync) after pulling this change to pick up typer 0.26.Summary by cubic
Migrate the CLI to
typer0.26 (vendored Click) and remove directclickusage to keep error handling correct and avoid version conflicts. Add a hard guard againsttyper-slimoverlays and fail fast with a clear ImportError if a corruptedtyperinstall is detected; no user-facing changes otherwise.Refactors
osmosis_ai/cli/_click_compat.pyto import vendored types (UsageError,ClickException,NoArgsIsHelpError,Context,Command,get_current_context).UsageErrorhack withNoArgsIsHelpError; usetyper.Exit/typer.Abort.install_output_context()the source of truth inget_output_context(); remove threadlocal lookups.OsmosisGroupsuggestions and disable Typer’ssuggest_commands; tests updated totyper.coreandtyper.testing.typerfiles were overlaid (guides reinstall).Dependencies
typer>=0.26,<0.27and addtyper-slim>=0.22to prevent overlay corruption; CLI no longer importsclick.pip install -e ".[dev]"oruv syncto pick up the new versions.Written for commit 1a01816. Summary will update on new commits.