microsoft/bocpy v0.11.0
microsoft/bocpy
Captured source
source ↗v0.11.0 - Cross-module behaviors
Repository: microsoft/bocpy
Tag: v0.11.0
Published: 2026-06-17T01:56:07Z
Prerelease: no
Release notes: A behavior-dispatch release. @when becomes a runtime decorator backed by a content-addressed marshalled-code registry instead of a transpile-time call-site rewrite. A behavior's code object is marshalled once, stored under a hex content-hash key, and resolved by value inside each worker sub-interpreter — so behaviors may now live in any importable module, not just __main__. The transpiler shrinks to a bindings reducer that exposes the defining module's imports, classes, functions, and constants to workers, and captures are now declared explicitly as trailing default parameters.
New Features
- Cross-module behaviors — a
@whenbody defined in any importable
module now resolves on workers via the marshalled-code registry, lifting the previous __main__-only restriction. A new worker-importable bocpy_test fixtures package exercises cross-module resolution, dunder handling, key collisions, and chained-global cases.
- Behaviors in the REPL, `python -c`, and piped stdin — a
@whenbody
defined in a __main__ with no source file on disk now runs on workers. The runtime reduces the live interactive namespace to its imported modules (each guarded so an environment module that cannot load in a sub-interpreter, such as readline, is skipped rather than fatal), and an interactive behavior is validated at decoration to reference only builtins, imported modules, and explicit captures.
- `whencall(func, args, captures)` — the lower-level escape hatch behind
@when: registers a behavior function against cowns and explicit capture values without the decorator sugar.
Breaking Changes
- `@when` behaviors must declare their captures explicitly. Implicit
capture of enclosing-scope variables as free variables is no longer supported: a @when body may only reference its cown parameters, the values it captures as trailing parameters, and names resolvable at the defining module's scope (imports, module-level classes/functions, constants, and builtins). Capture an enclosing local by adding a trailing parameter with a same-named default — the canonical name=name recipe:
# before — factor captured implicitly from the enclosing frame @when(x) def b(x): return x.value * factor # after — factor captured explicitly @when(x) def b(x, factor=factor): return x.value * factor
The loop-snapshot form def b(c, i=i) and the rename form def b(c, x=y) continue to work unchanged. This is the migration that lets a behavior's code object be marshalled and resolved by value across worker sub-interpreters.
Improvements
- Decoration-time capture validation —
@whennow rejects malformed
behaviors where the mistake is made, with actionable messages: a bare trailing parameter (no default) raises TypeError naming the cown/param counts; a body closing over an enclosing-function local raises SyntaxError naming the variable and suggesting the name=name fix; and async def / generator behaviors raise SyntaxError. Computed defaults (k=expensive()) are allowed and snapshotted once at schedule time.
- Interactive traceback labels — behaviors defined interactively are
relabelled ` so two distinct interactive behaviors no longer collide under a shared / ` filename.
Documentation
- Reworded the :doc:
c_abiandmessagingpages and the downstream
consumer template to describe the worker bindings module and bindings reducer, replacing the retired transpile-and-rewrite vocabulary.
Tests
- New
test_registry.pycovering registry round-trips, key derivation,
capture validation, and Resolver dispatch. test_transpiler.py rewritten for the bindings reducer; the dead constant-tracking machinery and its tests were removed.
Internal
- New
boc_registry.c/boc_registry.hC subsystem storing marshalled code
objects under opaque hex keys. The transpiler is reduced to a bindings reducer (MainBindings / bind_*); the legacy call-site rewriter, skeleton fallback, and export_module.py were removed in a clean cut. Behavior keys are content-addressed with length-prefixed framing and 128-bit truncation.
Notability
notability 3.0/10Routine minor release, no major traction.