Skip to main content

Python/import.c (part 11)

Source:

cpython 3.14 @ ab2d84fe1023/Python/import.c

This annotation covers C extension module loading. See modules_import10_detail for importlib._bootstrap, _find_and_load, and the importer protocol.

Map

LinesSymbolRole
1-80_PyImport_FindExtensionObjectLook up a cached extension module
81-180_PyImport_LoadDynamicModuleWithSpecLoad a .so/.pyd file
181-280_PyImport_InitModuleWithDefMulti-phase init vs single-phase
281-380Extension module cacheextensions dict in sys.modules
381-500dlopen / LoadLibraryPlatform-specific shared library loading

Reading

_PyImport_FindExtensionObject

// CPython: Python/import.c:1480 _PyImport_FindExtensionObject
PyObject *
_PyImport_FindExtensionObject(PyObject *name, PyObject *path)
{
/* Extensions are cached in _PyRuntime.imports.extensions by (name, path) */
PyObject *key = PyTuple_Pack(2, name, path);
PyObject *mod = PyDict_GetItemWithError(extensions, key);
if (mod != NULL && mod != Py_None) {
/* Found: re-init if needed */
PyModuleDef *def = PyModule_GetDef(mod);
if (def != NULL && def->m_init != NULL) {
return def->m_init(); /* re-run init to get a fresh module */
}
return Py_NewRef(mod);
}
return NULL;
}

Loaded extension modules are cached by (name, path). On a second import, the cache is checked first. Single-phase init modules return the cached object; multi-phase init modules re-run m_init.

_PyImport_LoadDynamicModuleWithSpec

// CPython: Python/import.c:1560 _PyImport_LoadDynamicModuleWithSpec
PyObject *
_PyImport_LoadDynamicModuleWithSpec(PyObject *spec, FILE *fp)
{
const char *path = PyBytes_AS_STRING(PyUnicode_AsEncodedString(spec_path, ...));
void *handle = _PyImport_FindSharedFuncptr("PyInit_", modname, path, fp);
if (handle == NULL) { PyErr_Format(PyExc_ImportError, ...); return NULL; }
PyObject *(*p0)(void) = (PyObject *(*)(void))handle;
PyObject *mod = (*p0)();
if (mod == NULL) return NULL;
_PyImport_InitModuleWithDef(mod, spec);
return mod;
}

FindSharedFuncptr calls dlopen(path) then dlsym(handle, "PyInit_modname"). The init function is called; it returns either a PyModuleDef * (multi-phase) or a PyObject * module (single-phase).

Multi-phase vs single-phase init

// CPython: Python/import.c:1620 (comment)
/* Single-phase init (legacy):
PyObject *PyInit_mymod(void) { return PyModule_Create(&def); }
Multi-phase init (3.5+):
PyObject *PyInit_mymod(void) { return PyModuleDef_Init(&def); }
Multi-phase allows the module to be re-initialized for subinterpreters. */

Multi-phase init decouples module creation from module execution. The Py_mod_create and Py_mod_exec slots are called separately, allowing the module object to exist before exec slots run.

gopy notes

_PyImport_LoadDynamicModuleWithSpec is not applicable to gopy (no C extensions). _PyImport_FindExtensionObject maps to vm.FindCachedModule. Extension module loading is replaced by the Go module registry in stdlibinit/registry.go.