From 99594fdca8d4e2d5d157c54dc4a13b7f54d7e9da Mon Sep 17 00:00:00 2001 From: ulises-jeremias Date: Wed, 22 Jul 2026 11:13:04 -0300 Subject: [PATCH 1/2] fix(cli): accept space-separated --addons/--extend values Typer only takes one value per flag; expand CNA-style `--addons a b` into repeated flags before parsing so the documented UX works. Co-authored-by: Cursor --- CHANGELOG.md | 6 ++ .../create-awesome-python-app/pyproject.toml | 2 +- .../src/create_awesome_python_app/__init__.py | 2 +- .../src/create_awesome_python_app/cli.py | 45 ++++++++- .../tests/test_cli.py | 99 +++++++++++++++++++ uv.lock | 2 +- 6 files changed, 152 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 471b254..36477ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 0.2.8 - 2026-07-22 + +### CLI + +- Accept space-separated `--addons` / `--extend` values (CNA Commander parity): `--addons fastapi-docker github-setup` expands to repeated flags before Typer parses. + ## 0.2.7 - 2026-07-22 ### CLI diff --git a/packages/create-awesome-python-app/pyproject.toml b/packages/create-awesome-python-app/pyproject.toml index b0f9ac3..84c85db 100644 --- a/packages/create-awesome-python-app/pyproject.toml +++ b/packages/create-awesome-python-app/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "create-awesome-python-app" -version = "0.2.7" +version = "0.2.8" description = "Composable scaffolding CLI for production-ready Python apps" readme = "README.md" requires-python = ">=3.12" diff --git a/packages/create-awesome-python-app/src/create_awesome_python_app/__init__.py b/packages/create-awesome-python-app/src/create_awesome_python_app/__init__.py index f8e94da..8abe760 100644 --- a/packages/create-awesome-python-app/src/create_awesome_python_app/__init__.py +++ b/packages/create-awesome-python-app/src/create_awesome_python_app/__init__.py @@ -1,3 +1,3 @@ """Create Awesome Python App CLI.""" -__version__ = "0.2.7" +__version__ = "0.2.8" diff --git a/packages/create-awesome-python-app/src/create_awesome_python_app/cli.py b/packages/create-awesome-python-app/src/create_awesome_python_app/cli.py index ae8d795..a3993cb 100644 --- a/packages/create-awesome-python-app/src/create_awesome_python_app/cli.py +++ b/packages/create-awesome-python-app/src/create_awesome_python_app/cli.py @@ -71,6 +71,49 @@ def _preprocess_fixture_argv(argv: list[str] | None = None) -> list[str]: return out +def _expand_variadic_option(argv: list[str], option: str) -> list[str]: + """Expand ``--addons a b`` into ``--addons a --addons b`` (CNA Commander parity). + + Typer's ``list[str]`` Option only accepts one value per flag. Commander uses + ``--addons [extensions...]``, so users naturally write space-separated lists. + """ + out: list[str] = [] + i = 0 + prefix = option + "=" + while i < len(argv): + arg = argv[i] + if arg == option: + i += 1 + values: list[str] = [] + while i < len(argv) and not argv[i].startswith("-"): + values.append(argv[i]) + i += 1 + if not values: + out.append(option) + else: + for value in values: + out.extend([option, value]) + continue + if arg.startswith(prefix): + value = arg[len(prefix) :] + out.extend([option, value] if value else [option]) + i += 1 + continue + out.append(arg) + i += 1 + return out + + +def _preprocess_cli_argv(argv: list[str] | None = None) -> list[str]: + """Apply argv rewrites needed before Typer parses the CLI.""" + out = _preprocess_fixture_argv(argv) + out = _expand_variadic_option(out, "--addons") + out = _expand_variadic_option(out, "--extend") + if argv is None: + sys.argv = out + return out + + def apply_fixture_mode(fixture: str | None) -> None: """Translate ``--fixture`` into ``CPA_CATALOG_FIXTURE`` / ``CPA_FIXTURE_DIR``.""" if fixture is None and os.environ.get("CPA_CATALOG_FIXTURE") != "1": @@ -204,7 +247,7 @@ def main() -> None: treat `cache` as project_directory). """ check_python_version(">=3.12", "create-awesome-python-app") - _preprocess_fixture_argv() + _preprocess_cli_argv() if len(sys.argv) > 1 and sys.argv[1] == "cache": sys.argv = [sys.argv[0], *sys.argv[2:]] cache_app(prog_name="create-awesome-python-app cache") diff --git a/packages/create-awesome-python-app/tests/test_cli.py b/packages/create-awesome-python-app/tests/test_cli.py index bdb85a2..df438b4 100644 --- a/packages/create-awesome-python-app/tests/test_cli.py +++ b/packages/create-awesome-python-app/tests/test_cli.py @@ -271,6 +271,105 @@ async def fake_create_python_app(project_directory, options, *_args, **_kwargs): assert options["template"] == f"file://{tpl}" +def test_expand_variadic_addons_and_extend() -> None: + from create_awesome_python_app.cli import _expand_variadic_option, _preprocess_cli_argv + + assert _expand_variadic_option( + ["cpa", "my-api", "--addons", "fastapi-docker", "github-setup", "--no-install"], + "--addons", + ) == [ + "cpa", + "my-api", + "--addons", + "fastapi-docker", + "--addons", + "github-setup", + "--no-install", + ] + assert _preprocess_cli_argv( + [ + "cpa", + "my-api", + "--template", + "fastapi-starter", + "--addons", + "fastapi-docker", + "github-setup", + "--extend", + "a", + "b", + "--no-interactive", + ] + ) == [ + "cpa", + "my-api", + "--template", + "fastapi-starter", + "--addons", + "fastapi-docker", + "--addons", + "github-setup", + "--extend", + "a", + "--extend", + "b", + "--no-interactive", + ] + + +def test_space_separated_addons_after_project_directory( + tmp_path: Path, monkeypatch +) -> None: + """CNA parity: ``--addons fastapi-docker github-setup`` (one flag, many values).""" + from create_awesome_python_app.cli import _preprocess_cli_argv + + tpl = tmp_path / "tpl" + tpl.mkdir() + captured: dict[str, object] = {} + + async def fake_check_for_latest_version(_package_name): + return None + + async def fake_create_python_app(project_directory, options, *_args, **_kwargs): + captured["project_directory"] = project_directory + captured["options"] = options + + monkeypatch.setattr( + "create_awesome_python_app.cli.check_for_latest_version", + fake_check_for_latest_version, + ) + monkeypatch.setattr( + "create_awesome_python_app.cli.create_python_app", + fake_create_python_app, + ) + + argv = _preprocess_cli_argv( + [ + "cpa", + "my-api", + "--template", + f"file://{tpl}", + "--addons", + "fastapi-docker", + "github-setup", + "--no-install", + "--no-interactive", + ] + ) + result = runner.invoke(app, argv[1:]) + + text = (result.stdout or "") + (result.stderr or "") + assert result.exit_code == 0, text + assert "unexpected extra argument" not in text.lower() + options = captured["options"] + assert isinstance(options, dict) + addons = options["addons"] + assert isinstance(addons, list) + assert len(addons) == 2 + assert any("fastapi-docker" in a for a in addons) + assert any("github-setup" in a for a in addons) + + def test_preprocess_fixture_argv_bare_and_with_dir() -> None: from create_awesome_python_app.cli import _FIXTURE_AUTO, _preprocess_fixture_argv diff --git a/uv.lock b/uv.lock index 835150d..2d081f5 100644 --- a/uv.lock +++ b/uv.lock @@ -138,7 +138,7 @@ wheels = [ [[package]] name = "create-awesome-python-app" -version = "0.2.7" +version = "0.2.8" source = { editable = "packages/create-awesome-python-app" } dependencies = [ { name = "create-python-app-core" }, From b79732c1ca95387f685dace65a44236411fadac0 Mon Sep 17 00:00:00 2001 From: ulises-jeremias Date: Wed, 22 Jul 2026 11:15:46 -0300 Subject: [PATCH 2/2] style: fix ruff import wrap and CHANGELOG MD024 Co-authored-by: Cursor --- CHANGELOG.md | 2 +- packages/create-awesome-python-app/tests/test_cli.py | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 36477ad..0392cee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## 0.2.8 - 2026-07-22 -### CLI +### CLI / argv - Accept space-separated `--addons` / `--extend` values (CNA Commander parity): `--addons fastapi-docker github-setup` expands to repeated flags before Typer parses. diff --git a/packages/create-awesome-python-app/tests/test_cli.py b/packages/create-awesome-python-app/tests/test_cli.py index df438b4..1bf15d1 100644 --- a/packages/create-awesome-python-app/tests/test_cli.py +++ b/packages/create-awesome-python-app/tests/test_cli.py @@ -272,7 +272,10 @@ async def fake_create_python_app(project_directory, options, *_args, **_kwargs): def test_expand_variadic_addons_and_extend() -> None: - from create_awesome_python_app.cli import _expand_variadic_option, _preprocess_cli_argv + from create_awesome_python_app.cli import ( + _expand_variadic_option, + _preprocess_cli_argv, + ) assert _expand_variadic_option( ["cpa", "my-api", "--addons", "fastapi-docker", "github-setup", "--no-install"],