Skip to main content

create-python-app-core

Discord

Programmatic scaffolding engine behind Create Awesome Python App. Import the scaffolding pipeline -- composable, headless, and CI-ready.

Requires Python >= 3.12.

This is the engine package. For the interactive CLI, use create-awesome-python-app instead.


Installation

pip install create-python-app-core

Or with uv:

uv add create-python-app-core

Usage

Scaffold a project programmatically

import asyncio
from create_python_app_core import create_python_app


async def main() -> None:
    await create_python_app(
        "my-app",
        {
            "projectName": "my-app",
            "template": "file:///path/to/template",
            "install": True,
        },
        transform_options=lambda opts: asyncio.sleep(0, result=opts),
    )


asyncio.run(main())

Scaffold with the installer API

from create_python_app_core import scaffold_project

scaffold_project(
    "my-app",
    template="file:///path/to/template",
    addons=[],
    extend=[],
    install=True,
    force=False,
    offline=False,
)

Resolve a template source

from create_python_app_core import resolve_source, get_template_dir_path

source = resolve_source(
    "https://github.com/Create-Python-App/cpa-templates?ref=main&subdir=fastapi"
)
print(source.kind)  # github
print(source.ref)  # main
print(source.subdir)  # fastapi

Download a repository into the cache

from create_python_app_core import resolve_source, download_repository

source = resolve_source("https://github.com/org/my-template")
root = download_repository(source, refresh="stale", offline=False)
template_dir = get_template_dir_path(source, root)

Load template configuration

from pathlib import Path

from create_python_app_core import load_cpa_config

cfg = load_cpa_config(Path("/path/to/template/cpa.config.json"))
for opt in cfg.custom_options:
    print(opt.key, opt.default)

Check environment info

from create_python_app_core import print_env_info

print_env_info()
# Prints Python, platform, uv, and git info. Then exits.

Validate the Python version

from create_python_app_core import check_python_version

check_python_version(">=3.12", "my-tool")
# Exits with code 1 if the interpreter does not match.

API Reference

All public exports from create_python_app_core:

Functions

