diff --git a/packages/create-awesome-python-app/README.md b/packages/create-awesome-python-app/README.md index f09e3f2..5f95a8c 100644 --- a/packages/create-awesome-python-app/README.md +++ b/packages/create-awesome-python-app/README.md @@ -1,5 +1,8 @@ # create-awesome-python-app -CLI package (stub). Entry point: `create-awesome-python-app`. +CLI package. Framework: **Typer** (chosen over Click for richer typing/help). -Depends on workspace member `create-python-app-core`. +```bash +uv run create-awesome-python-app --help +uvx create-awesome-python-app@latest my-app # after PyPI publish +``` diff --git a/packages/create-awesome-python-app/pyproject.toml b/packages/create-awesome-python-app/pyproject.toml index 4655243..07446e5 100644 --- a/packages/create-awesome-python-app/pyproject.toml +++ b/packages/create-awesome-python-app/pyproject.toml @@ -6,6 +6,8 @@ readme = "README.md" requires-python = ">=3.12" dependencies = [ "create-python-app-core", + "rich>=15.0.0", + "typer>=0.27.0", ] [project.scripts] 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 6bd53bb..4c70cce 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 @@ -1,5 +1,76 @@ -"""CLI entrypoint placeholder (full Typer app lands in Epic 4).""" +"""Typer CLI entrypoint for create-awesome-python-app.""" + +from __future__ import annotations + +import asyncio + +import typer +from create_python_app_core import ( + check_python_version, + create_python_app, + print_env_info, +) +from rich.console import Console + +from create_awesome_python_app import __version__ + +app = typer.Typer( + name="create-awesome-python-app", + help="Composable scaffolding CLI for production-ready Python apps.", + no_args_is_help=False, + add_completion=False, +) +console = Console(stderr=True) def main() -> None: - print("create-awesome-python-app stub — see roadmap issue #1") + """Console script entrypoint.""" + check_python_version(">=3.12", "create-awesome-python-app") + app() + + +@app.callback(invoke_without_command=True) +def scaffold( + ctx: typer.Context, + project_directory: str | None = typer.Argument("my-project"), + version: bool = typer.Option(False, "--version", help="Show version"), + info: bool = typer.Option(False, "--info", "-i", help="Print env info"), + verbose: bool = typer.Option(False, "--verbose", "-v"), + template: str | None = typer.Option(None, "--template", "-t"), + no_install: bool = typer.Option(False, "--no-install"), + force: bool = typer.Option(False, "--force", "-f"), +) -> None: + """Scaffold a new Python project (flags expand in later issues).""" + if version: + console.print(__version__) + raise typer.Exit(0) + if info: + print_env_info() + if ctx.invoked_subcommand is not None: + return + if not template: + console.print( + "[yellow]Stub CLI[/yellow]: pass --template " + "(full interactive mode lands in #34)." + ) + console.print(f"Would scaffold [cyan]{project_directory}[/cyan]") + raise typer.Exit(0) + + asyncio.run( + create_python_app( + project_directory or "my-project", + { + "template": template, + "install": not no_install, + "force": force, + "verbose": verbose, + }, + ) + ) + console.print(f"[green]Created[/green] {project_directory}") + + +@app.command("cache") +def cache_placeholder() -> None: + """Cache management (subcommands land in #37).""" + console.print("cache subcommands: see #37") diff --git a/packages/create-awesome-python-app/tests/test_cli.py b/packages/create-awesome-python-app/tests/test_cli.py index 98d18ef..e93738d 100644 --- a/packages/create-awesome-python-app/tests/test_cli.py +++ b/packages/create-awesome-python-app/tests/test_cli.py @@ -1,7 +1,17 @@ -from create_awesome_python_app.cli import main +from create_awesome_python_app.cli import app +from typer.testing import CliRunner +runner = CliRunner() -def test_main_prints_stub(capsys) -> None: - main() - captured = capsys.readouterr() - assert "stub" in captured.out.lower() + +def test_version() -> None: + result = runner.invoke(app, ["--version"]) + assert result.exit_code == 0 + combined = result.stdout + result.stderr + assert "0.0.0" in combined + + +def test_help() -> None: + result = runner.invoke(app, ["--help"]) + assert result.exit_code == 0 + assert "Scaffold" in result.stdout or "create" in result.stdout.lower() diff --git a/uv.lock b/uv.lock index aae61f9..5618975 100644 --- a/uv.lock +++ b/uv.lock @@ -18,6 +18,15 @@ dev = [ { name = "ruff", specifier = ">=0.12.0" }, ] +[[package]] +name = "annotated-doc" +version = "0.0.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288, upload-time = "2025-11-10T22:07:42.062Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303, upload-time = "2025-11-10T22:07:40.673Z" }, +] + [[package]] name = "anyio" version = "4.14.2" @@ -133,10 +142,16 @@ version = "0.0.0" source = { editable = "packages/create-awesome-python-app" } dependencies = [ { name = "create-python-app-core" }, + { name = "rich" }, + { name = "typer" }, ] [package.metadata] -requires-dist = [{ name = "create-python-app-core", editable = "packages/create-python-app-core" }] +requires-dist = [ + { name = "create-python-app-core", editable = "packages/create-python-app-core" }, + { name = "rich", specifier = ">=15.0.0" }, + { name = "typer", specifier = ">=0.27.0" }, +] [[package]] name = "create-python-app-core" @@ -235,6 +250,27 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, ] +[[package]] +name = "markdown-it-py" +version = "4.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/ff/7841249c247aa650a76b9ee4bbaeae59370dc8bfd2f6c01f3630c35eb134/markdown_it_py-4.2.0.tar.gz", hash = "sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49", size = 82454, upload-time = "2026-05-07T12:08:28.36Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl", hash = "sha256:9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a", size = 91687, upload-time = "2026-05-07T12:08:27.182Z" }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, +] + [[package]] name = "nodeenv" version = "1.10.0" @@ -411,6 +447,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, ] +[[package]] +name = "rich" +version = "15.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c0/8f/0722ca900cc807c13a6a0c696dacf35430f72e0ec571c4275d2371fca3e9/rich-15.0.0.tar.gz", hash = "sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36", size = 230680, upload-time = "2026-04-12T08:24:00.75Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/82/3b/64d4899d73f91ba49a8c18a8ff3f0ea8f1c1d75481760df8c68ef5235bf5/rich-15.0.0-py3-none-any.whl", hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb", size = 310654, upload-time = "2026-04-12T08:24:02.83Z" }, +] + [[package]] name = "ruff" version = "0.15.21" @@ -436,6 +485,30 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/dd/75/e90ab9aeece218a9fc5a5bc3ec97d0ee6bb3c4ff95869463c1de58e29a1c/ruff-0.15.21-py3-none-win_arm64.whl", hash = "sha256:6e83115d4b9377c1cbc13abf0e051f069fab0ef815ea0504a8a008cee24dd0a8", size = 11375265, upload-time = "2026-07-09T20:01:31.772Z" }, ] +[[package]] +name = "shellingham" +version = "1.5.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, +] + +[[package]] +name = "typer" +version = "0.27.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-doc" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "rich" }, + { name = "shellingham" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/37/78/fda3361b56efc27944f24225f6ecd13d96d6fcfe37bd0eb34e2f4c63f9fc/typer-0.27.0.tar.gz", hash = "sha256:629bd12ea5d13a17148125d9a264f949eb171fb3f120f9b04d85873cab054fa5", size = 203430, upload-time = "2026-07-15T19:21:07.007Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/40/03/26a383c9e58c213199d1aad1c3d353cfc22d4444ec6d2c0bf8ad02523843/typer-0.27.0-py3-none-any.whl", hash = "sha256:6f4b27631e47f077871b7dc30e933ec0131c1390fbe0e387ea5574b5bac9ccf1", size = 122716, upload-time = "2026-07-15T19:21:05.553Z" }, +] + [[package]] name = "typing-extensions" version = "4.16.0"