Przeglądaj źródła

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.
Chinesezjc 1 miesiąc temu
rodzic
commit
c4d6c25ffc

+ 2 - 2
.agents/notes/implemented/bug-fix/2026-07-31-code-runtime-python-settlement-fixes.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write .agents/notes/implemented/bug-fix/2026-07-31-code-runtime-python-settlement-fixes.md
-2026-07-31-code-runtime-python-settlement-fixes.md: 77f7f78cbd9b312a184d2a18cf66ceb1c3e52227
-2026-07-31-code-runtime-python-settlement-fixes.zh.md: b3dd8e8df05eb809774db961fdaaab5c9d56fdd4
+2026-07-31-code-runtime-python-settlement-fixes.md: 5101d660ca76b3644f1c87b717462e2297023c1b
+2026-07-31-code-runtime-python-settlement-fixes.zh.md: c2fb1f86c1bd0bd3c6682c5a925a38a80ff6fa11

Plik diff jest za duży
+ 0 - 0
.agents/notes/implemented/bug-fix/2026-07-31-code-runtime-python-settlement-fixes.md


Plik diff jest za duży
+ 0 - 0
.agents/notes/implemented/bug-fix/2026-07-31-code-runtime-python-settlement-fixes.zh.md


+ 5 - 3
packages/code-runtime/code-runtime-python/src/index.ts

@@ -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

+ 25 - 0
packages/code-runtime/code-runtime-python/tests/runtime.spec.ts

@@ -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

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików