Skip to content

angee.compose.runtime

Runtime composition discovery and generated-tree lifecycle.

Runtime discovers and validates addon composition, then delegates concrete Django model source emission to angee.compose.rendering.ModelRenderer. It owns the complete generated source map and its write, drift, and cleanup lifecycle under runtime/<label>/.

ModelContributions

python
class ModelContributions(NamedTuple)

Abstract source models one addon contributes, split by composition role.

owned

Models emitted as concrete runtime classes under the addon's label.

extensions

Same-row extensions that merge fields into another addon's model.

Runtime

python
class Runtime()

Own composition discovery and the generated runtime tree lifecycle.

One object owns the whole build-time lifecycle while concrete-model planning and rendering stay behind ModelRenderer:

  • render_sources — the seam: returns {relative path: text} for the whole runtime. Every other entry point renders through it.
  • emit — write that map to runtime_dir during the explicit angee build pass (resets, prunes orphans).
  • build — emit stale sources, project the composed folder addons into the host dependency group, then materialize applicable addon-owned migrations onto the downstream Django graph.
  • is_current / check — disk vs the rendered map.
  • reset / clean — delete generated files behind the GENERATED_SENTINEL gate while preserving */migrations/.

Construction groups source models by emitted label, resolves extends extensions, and fails fast on field collisions, so an invalid composition never reaches emission.

__init__

python
def __init__(addons: Iterable[AppConfig],
             *,
             runtime_dir: Path,
             runtime_module: str = "runtime",
             project_dir: Path | None = None) -> None

Create a runtime composition for addons and runtime_dir.

from_django

python
@classmethod
def from_django(cls) -> Runtime

Return a runtime using installed addons and Django settings.

ANGEE_RUNTIME_DIR is the single owner of where the runtime lives. angee.compose.settings always sets it. A host that installs the composer without it is misconfigured, so fail loudly here rather than let a caller silently skip emission and surface a cryptic missing-model error later in app population.

render_sources

python
def render_sources() -> dict[Path, str]

Return generated runtime source files keyed by relative path.

The composition seam. The returned map (path relative to runtime_dir → file text) is the single source of truth that emit writes and check compares against disk. It contains the generated package __init__ plus, per label, an empty app/migrations __init__ and a models.py, plus (when a consumer addon contributes through a permissions.extends.zed) the merged effective zed under permissions/<package>.zed — see angee.compose.permissions. Migrations themselves are never rendered here — Django's addon materialization and later makemigrations own runtime/<label>/migrations/ (redirected via MIGRATION_MODULES), and cleanup preserves it.

emit

python
def emit() -> None

Reset the runtime and write all sources (destructive; explicit).

Used by the angee build command: it runs the generated-tree cleanup gate and prunes stale files (e.g. a removed addon's leftover label), then rewrites.

runtime_migrations

python
def runtime_migrations() -> RuntimeMigrations

Return the addon migration materializer for this composed runtime.

addon_dependency_group

python
@property
def addon_dependency_group() -> AddonDependencyGroup

Return the dependency projector for this runtime's composed host.

build

python
def build() -> AddonDependencyGroupResult

Emit stale sources, project addon dependencies, then materialize migrations.

import_generated_models

python
def import_generated_models() -> None

Import generated concrete model modules for all emitted labels.

emit_if_stale

python
def emit_if_stale() -> bool

Write the runtime when it drifts from the sources, on every boot.

Called from the composer's import_models in app-populate phase 2. Write-only and idempotent: it never resets or cleans, so a present-but- stale runtime is healed file by file and a corrupted or non-Angee directory can never abort app population through the destructive cleanup gate. Orphaned files from a removed addon are pruned by the explicit angee build (which calls emit). Returning early when current keeps boots fast and avoids churning files the running process (and Django's autoreloader) already imported.

configure_migration_modules

python
def configure_migration_modules() -> Runtime

Redirect migrations for emitted runtime app labels.

is_current

python
def is_current() -> bool

Return whether the on-disk runtime matches the rendered sources.

check

python
def check() -> None

Raise for generated-source, dependency-group, or migration drift.

reset

python
def reset() -> None

Clear generated runtime sources while preserving migrations.

clean

python
def clean() -> None

Delete generated runtime files while preserving migrations.

model_contributions

python
def model_contributions(app_config: AppConfig) -> ModelContributions

Return source models and extensions declared by one Django app config.

Runtime owns this scan because addons deliberately remain plain Django AppConfig classes with no shared Angee base method to delegate to.

Released under the AGPL-3.0 License.