cpex — Rust-backed Python bindings
A native Python extension wrapping the cpex-core Rust runtime via PyO3.
Provides the canonical CPEX plugin lifecycle with await-based async APIs.
Requirements
- Python ≥ 3.10
- Rust toolchain (
rustup) - maturin (
pip install maturin)
Install
# From the bindings/python directory:
Quick Start
=
await
= await
# Always shut down to drain fire-and-forget tasks.
await
API
PluginManager(config_path: str)
Synchronous constructor. Reads the YAML config file, registers bundled APL
factories, and loads the config. Raises ValueError on missing file,
IO error, or config parse failure.
await manager.initialize()
Initialize all registered plugins. Must be called before invoke_hook.
await manager.shutdown()
Shut down all plugins and drain fire-and-forget background tasks. Call this before exit if you need fire-and-forget side effects to complete.
await manager.invoke_hook(hook_name, payload, extensions=None, context_table=None)
Invoke a hook by name. Returns a PipelineResult.
hook_name— e.g."cmf.tool_pre_invoke". Any hook name is accepted;cmf.*hooks use typedMessagePayload, others useGenericPayload.payload— JSON-compatibledict(str keys, depth ≤ 128).extensions— optionaldictof CPEX extensions fields.context_table— optionaldictfor stateful plugins.
Raises:
ValueError— payload conversion failure or config error.RuntimeError— plugin execution error.TimeoutError— wall-clock timeout exceeded (60 s default).
Policy denials do not raise — check result.continue_processing.
PipelineResult
| Attribute | Type | Description |
|---|---|---|
continue_processing |
bool |
False when a plugin denied |
violation |
dict | None |
Populated on deny |
errors |
list[dict] |
Per-plugin errors (on_error: ignore/disable) |
modified_payload |
dict | None |
Payload after transform-phase modifications |
modified_extensions |
dict | None |
Extensions after modifications |
metadata |
dict | None |
Optional telemetry metadata |
context_table |
dict |
Per-plugin state |
Shutdown Contract (Fire-and-Forget Tasks)
Plugins configured with mode: fire_and_forget run asynchronously.
Their side effects are not guaranteed until await manager.shutdown()
completes. In tests always await shutdown() before asserting
fire-and-forget side effects:
await
await # drain before asserting audit log
assert
Isolated Virtualenv Requirement
This package and the legacy ./cpex/ package share the import name cpex.
They must not be installed in the same virtualenv. Always use a
dedicated venv for the Rust-backed package.
Worker Threads
The tokio runtime thread count is controlled by:
CPEX_PY_WORKER_THREADS=4
Defaults to tokio's num_cpus when unset.
v1 Deferred Features
- Identity/delegation hooks (
identity_resolve,token_delegate): routed throughGenericPayloadin v1 — token fields are preserved in the raw dict but are not cryptographically validated. Typed constructors and token injection are planned for v2. - Cedarling PDP: available behind the
cedarlingCargo feature (--features cedarling), off by default.
Running Tests
Migration
See MIGRATION.md for a mapping from the legacy
pure-Python cpex framework to this Rust-backed package.