Skip to content

Commit f2a893e

Browse files
yeldarbyclaude
andcommitted
fix(cli): address Codex review — legacy shim, print_help, repeatable -p
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>
1 parent 3f87418 commit f2a893e

2 files changed

Lines changed: 53 additions & 15 deletions

File tree

roboflow/cli/__init__.py

Lines changed: 52 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -131,33 +131,71 @@ class _LegacyParserShim:
131131
"""
132132

133133
def parse_args(self, argv: list[str] | None = None) -> object: # noqa: ANN001
134-
"""Parse *argv* using typer, return an argparse-like namespace."""
134+
"""Parse *argv* and return an argparse-like namespace with ``func``.
135+
136+
Does NOT execute the command — callers are expected to call
137+
``args.func(args)`` themselves, matching the old argparse pattern.
138+
"""
135139
import sys
136140
import types
137141

138-
from click.testing import CliRunner as _ClickRunner
139-
140142
if argv is None:
141143
argv = sys.argv[1:]
142144

143-
runner = _ClickRunner(mix_stderr=False) # type: ignore[call-arg]
144-
result = runner.invoke(app, argv, catch_exceptions=True, standalone_mode=False) # type: ignore[arg-type]
145+
argv = _reorder_argv(list(argv))
146+
147+
# Build a namespace with the parsed values by invoking the CLI
148+
# in a dry-run fashion: we intercept before execution.
149+
ns = types.SimpleNamespace(
150+
json=False,
151+
api_key=None,
152+
workspace=None,
153+
quiet=False,
154+
func=None,
155+
)
156+
157+
# Extract global flags manually
158+
remaining = []
159+
i = 0
160+
while i < len(argv):
161+
if argv[i] in ("--json", "-j"):
162+
ns.json = True
163+
elif argv[i] in ("--quiet", "-q"):
164+
ns.quiet = True
165+
elif argv[i] in ("--api-key", "-k") and i + 1 < len(argv):
166+
i += 1
167+
ns.api_key = argv[i]
168+
elif argv[i] == "--workspace" and i + 1 < len(argv):
169+
i += 1
170+
ns.workspace = argv[i]
171+
else:
172+
remaining.append(argv[i])
173+
i += 1
174+
175+
# Set func to a lambda that invokes the CLI with the original argv
176+
original_argv = list(argv)
177+
178+
def _run_via_typer(_args: object) -> None:
179+
from typer.testing import CliRunner as _TyperRunner
145180

146-
if result.exception and not isinstance(result.exception, SystemExit):
147-
raise result.exception
181+
runner = _TyperRunner()
182+
result = runner.invoke(app, original_argv, catch_exceptions=False)
183+
if result.output:
184+
print(result.output, end="") # noqa: T201
185+
if result.exit_code:
186+
sys.exit(result.exit_code)
148187

149-
ns = types.SimpleNamespace()
150-
ns.func = None
151-
if result.exit_code == 0:
152-
ns._result = result
188+
ns.func = _run_via_typer
153189
return ns
154190

155191
def print_help(self) -> None:
156192
"""Print the CLI help text."""
157-
from click.testing import CliRunner as _ClickRunner
193+
from typer.testing import CliRunner as _TyperRunner
158194

159-
runner = _ClickRunner()
160-
runner.invoke(app, ["--help"]) # type: ignore[arg-type]
195+
runner = _TyperRunner()
196+
result = runner.invoke(app, ["--help"])
197+
if result.output:
198+
print(result.output, end="") # noqa: T201
161199

162200

163201
def build_parser() -> _LegacyParserShim:

roboflow/cli/handlers/_aliases.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ def search_export_alias(
156156
@app.command("upload_model", hidden=True)
157157
def upload_model_alias(
158158
ctx: typer.Context,
159-
project: Annotated[Optional[str], typer.Option("-p", help="Project ID")] = None,
159+
project: Annotated[Optional[list[str]], typer.Option("-p", help="Project ID (repeatable)")] = None,
160160
version_number: Annotated[Optional[int], typer.Option("-v", help="Version number")] = None,
161161
model_type: Annotated[Optional[str], typer.Option("-t", help="Model type")] = None,
162162
model_path: Annotated[Optional[str], typer.Option("-m", help="Model file path")] = None,

0 commit comments

Comments
 (0)