test(code-runtime-python): pin the pump reader against a class-attribute rebind; correct the staging comment

The review's three remaining items:
- A regression test rebinds __main__.ProtocolChannel.read_frame_async and asserts
  a binding reply still round-trips (the pump's reader is a bound method
  captured by _run before the program runs).
- The settlement note (en + zh) records that send_done's frame-shape check uses
  _run's bound _str/_isinstance.
- The staging-removal comment no longer claims teardown retries tracked state:
  teardown deliberately does not sweep staging, so a removal failure is the one
  case the gone-by-settlement contract degrades on.
Pairing re-recorded.
This commit is contained in:
Chinesezjc
2026-08-31 14:49:15 +08:00
committed by Tianyi Cui
parent 8ed96b1560
commit c4d6c25ffc
5 changed files with 34 additions and 7 deletions
@@ -1704,9 +1704,11 @@ export class PythonCodeRuntime extends CodeRuntime {
// Swallows only a failure to remove this run's staging directory —
// `force` already absorbs a missing one, so what remains is a
// filesystem-level refusal. The run's own outcome is already decided
// and must still be delivered, and teardown retries what stays
// tracked; the directory holds no secret, only a copy of two
// checked-in scripts.
// and must still be delivered; the directory holds no secret, only a
// copy of two checked-in scripts. teardown deliberately does not
// sweep staging (its staging is cleared inside each run's settle), so
// a removal failure here is the one case the "gone by settlement"
// contract degrades on.
}
resolve({ ...result, logs })
// Mark the fiber quiescent for THIS run: drop it from `live` and resolve
@@ -696,6 +696,31 @@ describe('PythonCodeRuntime — programs and bindings', () => {
expect(result.value).toEqual({ echoed: { n: 1 } })
}, 15_000)
it('keeps the reply pump reading when the read_frame_async class attribute is rebound', async () => {
// _pump_replies' frame reader is a bound method captured by _run before the
// program runs and passed in as an explicit argument, so a program rebinding
// `__main__.ProtocolChannel.read_frame_async` cannot redirect the pump (a
// body-local `channel.read_frame_async` lookup would resolve the rebound
// class attribute, since the pump starts after the program's top-level
// statements).
const { runtime } = await setup()
const result = await runtime.run({
program: [
'import __main__',
'async def boom(*a, **k):',
' raise RuntimeError("hijacked reader")',
'__main__.ProtocolChannel.read_frame_async = boom',
'first = await tools.echo({"n": 1})',
'return first',
].join('\n'),
bindings: tools({
echo: async args => ({ echoed: args as CodeJsonValue }),
}),
})
expect(result.error).toBeUndefined()
expect(result.value).toEqual({ echoed: { n: 1 } })
}, 15_000)
it('keeps the rejection contract when _BindingRejection is rebound', async () => {
// `dispatch`'s except clause resolves `_BindingRejection` at call time; a
// program that rebinds `__main__._BindingRejection = ValueError` would