Skip to content

Latest commit

History

History
271 lines (203 loc) · 10.3 KB

File metadata and controls

271 lines (203 loc) · 10.3 KB

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

importasynciofromcreate_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())

Scaffold with the installer API

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

Resolve a template source

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) # fastapi

Download a repository into the cache

fromcreate_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)

Load template configuration

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

Check environment info

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

Validate the Python version

fromcreate_python_app_coreimportcheck_python_versioncheck_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

SignatureDescription
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

NameDescription
__version__Installed package version string.
CPA_USER_AGENTHTTP User-Agent sent to PyPI (create-python-app-core/<version>).
NON_EMPTY_DIR_ERROR_CODEStable code for NonEmptyTargetDirectoryError (CPA_NON_EMPTY_TARGET_DIR).

Types

TypeShape
ResolvedSourcekind (github | file | slug | git), url, ref, subdir, local_path
CacheMetaurl, ref, fetched_at, commit
CpaConfigname, custom_options, raw
CpaCustomOptionkey, type, message, default
CpaErrorBase exception with .code attribute
ConfigParseErrorInvalid cpa.config.json (code: CPA_CONFIG_PARSE)
ManifestLoadErrorMissing template directory (code: CPA_MANIFEST_LOAD)
PackageManagerFallbackErrorPackage manager fallback failure (code: CPA_PM_FALLBACK)
ScaffoldAbortedErrorScaffold failed mid-run (code: CPA_ABORTED)
NonEmptyTargetDirectoryErrorTarget directory not empty (code: CPA_NON_EMPTY_TARGET_DIR)

create_python_app options dict

KeyTypeDefaultDescription
templatestr""Primary template specifier (URL, file://, or slug).
addonslist[str][]Additional template layers applied after the base template.
extendlist[str][]Extension layers applied last (later wins on conflicts).
forceboolFalseAllow scaffolding into a non-empty directory.
installboolTrueRun uv sync when pyproject.toml is present.
offlineboolFalseUse cached repos only; raise on cache miss.
refreshstrenv / "stale"Cache refresh mode: always, stale, or manual.
keep_on_failureboolFalseKeep the partial project directory when scaffolding fails.
cache_dirstr | PathNoneOverride the default cache root.
setdict{}Jinja context overrides (merged into projectName and custom option defaults).

Environment Variables

All CPA_* variables read by the core engine:

VariableDefaultDescription
CPA_CACHE_DIR~/.cache/cpaRoot directory for cloned repository cache entries.
CPA_REFRESHstaleDefault cache refresh mode: always, stale, or manual.
CPA_REFRESH_AFTER_HOURS24Hours before a stale cache entry is refreshed.
CPA_SKIP_GITunsetSet to 1 to skip git init and block all git subprocess calls.
CPA_STRICT_REPROunsetSet to 1 to require a full 40-character commit SHA in ?ref= query params.

Error Codes

Stable machine-readable codes on CpaError.code:

CodeException classWhen raised
CPA_ERRORCpaErrorGeneric base error (default).
CPA_CONFIG_PARSEConfigParseErrorMalformed or invalid cpa.config.json.
CPA_MANIFEST_LOADManifestLoadErrorTemplate directory not found on disk.
CPA_PM_FALLBACKPackageManagerFallbackErrorPackage manager fallback failure.
CPA_ABORTEDScaffoldAbortedErrorScaffold failed (template render, unexpected error, etc.).
CPA_NON_EMPTY_TARGET_DIRNonEmptyTargetDirectoryErrorTarget directory exists and is not empty.
CPA_GITCpaErrorGit subprocess failed or git not found.
CPA_SKIP_GITCpaErrorGit operation attempted while CPA_SKIP_GIT=1.
CPA_FILECpaErrorfile:// source path does not exist.
CPA_OFFLINECpaErrorOffline mode with no cached copy of the repository.
CPA_STRICT_REPROCpaError?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:

ModuleResponsibility
__init__.pyBarrel export and public API surface
api.pycreate_python_app, version checks, env info, PyPI lookup
installer.pyscaffold_project orchestration, uv sync, git init
loaders.pyFile discovery, .template / .append processing, layer merge
pyproject_merge.pyDeep-merge pyproject.toml across template layers
paths.pyURL resolution (GitHub, file://, slugs, ?ref=, ?subdir=)
git_cache.pyClone/pull with cache, refresh modes, offline support
config.pyReads optional cpa.config.json for custom CLI prompts
errors.pyTyped CpaError hierarchy with stable codes

Related


License

MIT (c) Create Python App Contributors