Skip to main content

Objects/moduleobject.c (part 6)

Source:

cpython 3.14 @ ab2d84fe1023/Objects/moduleobject.c

This annotation covers the module population API used by C extension authors. See objects_moduleobject5_detail for PyModule_New, PyModule_GetDict, and PyModuleDef.

Map

LinesSymbolRole
1-60PyModule_AddObjectAdd a reference to the module dict (steals ref)
61-120PyModule_AddObjectRefAdd without stealing the reference
121-180PyModule_AddTypeAdd a type and call PyType_Ready
181-260PyModule_AddIntConstantAdd a named integer constant
261-400PyModule_ExecDefRun multi-phase init exec slots

Reading

PyModule_AddObject

// CPython: Objects/moduleobject.c:560 PyModule_AddObject
int
PyModule_AddObject(PyObject *mod, const char *name, PyObject *value)
{
/* Steals a reference to value on success */
int ret = PyModule_AddObjectRef(mod, name, value);
if (ret == 0) {
Py_DECREF(value); /* steal */
}
return ret;
}

PyModule_AddObject is the classic API but its steal-on-success semantics are error-prone. If AddObject succeeds and the caller does not check the return value, an accidental double-free occurs if the caller also decrefs. The newer PyModule_AddObjectRef is preferred.

PyModule_AddObjectRef

// CPython: Objects/moduleobject.c:540 PyModule_AddObjectRef
int
PyModule_AddObjectRef(PyObject *mod, const char *name, PyObject *value)
{
if (!PyModule_Check(mod)) {
PyErr_BadInternalCall();
return -1;
}
PyObject *dict = ((PyModuleObject *)mod)->md_dict;
return PyDict_SetItemString(dict, name, value);
}

PyModule_AddObjectRef does not steal. It is equivalent to mod.__dict__[name] = value. The return value is 0 on success, -1 on error.

PyModule_AddType

// CPython: Objects/moduleobject.c:600 PyModule_AddType
int
PyModule_AddType(PyObject *module, PyTypeObject *type)
{
if (PyType_Ready(type) < 0)
return -1;
const char *name = _PyType_Name(type);
assert(name != NULL);
return PyModule_AddObjectRef(module, name, (PyObject *)type);
}

PyModule_AddType calls PyType_Ready (which fills in inherited slots, builds the MRO, etc.) before adding. The name is taken from type->tp_name, stripping any dotted prefix.

PyModule_ExecDef

// CPython: Objects/moduleobject.c:660 _PyModule_ExecDef
int
_PyModule_ExecDef(PyObject *module, PyModuleDef *def)
{
/* Run all m_slots entries of type Py_mod_exec */
if (def->m_slots == NULL) return 0;
for (PyModuleDef_Slot *cur_slot = def->m_slots;
cur_slot && cur_slot->slot;
cur_slot++) {
if (cur_slot->slot == Py_mod_exec) {
int (*exec_func)(PyObject *) = cur_slot->value;
if (exec_func(module) != 0) {
if (!PyErr_Occurred())
PyErr_Format(PyExc_SystemError, ...);
return -1;
}
}
}
return 0;
}

Multi-phase init (Py_mod_exec slots) allows a module to be partially initialized before exec slots run. This is necessary for modules that need to be importable during their own exec phase (e.g., _io).

gopy notes

PyModule_AddObjectRef is objects.ModuleAddObject in objects/module.go. PyModule_AddType calls objects.TypeReady then ModuleAddObject. PyModule_ExecDef runs exec slots from the PyModuleDef struct, translated as module.ExecSlot callbacks in objects/module.go.