Call the repository compiler from Python
Available in the development checkout; not yet part of published 0.3.0.
apizr.compiler provides the orchestration used by apizr readiness,
apizr expose plan and apizr expose build rest|mcp. It requires only the base
installation. It does not discover plugins or import optional transport servers.
Example
Given repository/sample.py containing
def add(a: int, b: int = 1) -> int: return a + b, run this Python code from its
parent directory. The output directories must be absent or empty.
The installed-wheel smoke test executes this exact example outside the checkout.
from pathlib import Path
from apizr.compiler import prepare_exposure, render_bundle
from apizr.exposure import ExposurePolicy
from apizr.repository_interfaces.output import write_bundle
from apizr.repository_readiness import RepositoryReadinessPolicy
prepared = prepare_exposure(
Path("repository"),
readiness_policy=RepositoryReadinessPolicy.model_validate(
{"execution": {"modes": ["direct"]}}
),
policy=ExposurePolicy.model_validate(
{
"selection": {"include": ["python:sample:add"]},
"interfaces": ["rest", "mcp"],
"execution": {"allowed": ["direct"]},
}
),
)
report, plan = prepared.readiness, prepared.plan
rest = render_bundle(prepared, interface="rest")
mcp = render_bundle(prepared, interface="mcp")
write_bundle(Path("python-rest"), rest)
write_bundle(Path("python-mcp"), mcp)
Analysis runs once. Both renderings use the retained source bytes even if the
repository changes afterward. Rendering returns a dict[str, bytes] without
writing files or starting a server. write_bundle is the existing staged writer:
it checks paths, refuses symlinks and nonempty destinations, and publishes the
complete bundle without replacing existing contents.
Operations and contracts
| Operation | Parameters | Result |
|---|---|---|
assess_readiness(root, *, scan_policy=None, graph_policy=None, readiness_policy=None) |
str or Path, existing ScanPolicy, GraphPolicy, RepositoryReadinessPolicy |
Existing RepositoryReadinessReport, including diagnostics and exit_code |
prepare_exposure(root, *, policy, scan_policy=None, graph_policy=None, readiness_policy=None) |
Same analysis policies plus an explicit ExposurePolicy |
PreparedExposure retaining evidence, readiness, policy and plan |
render_bundle(prepared, *, interface, execution_policy=None, runtime_image=None) |
Prepared context, "rest" or "mcp", existing execution policy/image models |
Existing bundle filenames and bytes |
PreparedExposure is an in-memory container for existing contracts, not a new
serialized schema. Its evidence is the existing RepositoryEvidence with
catalog, graph and retained sources. Canonical serializers remain
report_bytes, plan_bytes, catalog_bytes and graph_bytes in their existing
packages. For already saved Catalog/Graph artifacts, continue to use
apizr.repository_readiness.assess_repository; the repository-first operation
intentionally performs discovery.
Readiness alone needs no exposure selection. To reuse analysis for a plan and
bundles, call prepare_exposure once and use its readiness result; do not call
assess_readiness first unless you intend a separate discovery. Default readiness
still permits local-process/OCI contracts, so the example explicitly requests
direct readiness as well as direct exposure. Policies remain independent; the
compiler does not merge them or widen the selected execution modes.
Optional scan_policy and graph_policy retain their existing bounds. CLI
--exclude-dir adds to the default exclusions; a Python ScanPolicy with
excluded_directories explicitly supplied replaces that field, so include
ScanPolicy().excluded_directories when you want the same additive behavior.
Errors and execution boundaries
Operations do not print or exit the process. Invalid policy/input validation
raises the existing ValueError subclasses (including Pydantic
ValidationError); filesystem and decoding errors propagate. Exposure selection
raises ExposureRefused with structured diagnostics. Rendering preserves
BundleRefused and PolicyRefused. Readiness can return a report with nonzero
exit_code; callers decide how to handle it. The CLI retains its existing error
messages and exit-code translation.
Explicit selection is required for a nonempty bundle. An empty selection can produce an empty plan but cannot produce a bundle. Ineligible or unknown selected capabilities are refused. No source imports, package builds or project execution occur during analysis, planning or rendering.
Omitting execution_policy selects direct generation; ExecutionPolicy selects
local-process and ExecutionPolicyV2 selects OCI with an explicit RuntimeImage.
The chosen backend must be allowed by the plan: there is no fallback. These
operations preserve existing boundaries and do not start/probe backends, pull
images, resolve remote Git repositories or publish services. Runtime optional
dependencies are needed when running the generated service, not when rendering.
See exposure policies for the three
independent policy roles and existing limitations.