ReleaseMicrosoftMicrosoftpublished Jun 2, 2026seen 4d

microsoft/bocpy v0.7.0

microsoft/bocpy

Open original ↗

Captured source

source ↗
published Jun 2, 2026seen 4dcaptured 9hhttp 200method plain

0.7.0 - SBOM and Dependency Auditing

Repository: microsoft/bocpy

Tag: v0.7.0

Published: 2026-06-02T11:11:47Z

Prerelease: no

Release notes: Cown-lifecycle correctness fixes — three use-after-free paths in the `CownCapsule pickle / acquire / noticeboard machinery now hold the inner BOCCown` alive across the writer's wrapper drop — plus supply-chain hardening: pinned and hash-verified Python dependencies, SHA-pinned GitHub Actions, dependabot coverage, vulnerability scanning, and PEP 770 SBOMs embedded in every wheel.

New Features

  • PEP 770 SBOMs in every wheel — every wheel built by

.github/workflows/build_wheels.yml now embeds a CycloneDX 1.6 _ JSON SBOM under -.dist-info/sboms/bocpy.cdx.json. Generation runs inside cibuildwheel's repair step on every platform (Linux auditwheel, macOS delocate, Windows direct injection) via the new stdlib-only scripts/build_sbom.py. The inject subcommand rewrites the wheel's RECORD` atomically (temp file + rename).

  • SBOM verification in CI — the new `verify_sboms` job in

build_wheels.yml re-downloads the extracted SBOM artifact and runs two checks: scripts/validate_sbom.py (stdlib-only structural validator pinning bocpy's wire format) and grype _ (third-party SBOM scanner) with --fail-on high. A separate sboms artifact is also uploaded by the merge` job for downstream consumers.

  • ``bocpy.__version__`` — a runtime version attribute derived

from `importlib.metadata.version("bocpy"), with a PackageNotFoundError fallback. Exported from bocpy.__all__ and documented in __init__.pyi. pyproject.toml` remains the single source of truth for the version.

  • New documentation — :doc:sbom walk-through covering the

embedded SBOM format, extraction recipes, and verification commands.

  • ``wait(noticeboard=True)`` final-state capture — :func:wait

now accepts a `noticeboard keyword that returns the final noticeboard contents as a plain dict at shutdown (after the noticeboard thread exits, before the entries are freed). Useful for surfacing an early-stopping result, last error, or aggregated counter that a behavior deposited just before the runtime quiesced, replacing the older send / receive handshake that earlier examples used. Combined with stats=True it returns a new :class:WaitResult NamedTuple (also exported from bocpy.__all__) carrying both snapshots. The examples/prime_factor.py` example was migrated to the new pattern.

Bug Fixes

  • Cown-in-cown use-after-free — a `Cown` embedded inside

another cown's value, a message-queue payload, or a noticeboard snapshot was previously freed when the writer's local wrapper dropped, because pickle bytes carry no refcount on their own. `CownCapsule_reduce now takes an inheriting COWN_INCREF that _cown_capsule_from_pointer_inheriting consumes on unpickle, so the inner BOCCown survives until the consumer drops its decoded wrapper. Affects every cross-cown reference shape — see the new TestCownInCown` class for the full container-shape fuzz.

  • Acquire-failure poisoned-state — when `pickle.loads` failed

partway through `cown_acquire, the cown was left in a half-acquired state with the encoded bytes still in place. A retry would re-run pickle against bytes whose embedded inherited refs had already been partially consumed by pickle's error path, risking dereferences of freed BOCCown* pointers. The cown's xidata is now recycled on the failure path and a guard at the top of cown_acquire rejects any future acquire with a deterministic RuntimeError`; the worker recovery arm surfaces it on the failing behavior's result cown.

  • Noticeboard hidden-cown audit — when a noticeboard value

reached a `Cown via a route the pin walker cannot see — custom __reduce__ / __getstate__, copyreg.dispatch_table, closure capture, module-level cache — the borrowing reconstructor produced a token whose inner BOCCown was not held alive by the entry's pin set, leaving the next reader to UAF after the writer's wrapper dropped. A per-thread borrowing context (BOC_NB_CTX) now audits every CownCapsule_reduce against the caller's pin set during the noticeboard write pickle and fails the whole notice_write / notice_update` closed if any cown is unaccounted for.

  • `UnicodeDecodeError` on non-UTF-8 Windows locales

Behaviors.start read worker.py with open(path), which picks up locale.getpreferredencoding(False). On cp1252 (English Windows) the UTF-8 em-dashes in the worker source were silently mojibake-d; on cp949 (Korean Windows) the read failed with UnicodeDecodeError: 'cp949' codec can't decode byte 0xe2 and bocpy could not start at all (reported in #14 _ by @Forthoney _). Fixed by passing encoding="utf-8" explicitly in Behaviors.start, and the same fix was applied to every other open() site in the repo that reads or writes text known to contain non-ASCII bytes (sphinx/source/conf.py, examples/sketches.py x2, export_module.py`).

  • Silent worker-startup failures — `Behaviors.start_workers

ran `interpreters.create() and interpreters.run_string() on the worker thread without a try/except, so a failure in either killed the thread without ever replying on boc_behavior. The parent's bounded receive() then timed out with no diagnostic. Both calls are now wrapped, and every failure path sends a formatted traceback over boc_behavior` so the parent sees a structured error instead of a timeout.

  • Silent worker bootstrap import failures — the generated

bootstrap script that loads the user module into each worker sub-interpreter is now wrapped in a top-level try/except. Any `BaseException is formatted with the user module name and sent over boc_behavior (falls back to sys.stderr if the message-queue send itself raises), then re-raised so run_string` reports it as well. Module-import failures that previously surfaced only as a worker-startup timeout now arrive as a proper traceback.

  • ``boc_sched_worker_pop_slow`` skipped ``popped_local`` — the

slow-path pending-fallback and WSQ-dequeue branches returned work without bumping `popped_local (the fast path always did), so the documented producer/consumer identity in :c:type:boc_sched_stats_t` was violated whenever the fairness arm fired or a worker entered the slow path directly.…

Excerpt shown — open the source for the full document.

Notability

notability 3.0/10

Routine library release, low traction