← board

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

  1. SystemExit = class(Exception) in pylib. Raising it would make every except Exception: catch an exit, which CPython deliberately does not do (it is a BaseException). The hierarchy needs a BaseException level first.
  2. 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 an except 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.