Call the repository compiler from Python
For prerequisites and package availability, see Install Apizr.
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.
First prepare operator-local.json using the complete local grant, with the canonical absolute path of repository.
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.operator_policy import load_operator_policy
from apizr.repository_interfaces.output import write_bundle
from apizr.repository_readiness import RepositoryReadinessPolicy
operator = load_operator_policy(Path("operator-local.json"))
prepared = prepare_exposure(
Path("repository"),
operator_policy=operator,
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, *, operator_policy=None, 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, application=None, operator_policy=None, 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.
Authorize source analysis
operator_policy is an explicitly loaded OperatorPolicy. Omission raises
AuthorizationDenied with a fixed structured decision. Filesystem scan,
discover, analyze_repository and graph_repository share this gate. Local
roots match canonical absolute paths; relative arguments resolve against the
caller's working directory. No path component may be a symlink. Git analysis
accepts its live GitSnapshot, bound to the actual acquisition and commit.
See Git acquisition.
Admission does not authorize exposure, execution, image build, signing or publication. Existing contracts, selection refusals and bounds remain. Primitives accepting already supplied bytes/artifacts do not read a root and remain outside this gate. Arbitrary Python execution is not an authorization sandbox.
Pass an optional apizr.application.ApplicationConfig as application= for
explicit application inputs.
Preparation captures declared resource bytes while the authorized root is open;
rendering never reopens it. This also accepts an acquired Git snapshot while its
context is active. The CLI still refuses --git with --project and never loads
remote configuration implicitly.