feat(cli): migrate from argparse to typer#447
Merged
Conversation
Add typer dependency, bump Python to >=3.10, create _compat.py bridge helper (ctx_to_args), rewrite __init__.py with typer app + global callback, and migrate project.py as the reference handler implementation. The pattern: each handler module exports a typer.Typer() app with @command() decorators. Business logic is unchanged — only the parameter wiring changes. ctx_to_args() bridges typer.Context to the args namespace that output()/output_error() expect. NOTE: CLI is broken until all handlers are migrated (the __init__.py imports all handler apps). This commit establishes the pattern; the remaining 17 handlers will be migrated in parallel. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…yper Replace `register(subparsers)` with module-level `typer.Typer()` apps (`auth_app`, `workspace_app`) following the project.py reference pattern. Thin typer command wrappers call `ctx_to_args()` then delegate to unchanged business logic functions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…to typer - version.py: exports version_app Typer with list/get/download/export/create subcommands - infer.py: exports infer_command() for top-level registration - search.py: exports search_command() for top-level registration - All business logic unchanged; uses ctx_to_args bridge from _compat Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…rse to typer Follows the same pattern as project.py: export typer apps, use ctx_to_args bridge, lazy imports in business logic. annotation.py uses 3-level nesting with batch_app and job_app sub-typers. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…e to typer Follows the same pattern as project.py: typer.Typer app with ctx_to_args bridge, business logic unchanged. Train uses invoke_without_command=True so bare `roboflow train -p X -v Y` works. Deployment legacy aliases use hidden=True. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…parse to typer Migrate workflow, folder, universe, video, batch, completion handlers to export typer apps. Rewrite _aliases.py to export register_aliases(app) using typer command decorators instead of argparse subparsers. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All CLI handlers migrated from argparse to typer: - 17 command group handlers export typer.Typer() apps - 2 top-level commands (infer, search) export registration functions - _aliases.py rewritten with register_aliases() using hidden=True - _compat.py bridge (ctx_to_args) enables unchanged business logic - _CleanHelpFormatter and _reorder_argv deleted (typer handles natively) - Shell completion built-in via typer (--install-completion, --show-completion) CLI is fully functional: roboflow --help shows clean Rich-formatted output, all commands work, hidden aliases work but don't appear in help. All linting clean (ruff format, ruff check, mypy). Tests need migration to typer.testing.CliRunner (159 argparse-specific test errors expected — next step). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…nner Replace build_parser().parse_args() patterns with CliRunner.invoke(app, ...) across all 18 test files. Registration tests now verify commands via --help exit code instead of inspecting argparse internals. Behavior tests that call handler functions directly with mocked APIs are preserved unchanged. Also adds _LegacyParserShim to build_parser() so backwards-compat tests pass. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Replace argparse references with typer architecture - Replace argparse handler template with typer pattern - Update Python version to 3.10+ Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Typer/Click doesn't propagate parent-level options (--json, --api-key, --workspace, --quiet) to subcommand parsers. Users and agents naturally write 'roboflow project list --json' which fails without reordering. Restore the _reorder_argv helper from the argparse era — it moves known global flags to before the subcommand in sys.argv before typer processes them. This is essential for agent-friendliness. 374 tests pass, all linting clean. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…dead code 1. _reorder_argv: don't reorder value flags (--api-key) that have no following value. Leaves them in place so typer shows a proper error. 2. _version_callback: check sys.argv for --json/-j to output JSON version. Removes dead _json_version_callback function. 374 tests pass, all linting clean. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…tion 1. In --json mode, typer/Click validation errors (missing args, bad options) now emit structured JSON on stderr instead of Rich-formatted text. Uses standalone_mode=False to catch click.exceptions.UsageError. 2. Shell completion commands (bash, zsh, fish) now generate real completion scripts using Click's shell_completion API. No longer stubs. Usage: eval "$(roboflow completion zsh)" 374 tests pass, all linting clean. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ection Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 696e5d4b93
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
1. _LegacyParserShim.parse_args() no longer executes commands — returns a namespace with func that can be called separately, matching the old argparse parse-then-execute pattern. 2. print_help() now outputs the help text (was discarding CliRunner output). 3. upload_model alias -p flag is now Optional[list[str]] to preserve the repeatable action='append' behavior for multi-project deployments. 374 tests pass, all linting clean. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Bump version to 1.3.0 - Better CLI description: 'Build and deploy computer vision models...' - Hide noisy aliases from --help (login, whoami, upload, import) — keep download - Hide --install-completion/--show-completion (we have 'completion' subcommand) - Add 'roboflow help' as alias for --help - workspace get defaults to current workspace when no ID given - Commands alphabetized in help output 374 tests pass, all linting clean. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Hide login/whoami/upload/import aliases from --help (keep download visible)
- Commands fully alphabetized in help output
- Exit code 0 (not 2) when running roboflow with no args
- Translate raw API hints ('issuing a GET request') to CLI hints
('roboflow project list', 'roboflow workspace get')
- workspace get defaults to current workspace when no arg given
- Hide --install-completion/--show-completion (we have 'completion' subcommand)
- Add 'roboflow help' command as alias for --help
374 tests pass, all linting clean.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Flattened command listing: roboflow --help now shows all 65 visible commands as 'noun verb' (e.g. 'project list', 'project get') instead of collapsed groups. Fully alphabetized. - Added -h shorthand for --help (works on all subcommands too) - Added -v shorthand for --version - Options sorted alphabetically in help output - Hidden commands (legacy aliases) excluded from help but still functional 374 tests pass, all linting clean. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All four help paths (--help, -h, no args, 'help' command) now show the same Rich-formatted flattened command listing. Subcommand --help still uses typer's default grouped view. Root help intercept happens in main() before typer processes --help, so the flattened output is shown instead of typer's grouped view. Rich panels and tables match the subcommand help styling. 374 tests pass, all linting clean. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Options use typer's color scheme: bold cyan for long flags, bold green for short flags, bold yellow for metavar (TEXT). Commands use cyan for group name and bold for verb, giving a visual indication that they're tied together. Top-level commands (download, infer, search) are just bold. 374 tests pass, all linting clean. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All commands in --help are now consistently 'noun verb' format: - Hidden: download (→ version download), infer (→ video infer exists), search (→ image search / universe search exist) - All hidden aliases still work, just not shown in help - Fixed description truncation: use full short_help instead of get_short_help_str() which Click caps at ~45 chars 374 tests pass, all linting clean. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ation 1. Add 'model infer' command that delegates to the existing infer logic. Now inference lives in the noun-verb hierarchy where users expect it. 2. Make 'image search' project-optional: without -p searches the whole workspace (using the workspace-level RoboQL search). With -p searches within a specific project. Also adds --export for dataset export. This unifies the old 'search' and 'image search' into one command. 3. Fix help text truncation by using cmd.help (full docstring first line) instead of short_help which Click caps at ~45 chars. Hidden aliases (roboflow infer, roboflow search, roboflow download) all still work — they're just not shown in --help. 374 tests pass, all linting clean. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
SortedGroup now sorts options on individual commands (not just groups) by overriding get_command() to sort params before help rendering. Sort order: required options first, then alphabetical by flag name, --help always last. Commands within groups also alphabetized. Applied to all 20 Typer() instances across 16 handler files. 374 tests pass, all linting clean. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Split options into separate Rich table columns (long flag, short flag, metavar, description) instead of concatenating into one text column. Now --api-key/-k/TEXT and --json/-j align properly. 374 tests pass, all linting clean. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…un/deploy) Don't advertise features that aren't implemented yet: - batch group: hidden entirely (all 4 commands are stubs) - workflow build/run/deploy: hidden individually (other workflow commands like list/get/create/update/fork are implemented) Hidden commands still work and give clear 'not yet implemented' messages. 374 tests pass, all linting clean. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
python_requires was bumped to >=3.10 in setup.py as part of the typer migration. The CI matrix still included 3.9, causing failures. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All 374 tests pass locally on Python 3.11.6. Previous CI run appears to have had a transient failure. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Typer's Rich-based help rendering emits ANSI escape codes in CI (GitHub Actions), which breaks string containment checks for hyphenated flags like --api-key. The escape codes split the text across color boundaries, so "api-key" as a contiguous substring is not found. Add _strip_ansi() helper to affected test files and use it when asserting on help output text. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
tonylampada
approved these changes
Apr 3, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Migrates the CLI framework from argparse to typer for better help output, built-in shell completion, simpler testing, and consistency with the inference CLI.
Benefits realized
roboflow completion bash/zsh/fishgenerates real scripts. No longer a stub.hidden=Truereplaces our custom_CleanHelpFormattertyper.testing.CliRunnerreplaces manual argparse construction--help. Rich-formatted errors in text mode.--jsonmode now emits structured JSON even for typer validation errors (missing args, bad options) — argparse couldn't do thisctx_to_args()bridge means business logic still usesargs.projectlookups. Full type safety is a future cleanup.register(subparsers)replaced with@command()decorators, butctx_to_args()bridge adds a small adapter layer. Net reduction is real but not as dramatic as hoped.@app.callback()is cleaner than argparse, but_reorder_argvstill needed (see below)_reorder_argvroboflow project list --jsonfails without the hack. Had to restore it.What changed
typer.Typer()apps with@command()decoratorsroboflow completion bash/zsh/fishgenerates real completion scripts--jsonvalidation errors: Structured JSON on stderr for all error types (including typer/Click validation)--json --version: Outputs{"version": "1.2.16"}_compat.pybridge:ctx_to_args()converts typer Context to args namespace, keeping all business logic unchanged_LegacyParserShim:parse_args()returns namespace withfunc(no longer executes);print_help()outputs textBackwards compatibility preserved
roboflow=roboflow.roboflowpy:mainentry point worksfrom roboflow.roboflowpy import mainand_argparserimports work_LegacyParserShimprovidesparse_args()(parse without executing) andprint_help()-ponupload_model)--jsonworks in any position (restored_reorder_argv)--jsonoutput schemas identicalWhat was removed
_CleanHelpFormatter→ typerhidden=Truepkgutilauto-discovery → explicitapp.add_typer()registrationtyper.testing.CliRunnerTesting
make check_code_qualityclean (ruff format, ruff check, mypy)--jsonpositioning bugDependency changes
typer>=0.12.0(brings click, rich, shellingham)python_requires=">=3.10", ruff/mypy targets to py310