diff --git a/packages/create-python-app-core/README.md b/packages/create-python-app-core/README.md index 4e8427e..291ea16 100644 --- a/packages/create-python-app-core/README.md +++ b/packages/create-python-app-core/README.md @@ -1,3 +1,15 @@ # create-python-app-core -Programmatic scaffolding engine (stub). Consumed by `create-awesome-python-app`. +Programmatic scaffolding engine behind Create Awesome Python App. + +```python +from create_python_app_core import ( + create_python_app, + check_python_version, + check_for_latest_version, + print_env_info, + CPA_USER_AGENT, +) +``` + +Requires **Python >= 3.12**. diff --git a/packages/create-python-app-core/pyproject.toml b/packages/create-python-app-core/pyproject.toml index d2087b6..ee8b00a 100644 --- a/packages/create-python-app-core/pyproject.toml +++ b/packages/create-python-app-core/pyproject.toml @@ -4,7 +4,9 @@ version = "0.0.0" description = "Scaffolding engine for Create Awesome Python App (stub)" readme = "README.md" requires-python = ">=3.12" -dependencies = [] +dependencies = [ + "packaging>=24.0", +] [build-system] requires = ["hatchling"] diff --git a/packages/create-python-app-core/src/create_python_app_core/__init__.py b/packages/create-python-app-core/src/create_python_app_core/__init__.py index bbb407c..f18acff 100644 --- a/packages/create-python-app-core/src/create_python_app_core/__init__.py +++ b/packages/create-python-app-core/src/create_python_app_core/__init__.py @@ -1,3 +1,21 @@ -"""Scaffolding engine for Create Awesome Python App.""" +"""Programmatic scaffolding engine for Create Awesome Python App.""" -__version__ = "0.0.0" +from __future__ import annotations + +from create_python_app_core._version import __version__ +from create_python_app_core.api import ( + CPA_USER_AGENT, + check_for_latest_version, + check_python_version, + create_python_app, + print_env_info, +) + +__all__ = [ + "__version__", + "CPA_USER_AGENT", + "check_for_latest_version", + "check_python_version", + "create_python_app", + "print_env_info", +] diff --git a/packages/create-python-app-core/src/create_python_app_core/_version.py b/packages/create-python-app-core/src/create_python_app_core/_version.py new file mode 100644 index 0000000..6c8e6b9 --- /dev/null +++ b/packages/create-python-app-core/src/create_python_app_core/_version.py @@ -0,0 +1 @@ +__version__ = "0.0.0" diff --git a/packages/create-python-app-core/src/create_python_app_core/api.py b/packages/create-python-app-core/src/create_python_app_core/api.py new file mode 100644 index 0000000..299cf2f --- /dev/null +++ b/packages/create-python-app-core/src/create_python_app_core/api.py @@ -0,0 +1,59 @@ +"""Public API surface (mirrors @create-node-app/core index exports).""" + +from __future__ import annotations + +import sys +from collections.abc import Awaitable, Callable +from typing import Any + +from create_python_app_core._version import __version__ + +CPA_USER_AGENT = ( + f"create-python-app-core/{__version__} " + "(https://github.com/Create-Python-App/create-python-app)" +) + + +def check_python_version(required: str, package_name: str) -> None: + """Exit if the running interpreter does not satisfy *required* (PEP 440).""" + from packaging.specifiers import SpecifierSet + from packaging.version import Version + + current = Version(".".join(map(str, sys.version_info[:3]))) + if current not in SpecifierSet(required): + print( + f"You are running Python {current}.\n" + f"{package_name} requires Python {required}.\n" + "Please update your version of Python.", + file=sys.stderr, + ) + raise SystemExit(1) + + +async def check_for_latest_version(package_name: str) -> str | None: + """Fetch latest version from PyPI. Returns None on failure.""" + # Implemented fully in #31; stub returns None until then. + _ = package_name + return None + + +def print_env_info() -> None: + """Print environment info then exit. Full implementation in #30.""" + print(f"create-python-app-core {__version__}") + print(f"Python {sys.version}") + raise SystemExit(0) + + +async def create_python_app( + project_directory: str, + options: dict[str, Any], + transform_options: Callable[[dict[str, Any]], Awaitable[dict[str, Any]]] + | None = None, +) -> None: + """Scaffold a project. Full installer lands in #29.""" + if transform_options is not None: + options = await transform_options(options) + raise NotImplementedError( + "create_python_app installer not implemented yet — see #29 " + f"(project={project_directory!r}, options_keys={list(options)})" + ) diff --git a/packages/create-python-app-core/tests/test_api.py b/packages/create-python-app-core/tests/test_api.py new file mode 100644 index 0000000..6c4e242 --- /dev/null +++ b/packages/create-python-app-core/tests/test_api.py @@ -0,0 +1,11 @@ +import pytest +from create_python_app_core import check_python_version + + +def test_check_python_version_accepts_current() -> None: + check_python_version(">=3.12", "create-python-app-core") + + +def test_check_python_version_rejects_impossible() -> None: + with pytest.raises(SystemExit): + check_python_version(">=99.0", "create-python-app-core") diff --git a/packages/create-python-app-core/tests/test_version.py b/packages/create-python-app-core/tests/test_version.py index 45843ee..f513cfc 100644 --- a/packages/create-python-app-core/tests/test_version.py +++ b/packages/create-python-app-core/tests/test_version.py @@ -1,5 +1,10 @@ -from create_python_app_core import __version__ +from create_python_app_core import CPA_USER_AGENT, __version__ def test_version_is_semver_stub() -> None: assert __version__ == "0.0.0" + + +def test_user_agent_contains_version() -> None: + assert __version__ in CPA_USER_AGENT + assert "create-python-app-core" in CPA_USER_AGENT diff --git a/uv.lock b/uv.lock index 1b44797..aa3c3b0 100644 --- a/uv.lock +++ b/uv.lock @@ -119,6 +119,12 @@ requires-dist = [{ name = "create-python-app-core", editable = "packages/create- name = "create-python-app-core" version = "0.0.0" source = { editable = "packages/create-python-app-core" } +dependencies = [ + { name = "packaging" }, +] + +[package.metadata] +requires-dist = [{ name = "packaging", specifier = ">=24.0" }] [[package]] name = "distlib"