Skip to content

Expose ClickException and UsageError from the typer namespace#1868

Open
uttam12331 wants to merge 1 commit into
fastapi:masterfrom
uttam12331:feat/1867-expose-clickexception
Open

Expose ClickException and UsageError from the typer namespace#1868
uttam12331 wants to merge 1 commit into
fastapi:masterfrom
uttam12331:feat/1867-expose-clickexception

Conversation

@uttam12331

Copy link
Copy Markdown

Summary

Closes #1867.

After vendoring Click in 0.26.0, Typer re-exports several vendored exception types — typer.BadParameter, typer.Abort, typer.Exit — but not the general-purpose ClickException / UsageError. That leaves no public way to raise a concise user-facing CLI error without importing from the private typer._click module (which the 0.26.0 vendoring guidance tells users not to do).

Change

Re-export the two vendored base errors alongside the existing ones, keeping the imports alphabetically ordered:

from ._click.exceptions import ClickException as ClickException
from ._click.exceptions import UsageError as UsageError

Apps can now do:

raise typer.ClickException("something broke")   # exit code 1, Typer-rendered
raise typer.UsageError("bad usage")             # exit code 2

Tests

Added test_click_exception and test_usage_error to tests/test_exit_errors.py asserting the exit codes (1 and 2) and that the message reaches the output.

Verified locally: pytest tests/test_exit_errors.py6 passed; ruff check clean.

After vendoring Click in 0.26.0, Typer re-exports several vendored exception
types (BadParameter, Abort, Exit) but not the general-purpose ClickException /
UsageError, leaving no public way to raise a concise user-facing CLI error
without reaching into the private ._click module.

Re-export ClickException and UsageError so apps can do
`raise typer.ClickException("...")` and get Typer's error rendering and the
expected exit code.

Closes fastapi#1867
JoyboyBrian added a commit to Osmosis-AI/osmosis-sdk-python that referenced this pull request Jul 1, 2026
## Summary

Typer 0.26 vendors Click ([breaking
change](https://github.com/fastapi/typer/releases/tag/0.26.0)), so
exceptions raised by Typer are no longer instances of the external
`click` package's classes — the reason for our previous `typer <0.26`
cap. Instead of staying frozen on 0.25 forever, this migrates the CLI
off direct `import click` entirely and moves to `typer>=0.26,<0.27`.

Closes #243 (supersedes the Dependabot range widening; that PR would
have broken the CLI's top-level error handling).

## Changes

- **New
[`cli/_click_compat.py`](../blob/refactor/migrate-typer-0.26/osmosis_ai/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 fastapi/typer#1868).
The `<0.27` cap 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 explicit
`except NoArgsIsHelpError`.
- **`get_output_context()`** — the ContextVar set by
`install_output_context()` is now the source of truth; the redundant
reach into Click's threadlocal context stack is gone.
- **`OsmosisGroup` is kept unchanged** — tests pin its deliberate
behaviors (single-candidate suggestion format, hidden-command filtering,
the `osmosis help` nudge). Typer's native `suggest_commands` is
explicitly disabled so it cannot double-append its own (unfiltered,
multi-candidate) suggestions.
- **Tests** — construct vendored objects (`typer.core.TyperCommand`; the
vendored `Command` is now abstract) and use `typer.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 help` nudge, `--version`, and the `--json`
structured error envelope.

CLI code no longer depends on the external `click` package at all, which
also ends click version conflicts with other click-dependent
dependencies in user environments.

## Verification

- `pytest`: 1721 passed
- `ruff check` / `ruff format --check`: clean
- `pyright osmosis_ai/`: 0 errors; `--verifytypes`: no unexpected public
API errors

## Note for local editable installs

Dependencies of editable installs do not auto-update: run `pip install
-e ".[dev]"` (or `uv sync`) after pulling this change to pick up typer
0.26.

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Migrate the CLI to `typer` 0.26 (vendored Click) and remove direct
`click` usage to keep error handling correct and avoid version
conflicts. Add a hard guard against `typer-slim` overlays and fail fast
with a clear ImportError if a corrupted `typer` install is detected; no
user-facing changes otherwise.

- **Refactors**
- Add `osmosis_ai/cli/_click_compat.py` to import vendored types
(`UsageError`, `ClickException`, `NoArgsIsHelpError`, `Context`,
`Command`, `get_current_context`).
- Replace the empty-message `UsageError` hack with `NoArgsIsHelpError`;
use `typer.Exit`/`typer.Abort`.
- Make the ContextVar set by `install_output_context()` the source of
truth in `get_output_context()`; remove threadlocal lookups.
- Preserve `OsmosisGroup` suggestions and disable Typer’s
`suggest_commands`; tests updated to `typer.core` and `typer.testing`.
- Assert Typer↔vendored Click lineage at import and raise an actionable
ImportError if `typer` files were overlaid (guides reinstall).

- **Dependencies**
- Pin `typer>=0.26,<0.27` and add `typer-slim>=0.22` to prevent overlay
corruption; CLI no longer imports `click`.
- For editable installs, run `pip install -e ".[dev]"` or `uv sync` to
pick up the new versions.

<sup>Written for commit 1a01816.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/Osmosis-AI/osmosis-sdk-python/pull/248?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>

<!-- End of auto-generated description by cubic. -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Expose a public general-purpose CLI error after vendoring Click

2 participants