Python/sysmodule.c
cpython 3.14 @ ab2d84fe1023/Python/sysmodule.c
The sys module. Every attribute that Python code reads from sys is
either set here at interpreter startup or updated dynamically by functions
in this file. The top half (~lines 1 to 2200) is the function table:
C implementations for sys.exit, sys.getframe, sys.exc_info,
sys.intern, sys.settrace, sys.setprofile, sys.audit, and dozens
of smaller accessors. The bottom half (~lines 2200 to 4522) is the
initialization path: _PySys_InitMain walks the process environment and
the interpreter config to populate sys.argv, sys.path, sys.modules,
sys.version, sys.platform, sys.byteorder, sys.maxsize, sys.stdin,
sys.stdout, sys.stderr, sys.__stdin__, sys.__stdout__, and
sys.__stderr__.
_PyAttrHasDefaultValue and _PySys_SetAttr provide a thread-safe
setter used both internally and from the public PySys_SetObject API.
sys_audit implements the audit-hook dispatch added in PEP 578: it walks
the per-interpreter hook list installed by PySys_AddAuditHook and calls
each hook in registration order.
Map
| Lines | Symbol | Role | gopy |
|---|---|---|---|
| 1-180 | file header / sys_audit_tstate | Includes, forward declarations, and the inner audit dispatcher that walks tstate->interp->audit_hooks. | module/sys/module.go:audit |
| 181-420 | sys_exit / sys_exc_info / sys_current_exceptions / sys_current_frames | Core introspection builtins. sys_exit raises SystemExit; sys_exc_info returns the (type, value, tb) triple. | module/sys/module.go:sysExit |
| 421-700 | sys_getframe / sys_getframemodulename / sys_getrecursionlimit / sys_setrecursionlimit / sys_intern | Frame and limit accessors. sys_getframe walks the frame chain to depth N; sys_intern calls PyUnicode_InternInPlace. | module/sys/module.go:sysGetFrame |
| 701-1000 | sys_getprofile / sys_setprofile / sys_getprofilefunc / sys_gettrace / sys_settrace / sys_settrace_all_threads | Profiling and tracing hooks. sys_settrace sets tstate->c_tracefunc and the corresponding c_traceobj. | module/sys/module.go:sysSetTrace |
| 1001-1300 | sys_getwindowsversion / sys_getandroidapilevel / sys_getdlopenflags / sys_setdlopenflags / sys_getrefcount / sys_getsizeof / sys_getfilesystemencoding | Platform and memory accessors. Mostly thin wrappers around fields of PyInterpreterState or PyConfig. | module/sys/module.go:sysSizeOf |
| 1301-1600 | sys_audit / PySys_Audit / PySys_AddAuditHook | Audit hook registration and dispatch (PEP 578). PySys_AddAuditHook is safe to call before Py_Initialize. | module/sys/module.go:sysAudit |
| 1601-1900 | sys_getobjects / sys_unraisablehook / sys_excepthook / sys_displayhook | Default hooks. sys_displayhook writes the repr of a non-None result to stdout and stores it in _. | module/sys/module.go:sysDisplayHook |
| 1901-2200 | sys_methods table / constant attributes | PyMethodDef array for all sys callables, plus the PyMemberDef and PyGetSetDef tables for attributes like sys.flags and sys.float_info. | module/sys/module.go:sysMethods |
| 2201-2800 | make_sys_argv / sys_set_path / _PySys_SetStringWithError / _PySys_SetObjectId | Helpers used by _PySys_InitMain to populate individual attributes. make_sys_argv converts the C wchar_t ** argv into a Python list of strings. | module/sys/module.go:initArgv |
| 2801-3600 | _PySys_InitMain | Main startup sequence: populates sys.argv, sys.path, sys.modules, sys.version, sys.platform, sys.byteorder, sys.executable, and streams. | module/sys/module.go:InitMain |
| 3601-4100 | _PySys_SetAttr / PySys_SetObject / PySys_GetObject / PySys_GetAttr / _PySys_GetOptionalAttr | Public and internal sys attribute accessors used throughout the runtime. | module/sys/module.go:SetAttr |
| 4101-4522 | PyModule_Create-time module def / _PySys_Create | Allocate the sys module object, run _PySys_InitMain, and return it to the interpreter init sequence. | module/sys/module.go:Create |
Reading
sys_exit (lines 181 to 250)
cpython 3.14 @ ab2d84fe1023/Python/sysmodule.c#L181-250
static PyObject *
sys_exit_impl(PyObject *module, PyObject *status)
{
/* Raise SystemExit so callers may catch it or we'll exit. */
PyErr_SetObject(PyExc_SystemExit, status);
return NULL;
}
sys.exit is one of the simplest functions in the file. It calls
PyErr_SetObject(PyExc_SystemExit, status) and returns NULL. The calling
convention for C functions means returning NULL with an active exception
set propagates the exception up the call stack. There is no exit(3) call
here. The eventual exit happens in handle_system_exit inside
pythonrun.c, which catches SystemExit at the top of the interpreter
loop, extracts the code attribute, and calls Py_Exit.
The status argument defaults to 0 (via Argument Clinic's = None
default rendered as Py_None and then tested for None). When status
is a non-zero integer the exit code passed to Py_Exit is that integer.
When it is a string, handle_system_exit writes it to stderr and exits
with code 1.
In gopy, module/sys/module.go:sysExit raises objects.SystemExit via
errors.SetObject, matching this behavior exactly.
sys_getframe (lines 490 to 570)
cpython 3.14 @ ab2d84fe1023/Python/sysmodule.c#L490-570
static PyObject *
sys_getframe_impl(PyObject *module, int depth)
{
PyFrameObject *f = PyThreadState_GetFrame(_PyThreadState_GET());
while (depth > 0 && f != NULL) {
PyFrameObject *back = PyFrame_GetBack(f);
Py_DECREF(f);
f = back;
--depth;
}
if (f == NULL) {
PyErr_SetString(PyExc_ValueError,
"call stack is not deep enough");
return NULL;
}
return (PyObject *)f;
}
sys._getframe(depth) walks the chain of PyFrameObject.f_back pointers
exactly depth steps, decrementing the reference count of each intermediate
frame as it goes. The returned frame is a new reference. A depth of 0
returns the immediate caller's frame; this is what inspect.currentframe()
and traceback.extract_stack() use.
The function is intentionally not marked as a public API. User code is
expected to call it only through inspect; direct use is a C-extension
pattern and is likely to break under alternative Python implementations.
In gopy, walking the frame chain is done via vm.CurrentFrame which keeps
frames as linked *objects.Frame values on the goroutine's eval stack.
sys_settrace (lines 820 to 920)
cpython 3.14 @ ab2d84fe1023/Python/sysmodule.c#L820-920
static PyObject *
sys_settrace(PyObject *module, PyObject *o)
{
if (o == Py_None)
PyEval_SetTrace(NULL, NULL);
else
PyEval_SetTrace(trace_trampoline, o);
Py_RETURN_NONE;
}
sys.settrace delegates immediately to PyEval_SetTrace in ceval.c.
PyEval_SetTrace sets tstate->c_tracefunc and tstate->c_traceobj on
the current thread state. When c_tracefunc is non-NULL the eval loop
calls it at call, return, exception, and line events. The
trampoline trace_trampoline unwraps the Python callable from
tstate->c_traceobj and invokes it with a three-argument (frame, event, arg) tuple.
sys.setprofile is structurally identical, delegating to
PyEval_SetProfile. The profile hook fires only on call and return
events and does not receive line events; it has lower overhead and is
what cProfile uses.
sys.settrace_all_threads (added in 3.12) calls PyEval_SetTrace on
every thread state in the interpreter, not just the calling thread. It
acquires the interpreter's threads_mutex before iterating.
sys_audit / PySys_Audit (lines 1301 to 1500)
cpython 3.14 @ ab2d84fe1023/Python/sysmodule.c#L1301-1500
int
PySys_Audit(const char *event, const char *argFormat, ...)
{
...
PyInterpreterState *interp = _PyInterpreterState_GET();
_Py_AuditHookEntry *e = interp->audit_hooks.head;
while (e != NULL) {
if (e->hookCFunction(event, eventArgs, e->userData) < 0) {
...
return -1;
}
e = e->next;
}
...
return 0;
}
PEP 578 audit hooks fire whenever a security-sensitive operation is about
to execute: open, import, compile, exec, socket.connect, and
about 60 others. Each hook is a C function pointer with signature
int hook(const char *event, PyObject *args, void *userData).
PySys_AddAuditHook registers hooks on the runtime (pre-init) or
interpreter (post-init) hook list.
The sys.addaudithook Python function adds a Python callable to the same
list by wrapping it in a C trampoline. Hooks are called in FIFO order.
A hook returning -1 aborts the audit and propagates the exception to
the caller. In gopy, module/sys/module.go:sysAudit maintains the same
linked-list structure and call order.
_PySys_InitMain (lines 2801 to 3600)
cpython 3.14 @ ab2d84fe1023/Python/sysmodule.c#L2801-3600
_PySys_InitMain is called from _Py_InitializeMain in pylifecycle.c
once the interpreter state and the sys module object both exist. It
performs three broad tasks.
First, it sets the invariant attributes: sys.version, sys.version_info,
sys.hexversion, sys.platform, sys.byteorder, sys.maxsize,
sys.maxunicode, sys.float_info, sys.int_info, sys.hash_info, and
sys.implementation.
Second, it converts the process argv and environment into Python objects.
make_sys_argv converts the wide-char argv from PyConfig.argv into a
list of str. sys.path is built from PyConfig.module_search_paths,
prepending an empty string for "" (meaning CWD) when the first element
is empty.
Third, it wires up the standard streams. sys.stdin, sys.stdout, and
sys.stderr are created from PyConfig.stdio_encoding and
PyConfig.stdio_errors using io.open on file descriptors 0, 1, and 2.
The originals are also stored in sys.__stdin__, sys.__stdout__, and
sys.__stderr__ so user code can restore them after reassignment.
CPython 3.14 changes worth noting
sys.settrace_all_threads and sys.setprofile_all_threads were added in
3.12 and are unchanged in 3.14. sys._getframe gained a depth parameter
default of 0 in 3.13 (it was positional-only with no default before).
sys.audit is now also checked during import of frozen modules (gh-90867).
sys._monitoring (PEP 669) appears as a submodule of sys in 3.12+ but
its implementation lives in Python/instrumentation.c, not here.