Measured 2026-09-19
import sys
try:
sys.exit(3)
except SystemExit as e:
print("caught", e.code)
print("end")
CPython prints caught 3 and end and exits 0. NilPy exits 3 at the
sys.exit and prints nothing, so the except never runs. A finally: around
it does not run either.
What is already right
pysys_exit takes a Variant and follows CPython's status rules: no argument
or None exits 0, an int exits with it mod 256, a bool exits 0/1, and anything
else is printed to stderr and exits 1.
test/test_nilpy_sys_exit_status_follows_cpython.npy pins that, one process per
case. Before 2026-09-19 the parameter was an Integer, so sys.exit("msg")
exited silently with the string pointer's low byte as the status.
Why it is not one line
SystemExit = class(Exception)in pylib. Raising it would make everyexcept Exception:catch an exit, which CPython deliberately does not do (it is a BaseException). The hierarchy needs a BaseException level first.- Nothing handles an uncaught one. The unhandled path is machine code emitted
per backend in exception_emit.inc (x64, i386, aarch64, arm32, riscv, wasm),
and it prints
Unhandled exception: <class>: <msg>then exits with EXITCODE_UNHANDLED_EXCEPTION. A NilPy main program could instead be wrapped by the frontend in anexcept SystemExit, which exits with its code: one frontend change instead of six backend ones.
Who meets it
Test harnesses and argparse callers that catch the exit. test/test_nilpy_argparse_tsp_surface runs one process per case for exactly this reason. That Space Program only exits, so it does not need this.