fix(code-runtime-python): reserve the log array envelope byte, unblock SIGXCPU before re-raise

Addresses the review's two remaining code warnings and the three suggestions:
- Log ledgers (host and child) start one byte below the budget, reserving the
  serialized outer-array envelope (two brackets and n-1 commas over n entries'
  separators); the exact-zero test moves to maxLogBytes 104 and a new exact-limit
  case pins that maxLogBytes 5 admits ['a'] (5 bytes) while 4 truncates to the
  marker alone.
- die_if_cpu_exhausted unblocks SIGXCPU (pthread_sigmask SIG_UNBLOCK, captured at
  import, None-guarded for Windows) before re-delivering it, so a program that
  masks SIGXCPU, burns past the soft limit, and returns is still classified as a
  timeout; a regression test pins the masked path.
- ast.parse passes filename="<model>" so parse-time syntax diagnostics carry the
  same source label as compile and runtime tracebacks; the syntax-error test
  asserts the label.
- The NUL-escape test comments use the true six-byte JSON escape \u0000 instead
  of the caret notation; the README Known Limitations (en + zh) records that
  PID-reuse protection is inert on macOS; a combined-rebind regression test pins
  BaseException plus the traceback reporter rebinding together.
This commit is contained in:
Chinesezjc
2026-08-31 14:41:08 +08:00
committed by Tianyi Cui
parent 96597c5ed8
commit 4e0d77c1d6
6 changed files with 141 additions and 12 deletions
@@ -969,7 +969,14 @@ export class PythonCodeRuntime extends CodeRuntime {
const logs: string[] = []
// One host-side ledger covers normal frames, forged frames, and stray stdout bytes.
let logBudget = this.config.maxLogBytes
// The ledger starts one byte below maxLogBytes: each entry is charged its
// JSON-string cost plus one separator byte, and the serialized outer logs
// array adds one more byte of envelope (two brackets and n-1 commas over n
// entries' separators), so a result that exactly exhausts the ledger would
// serialize to maxLogBytes + 1. Reserving that byte keeps an admitted
// result within the configured cap; the truncation-marker entry is
// envelope, not payload, and rides uncharged.
let logBudget = this.config.maxLogBytes - 1
let logsTruncated = false
const admit = (text: string): void => {
// Post-truncation admits are no-ops: once the ledger has truncated, the