From b31d4fadc113628cfc9ec5627e05a5adaf49150b Mon Sep 17 00:00:00 2001 From: Srijan Upadhyay Date: Wed, 19 Aug 2026 12:56:27 +0530 Subject: [PATCH] fix(cli): stop --yaml and --yes both claiming -y RunContext.cli_command bound -y to both --yaml and --yes/--no-confirm, so Click emitted a warning on every invocation. Drop -y from --yaml, keeping -y as the conventional alias for --yes, and update the option table in docs/guides/cli.md to match. Adds TestShortFlagCollision, which asserts the paired invariant from the generated Click command: --yaml carries no short flag, --yes still owns -y, and no two options share a short flag. It fails if -y is put back on --yaml. Closes #559 Signed-off-by: Srijan Upadhyay --- docs/guides/cli.md | 2 +- nemo_run/cli/api.py | 4 +--- test/cli/test_api.py | 20 ++++++++++++++++++++ 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/docs/guides/cli.md b/docs/guides/cli.md index 226b7d5e..a63b374d 100644 --- a/docs/guides/cli.md +++ b/docs/guides/cli.md @@ -346,7 +346,7 @@ Train a model using the specified configuration. │ --dryrun Print the scheduler request without submitting │ --factory -f TEXT Predefined factory to use [default: None] │ --load -l TEXT Load a factory from a directory [default: None] -│ --yaml -y TEXT Path to a YAML file to load [default: None] +│ --yaml TEXT Path to a YAML file to load [default: None] │ --repl -r Enter interactive mode │ --detach Detach from the run │ --yes,--no-confirm -y Skip confirmation before execution diff --git a/nemo_run/cli/api.py b/nemo_run/cli/api.py index e05b921f..97227495 100644 --- a/nemo_run/cli/api.py +++ b/nemo_run/cli/api.py @@ -890,9 +890,7 @@ def command( load: Optional[str] = typer.Option( None, "--load", "-l", help="Load a factory from a directory" ), - yaml: Optional[str] = typer.Option( - None, "--yaml", "-y", help="Path to a YAML file to load" - ), + yaml: Optional[str] = typer.Option(None, "--yaml", help="Path to a YAML file to load"), repl: bool = typer.Option(False, "--repl", "-r", help="Enter interactive mode"), detach: bool = typer.Option(False, "--detach", help="Detach from the run"), skip_confirmation: bool = typer.Option( diff --git a/test/cli/test_api.py b/test/cli/test_api.py index da359c12..e9b14fc4 100644 --- a/test/cli/test_api.py +++ b/test/cli/test_api.py @@ -1918,3 +1918,23 @@ class TestExtractConstituentTypes: def test_various_type_hints(self, type_hint, expected_types): """Test get_underlying_types with various type hints.""" assert extract_constituent_types(type_hint) == expected_types + + +class TestShortFlagCollision: + """Regression test for issue #559: -y was bound to both --yaml and --yes.""" + + def test_short_flag_y_belongs_only_to_yes(self): + @run.cli.entrypoint + def dummy_task(yaml: Optional[str] = typer.Option(None, "--yaml", help="YAML file")): + return yaml + + app = typer.Typer() + RunContext.cli_command(app, "task", dummy_task) + params = typer.main.get_command(app).params + + opts = {param.name: param.opts for param in params} + assert opts["yaml"] == ["--yaml"] + assert "-y" in opts["skip_confirmation"] + + shorts = [opt for param in params for opt in param.opts if len(opt) == 2] + assert len(shorts) == len(set(shorts)), f"duplicate short flags: {shorts}"