fix(python): name the program in tracebacks and run it in its own namespace - #425
mutewinter wants to merge 3 commits into
Conversation
…mespace
The program was pasted into the worker's wrapper script, so every traceback named /tmp/_jb_script.py at a line four hundred past the program's own, for -c code and script files alike, and the wrapper's helpers sat in the program's globals. The program is compiled under its own name (<string>, <stdin>, or the script path as typed) and run in a fresh __main__ namespace, the wrapper's frame is dropped from what is printed, __file__ and sys.path[0] are set for a script file as CPython sets them, and sys.exit("message") prints the message before exiting 1.
|
@mutewinter is attempting to deploy a commit to the Vercel Labs Team on Vercel. A member of the Team first needs to authorize it. |
🤖 auto-maintain reviewAutomated, advisory triage for
Review panel: 🟡 medium highest severity just-bash maintainer code review: 🟡 medium
General code review: 🟡 medium
Adversarial security: 🟡 medium
Adversarial security (second opinion): 🟡 medium
Standard Bash and host portability: 🟡 medium
Posted by auto-maintain. This automated code review is advisory; a human maintainer makes the call. |
A fresh dict was the program's namespace, so sys.modules['__main__'] stayed the wrapper: pickle could not find a class the program defined, and unittest.main() and doctest.testmod() found nothing. The program now runs in a types.ModuleType('__main__') registered in sys.modules. A compile-time error, which has no program frame, prints from the exception alone the way CPython prints a SyntaxError, rather than naming the wrapper; every non-None, non-integer sys.exit value prints and exits 1, including '' and 0.0; and the worker is told where the program came from (source: file, stdin, inline) rather than inferring it from the file name, so a script called <stdin> is still a file.
|
All four of the maintainer/general findings were right and are fixed in the second commit, with a test for each: the program now runs in a The PEP 263 finding does not hold on the vendored 3.13: |
…ource reaching the compiler as written The traceback and namespace cases move from python3.files.test.ts into python3.tracebacks.test.ts, registered in the fork pool like the other real-worker suites, so the two python3 PRs open at once no longer append to the same file. Five cases pin a consequence of compiling the program from its own string literal rather than pasting it into the indented wrapper: a triple-quoted string keeps its lines unindented from a script, stdin, and -c; code the program builds in a string and runs with exec() no longer gains an IndentationError; and a module-level `from __future__` import is honored. All five fail on the wrapper as it was.
|
Pushed 5bf12fe, two things. The traceback cases now live in While looking at #431, which reports the wrapper's four-space indentation rewriting the contents of triple-quoted strings, I checked whether this change already covers it: it does, since the program reaches |
Problem
A three-line program fails at line 413 of a file nobody wrote. A script file gets the same treatment:
python3 report.pyreports/tmp/_jb_script.pytoo, so the path and line number are wrong in every traceback the runtime produces, which is the one output an agent debugging its script reads most carefully. Related:sys.exit("message")exits 1 silently where CPython prints the message,__file__is unset for a script,sys.path[0]is the wrapper's/tmp, and the wrapper's own imports leak into the program:The same paste also rewrites the program's data. Every line goes into the wrapper's
try:block with four spaces in front, including the lines inside a triple-quoted string, so a multi-line literal silently gains content, and a program that builds code in a string and runs it withexec()fails with anIndentationErrorit did not write. #431 reports that half independently, with the same diagnosis; this PR fixes it by the same means and pins it with the tests below.Cause
runPythonpastesinput.pythonCodeinto the wrapper'stry:block after roughly four hundred lines of path shims and the HTTP bridge, and runs the whole file:So the program is part of
/tmp/_jb_script.py, its line numbers are offset by the wrapper's length (which varies with the environment variable count), and it shares the wrapper's module globals.traceback.print_exc()then prints the wrapper's own frame first. The existing tests assert on stdout and onValueErrorappearing in stderr, never on the file or line a traceback names.Fix
The program is compiled with
compile(code, name, 'exec')under its own name (<string>for-cand-m,<stdin>for a program read from stdin, the script path as typed for a file) and run withexecin a fresh__main__namespace.python3.tspasses the name as a newfileNameonWorkerInput. For a script file,__file__is the absolute path andsys.path[0]is/hostplus the script's directory, so a sibling module imports without the/hostprefix the current test comment documents; for inline code,sys.path[0]is''as CPython sets it. Theexceptclause prints frome.__traceback__.tb_next, which drops the wrapper's frame, andsys.exitwith a non-integer code prints it to stderr.JSON.stringifyproduces the Python literal: JSON's escapes are a subset of Python's, and the wrapper is written to MEMFS as UTF-8.Scope
Unchanged: the path shims, the HTTP bridge and
jb_http,sys.argv, exit codes, andrunpybehavior for-m, whose tracebacks already named real files becauserunpycompiles them itself.Not addressed, deliberately: frames inside the shims themselves (
_redir_openand friends) still appear below the program's frame when anOSErrorcomes through one, andBaseExceptions other thanExceptionstill propagate through the wrapper. Both are small; say the word and either is a few lines.Tests
python3.tracebacks.test.ts, a new file registered in the fork pool beside the other real-worker suites:-ccode fails atFile "<string>", line 3; a script fails atFile "report.py", line 4andline 2, in failwith no wrapper frame; a syntax error prints as CPython prints it;__file__is/tmp/app/main.pyandimport helperfinds a sibling without touchingsys.path; the program issys.modules['__main__'], so a class it defines pickles; a coding cookie is accepted; the program's non-dunder globals are[]; a triple-quoted string keeps its lines unindented from a script, from stdin, and from-c; code the program builds in a string and runs withexec()runs; and a module-levelfrom __future__import is honored.python3.env.test.ts:sys.exit('error message')writeserror message\nto stderr, and other non-integer codes print and exit 1. Ten of the twelve traceback cases fail against the wrapper as it is onmain, verified by running the file against a worker built frommain'sworker.tsin a separate worktree, not inferred. The existingrunpyand/host/tmpsys.pathtests still pass unchanged.Suite: the moved and new cases plus
python3.files.test.ts, 28 passed.Authored with Claude Opus 5