Signature Description
create_python_app(project_directory, options, transform_options=None) Async orchestrator. Applies transform_options, then delegates to scaffold_project.
scaffold_project(project_directory, *, template, addons=None, extend=None, force=False, install=True, offline=False, refresh=None, keep_on_failure=False, cache_dir=None, options=None) Main scaffolding pipeline. Resolves sources, downloads layers, merges files, runs uv sync, and initializes git.
resolve_source(spec, *, cache_dir=None) Parses a template/extension specifier (GitHub URL, file://, slug) into a ResolvedSource.
get_template_dir_path(source, root) Returns the template/ subdirectory when present, otherwise the resolved root.
default_cache_dir() Returns CPA_CACHE_DIR or ~/.cache/cpa.
download_repository(source, *, offline=False, refresh=None, cache_root=None) Clones or refreshes a Git repo into the cache. Returns the entry directory.
read_cache_meta(entry) Reads .cpa-cache.json metadata from a cache entry.
write_cache_meta(entry, meta) Writes .cpa-cache.json metadata for a cache entry.
load_cpa_config(path) Loads optional cpa.config.json (custom CLI prompts). Returns empty CpaConfig when missing.
assert_directory_is_empty(path, *, force=False) Raises NonEmptyTargetDirectoryError when the target exists and is non-empty.
load_layer(source, root, dest, *, overwrite=True, context=None) Copies one template/extension layer into dest.
merge_layers(layers, dest, *, context=None) Applies layers in order (template, addons, extend). Later layers win.
merge_pyproject_text(base_text, overlay_text) Deep-merges two pyproject.toml documents as TOML.
check_python_version(required, package_name) Compares sys.version_info against a PEP 440 specifier. Exits with code 1 if too old.
check_for_latest_version(package_name) Async. Fetches the latest version from PyPI. Returns None on failure.
print_env_info() Prints OS, Python, uv, and git info to stdout, then exits.

Constants

Name Description
__version__ Installed package version string.
CPA_USER_AGENT HTTP User-Agent sent to PyPI (create-python-app-core/<version>).
NON_EMPTY_DIR_ERROR_CODE Stable code for NonEmptyTargetDirectoryError (CPA_NON_EMPTY_TARGET_DIR).

Types

Type Shape
ResolvedSource kind (github | file | slug | git), url, ref, subdir, local_path
CacheMeta url, ref, fetched_at, commit
CpaConfig name, custom_options, raw
CpaCustomOption key, type, message, default
CpaError Base exception with .code attribute
ConfigParseError Invalid cpa.config.json (code: CPA_CONFIG_PARSE)
ManifestLoadError Missing template directory (code: CPA_MANIFEST_LOAD)
PackageManagerFallbackError Package manager fallback failure (code: CPA_PM_FALLBACK)
ScaffoldAbortedError Scaffold failed mid-run (code: CPA_ABORTED)
NonEmptyTargetDirectoryError Target directory not empty (code: CPA_NON_EMPTY_TARGET_DIR)

create_python_app options dict

Key Type Default Description
template str "" Primary template specifier (URL, file://, or slug).
addons list[str] [] Additional template layers applied after the base template.
extend list[str] [] Extension layers applied last (later wins on conflicts).
force bool False Allow scaffolding into a non-empty directory.
install bool True Run uv sync when pyproject.toml is present.
offline bool False Use cached repos only; raise on cache miss.
refresh str env / "stale" Cache refresh mode: always, stale, or manual.
keep_on_failure bool False Keep the partial project directory when scaffolding fails.
cache_dir str | Path None Override the default cache root.
set dict {} Jinja context overrides (merged into projectName and custom option defaults).

Environment Variables

All CPA_* variables read by the core engine:

Variable Default Description
CPA_CACHE_DIR ~/.cache/cpa Root directory for cloned repository cache entries.
CPA_REFRESH stale Default cache refresh mode: always, stale, or manual.
CPA_REFRESH_AFTER_HOURS 24 Hours before a stale cache entry is refreshed.
CPA_SKIP_GIT unset Set to 1 to skip git init and block all git subprocess calls.
CPA_STRICT_REPRO unset Set to 1 to require a full 40-character commit SHA in ?ref= query params.

Error Codes

Stable machine-readable codes on CpaError.code:

Code Exception class When raised
CPA_ERROR CpaError Generic base error (default).
CPA_CONFIG_PARSE ConfigParseError Malformed or invalid cpa.config.json.
CPA_MANIFEST_LOAD ManifestLoadError Template directory not found on disk.
CPA_PM_FALLBACK PackageManagerFallbackError Package manager fallback failure.
CPA_ABORTED ScaffoldAbortedError Scaffold failed (template render, unexpected error, etc.).
CPA_NON_EMPTY_TARGET_DIR NonEmptyTargetDirectoryError Target directory exists and is not empty.
CPA_GIT CpaError Git subprocess failed or git not found.
CPA_SKIP_GIT CpaError Git operation attempted while CPA_SKIP_GIT=1.
CPA_FILE CpaError file:// source path does not exist.
CPA_OFFLINE CpaError Offline mode with no cached copy of the repository.
CPA_STRICT_REPRO CpaError ?ref= is not a full SHA while CPA_STRICT_REPRO=1.

How It Works

create_python_app()
  |-- transform_options() (optional)
  |-- scaffold_project()
        |-- assert_directory_is_empty()
        |-- resolve_source() for each template / addon / extend
        |-- download_repository() (git clone or file://)
        |-- load_cpa_config() from cpa.config.json
        |-- build_scaffold_context() (projectName + custom options + --set)
        |-- merge_layers() (Jinja .template, .append, pyproject merge)
        |-- uv sync (when install=True and pyproject.toml exists)
        |-- git init (unless CPA_SKIP_GIT=1)
        +-- cleanup partial directory on failure (unless keep_on_failure)

Architecture

The package is organized into these modules:

Module Responsibility
__init__.py Barrel export and public API surface
api.py create_python_app, version checks, env info, PyPI lookup
installer.py scaffold_project orchestration, uv sync, git init
loaders.py File discovery, .template / .append processing, layer merge
pyproject_merge.py Deep-merge pyproject.toml across template layers
paths.py URL resolution (GitHub, file://, slugs, ?ref=, ?subdir=)
git_cache.py Clone/pull with cache, refresh modes, offline support
config.py Reads optional cpa.config.json for custom CLI prompts
errors.py Typed CpaError hierarchy with stable codes

Related


License

MIT (c) Create Python App Contributors

Metadata

Release files for create-python-app-core 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for create-python-app-core 0.3.0
File Size Uploaded
create_python_app_core-0.3.0.tar.gz 19.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for create-python-app-core 0.3.0
File Interpreter ABI Platform
create_python_app_core-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 38.4 kB

Release files / create_python_app_core-0.3.0.tar.gz

Download URL create_python_app_core-0.3.0.tar.gz
Size 19.7 kB
Tags Source
SHA-256 checksum
How to use checksums
75d38550672573cb0cf022215a6bdbe931eaa88edb6b13247da0f3be36edf34f
BLAKE2b-256 checksum
How to use checksums
8bcfa65330683aa082a9a3604b3f48c6afc09dbcd4c044a59dd58e4fb84f4f00
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 10, 2026.

Transparency log

Release files / create_python_app_core-0.3.0-py3-none-any.whl

Download URL create_python_app_core-0.3.0-py3-none-any.whl
Size 18.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c5f7af66668e3dda172b62cad038bfd2a844dcc2296a535636cb209339f5b429
BLAKE2b-256 checksum
How to use checksums
0ab78b91ebeb3af5cc9cf59e925fdfd1a90586a25a8ad5689806b0376796d54f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 10, 2026.

Transparency log

Release history Release notifications | RSS feed

0.3.3

2 release files

0.3.2

2 release files

0.3.1

2 release files

This release

0.3.0 This release

2 release files

0.2.12

2 release files

0.2.10

2 release files

0.2.6

2 release files

0.2.5

2 release files

0.2.4

2 release files

0.2.3

2 release files

0.2.2

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page