Skip to main content

Modules/errnomodule.c

cpython 3.14 @ ab2d84fe1023/Modules/errnomodule.c

The errno module is one of the simplest C extension modules in CPython. Its entire job is to walk every platform errno.h constant and expose it as a Python int attribute on the module, then construct a reverse-mapping dict errno.errorcode that maps integer codes back to their symbolic names (e.g. {1: "EPERM", 2: "ENOENT", ...}).

There is no Python-layer fallback. The module is entirely self-contained in this file and is built as a built-in on all platforms.

Map

LinesSymbolRolegopy
1-15includesHeaders: Python.h, platform errno.h.
15-55add_errno_const macro, constant blockRegisters each errno.h symbol via PyModule_AddIntConstant.module/errno/module.go:addConst
55-75errno_exec, errorcode dict constructionBuilds the errorcode dict from the already-registered constants.module/errno/module.go:buildErrorcode
75-80_errnomodule, PyInit_errnoModule definition and entry point.module/errno/module.go:Module

Reading

add_errno_const macro pattern (lines 15 to 55)

cpython 3.14 @ ab2d84fe1023/Modules/errnomodule.c#L15-55

Every platform constant is added through a single macro so no per-constant boilerplate accumulates:

#define add_errno_const(name) \
PyModule_AddIntConstant(m, #name, name)

The macro stringifies the symbol name with #name, so the attribute name in Python matches the C identifier exactly. The body of errno_exec is then a flat list of conditional blocks:

#ifdef EPERM
add_errno_const(EPERM);
#endif
#ifdef ENOENT
add_errno_const(ENOENT);
#endif
/* ... ~100 more #ifdef blocks ... */

Each block is guarded with #ifdef so the module compiles cleanly on any platform regardless of which subset of constants the system header defines. PyModule_AddIntConstant returns -1 on failure; the generated code checks the return value and aborts module init if any constant fails to register.

errorcode dict construction (lines 55 to 75)

cpython 3.14 @ ab2d84fe1023/Modules/errnomodule.c#L55-75

After all constants are registered, errno_exec reads them back from the module's __dict__ to build the reverse mapping:

PyObject *d = PyModule_GetDict(m);
PyObject *de = PyDict_New();
/* ... */
PyObject *keys = PyMapping_Keys(d);
for (int i = 0; i < PyList_GET_SIZE(keys); i++) {
PyObject *key = PyList_GET_ITEM(keys, i);
PyObject *val = PyObject_GetItem(d, key);
/* Skip non-integer entries (e.g. __name__, __doc__). */
if (!PyLong_Check(val)) { Py_DECREF(val); continue; }
/* Only add the first name for any given code number. */
if (!PyDict_Contains(de, val))
PyDict_SetItem(de, val, key);
Py_DECREF(val);
}
PyModule_AddObject(m, "errorcode", de);

The "first name wins" policy means that on Linux, where EDEADLOCK is an alias for EDEADLK, only one string appears in errorcode for the shared integer value. Which name wins depends on iteration order of the module dict, which is insertion order in CPython 3.7+.

gopy mirror

module/errno/module.go. Each platform constant is added with the Go equivalent of PyModule_AddIntConstant. The errorcode dict is built by iterating the registered names after the constant block, mirroring the CPython iteration strategy.

CPython 3.14 changes

The module moved from single-phase to multi-phase init (Py_mod_exec) in 3.12, splitting the old PyInit_errno monolith into an errno_exec function. The constant list and errorcode logic are otherwise unchanged since Python 2.