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-appinstead.
pip install create-python-app-coreOr with uv:
uv add create-python-app-coreimportasynciofromcreate_python_app_coreimportcreate_python_appasyncdefmain() ->None:
awaitcreate_python_app(
"my-app",
{
"projectName": "my-app",
"template": "file:///path/to/template",
"install": True,
},
transform_options=lambdaopts: asyncio.sleep(0, result=opts),
)
asyncio.run(main())fromcreate_python_app_coreimportscaffold_projectscaffold_project(
"my-app",
template="file:///path/to/template",
addons=[],
extend=[],
install=True,
force=False,
offline=False,
)fromcreate_python_app_coreimportresolve_source, get_template_dir_pathsource=resolve_source(
"https://github.com/Create-Python-App/cpa-templates?ref=main&subdir=fastapi"
)
print(source.kind) # githubprint(source.ref) # mainprint(source.subdir) # fastapifromcreate_python_app_coreimportresolve_source, download_repositorysource=resolve_source("https://github.com/org/my-template")
root=download_repository(source, refresh="stale", offline=False)
template_dir=get_template_dir_path(source, root)frompathlibimportPathfromcreate_python_app_coreimportload_cpa_configcfg=load_cpa_config(Path("/path/to/template/cpa.config.json"))
foroptincfg.custom_options:
print(opt.key, opt.default)fromcreate_python_app_coreimportprint_env_infoprint_env_info()
# Prints Python, platform, uv, and git info. Then exits.fromcreate_python_app_coreimportcheck_python_versioncheck_python_version(">=3.12", "my-tool")
# Exits with code 1 if the interpreter does not match.All public exports from create_python_app_core:
| 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. |
| 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). |
| 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) |
| 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). |
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. |
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. |
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)
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 |
create-awesome-python-app-- Interactive CLI built on this core- Create Python App -- Monorepo
- Templates catalog
MIT (c) Create Python App Contributors