diff --git a/packages/code-runtime/code-runtime-python/py/bootstrap.py b/packages/code-runtime/code-runtime-python/py/bootstrap.py index 90f108b4e2..1995ba0ad2 100644 --- a/packages/code-runtime/code-runtime-python/py/bootstrap.py +++ b/packages/code-runtime/code-runtime-python/py/bootstrap.py @@ -735,12 +735,6 @@ async def _run(channel: ProtocolChannel) -> None: # with no done frame, misreporting the run as a `worker-exit`. A frame local # is not reachable by `__main__._X = ...`, so the catch is immune. _BaseException = BaseException - # `RuntimeError` is likewise bound into a local for the reply pump's catch: - # a rebind of `__main__.RuntimeError` would otherwise make the pump's - # `except RuntimeError` not match a closed-loop scheduling failure, killing - # the pump and stranding every later reply. - _RuntimeError = RuntimeError - # 1. Boot handshake. boot = channel.read_frame() if boot is None or boot.get("type") != "boot": @@ -1122,12 +1116,6 @@ async def _pump_replies( pending: dict[int, tuple[asyncio.AbstractEventLoop, asyncio.Future[Any]]], pending_lock: "threading.Lock", ) -> None: - # The exception class the closed-loop catch below resolves is bound into a - # LOCAL here. This bootstrap IS `__main__`, so `__main__.RuntimeError = ...` - # would otherwise rebind the module global the `except RuntimeError` clause - # resolves at runtime, and a closed-loop scheduling failure would then escape - # the catch, killing the pump and stranding every later reply. - _RuntimeError = RuntimeError """Background task: read reply frames and settle pending futures. Cancelled after ``done`` is posted. Unknown ids and post-settlement replies @@ -1143,6 +1131,14 @@ async def _pump_replies( so a reply cannot race the claim that registers its id. """ + # The exception class the closed-loop catch below resolves is bound into a + # LOCAL here, after the docstring, before any model code runs. This bootstrap + # IS `__main__`, so `__main__.RuntimeError = ...` would otherwise rebind the + # module global the `except RuntimeError` clause resolves at runtime, and a + # closed-loop scheduling failure would then escape the catch, killing the + # pump and stranding every later reply. + _RuntimeError = RuntimeError + def complete(fut: asyncio.Future[Any], ok: bool, value: Any, message: Any) -> None: # Runs on the Future's own loop. `done()` re-checked here because # cancellation or a duplicate reply may have settled it between the pop @@ -2235,7 +2231,18 @@ def _join_bounded(lines, max_bytes: int) -> str: return "".join(chunks) -def _done_with_value(value: Any, max_value_bytes: int) -> dict[str, Any] | str: +def _done_with_value( + value: Any, + max_value_bytes: int, + # Bound as DEFAULT ARGUMENTS so they are captured at import time, before + # model code runs: `_done_with_value` runs AFTER the program (which is + # `__main__`) may have rebound `__main__._check_done_value` or + # `__main__._encode_json_plain`, and a module-global lookup at call time + # would let a one-line rebind rewrite a legitimate success into an + # `exception`. Defaults are evaluated at def time, so they are the originals. + _check_done_value: Any = _check_done_value, + _encode_json_plain: Any = _encode_json_plain, +) -> dict[str, Any] | str: """Build the terminal done frame under the seam's lossless-JSON contract. A completion value returned by the program (``None`` when it returns