pycore_import.h
Internal header that exposes the private import API used by the interpreter core.
Public callers use Python/import.c; this header wires together the per-interpreter
state, the extension module cache, and the dynamic loader.
Map
| Lines | Symbol | Role |
|---|---|---|
| 1–30 | _PyImport_State | Per-interpreter import state struct |
| 31–50 | _PyImport_FindExtensionObject | Look up a cached extension module by name + path |
| 51–70 | _PyImport_FixupExtensionObject | Write an extension module into the cache after load |
| 71–90 | _PyImport_GetModuleAttr | Attribute fetch on a module object without Python overhead |
| 91–110 | _PyImport_IsDefaultImportFunc | Test whether __import__ has been replaced |
| 111–130 | _PyImport_LoadDynamic | Platform-specific .so / .pyd loader entry point |
| 131–150 | misc guards | extern "C" guards and include sentinels |
Reading
Per-interpreter import state
Every sub-interpreter carries its own _PyImport_State so that imports in one
interpreter do not bleed into another. The two most-referenced fields are
modules_by_index (a PyListObject that maps PyModuleDef.m_base.m_index
to the live module object) and builtins_copy (a snapshot of builtins.__dict__
taken at interpreter startup).
// CPython: Include/internal/pycore_import.h:12 _PyImport_State
struct _PyImport_State {
PyObject *modules_by_index; /* list; indexed by m_base.m_index */
PyObject *builtins_copy; /* shallow copy of builtins.__dict__ */
PyObject *import_func; /* cached builtins.__import__ */
...
};
Extension module cache
_PyImport_FindExtensionObject walks a per-interpreter dict keyed on
(name, path) tuples. A hit returns the cached sys.modules entry without
re-running dlopen. _PyImport_FixupExtensionObject is called by the dynamic
loader immediately after a successful PyInit_xxx to populate that same dict.
// CPython: Include/internal/pycore_import.h:51 _PyImport_FindExtensionObject
PyObject *_PyImport_FindExtensionObject(PyObject *name, PyObject *path);
// CPython: Include/internal/pycore_import.h:58 _PyImport_FixupExtensionObject
int _PyImport_FixupExtensionObject(PyObject *mod,
PyObject *name,
PyObject *path,
PyObject *modules);
Default import check
Before running the full import machinery CPython asks whether __import__ is
still the built-in. If it has been monkey-patched the fast path is skipped and
the call goes through the normal attribute lookup.
// CPython: Include/internal/pycore_import.h:96 _PyImport_IsDefaultImportFunc
int _PyImport_IsDefaultImportFunc(PyThreadState *tstate, PyObject *func);
Dynamic loader
_PyImport_LoadDynamic is the single cross-platform entry point for .so /
.pyd extension loading. On POSIX it ultimately calls dlopen; on Windows it
calls LoadLibraryExW. The function signature is identical on all platforms so
the rest of the import machinery stays portable.
// CPython: Include/internal/pycore_import.h:115 _PyImport_LoadDynamic
PyObject *_PyImport_LoadDynamic(PyObject *name, PyObject *path,
PyObject *file);
gopy notes
_PyImport_Statemaps to a struct embedded invm.Interpreter. Themodules_by_indexlist is replicated inobjects/module.goas a Go slice._PyImport_FindExtensionObjectand_PyImport_FixupExtensionObjectare ported invm/eval_import.go. The cache key uses a[2]stringarray instead of a Python tuple._PyImport_LoadDynamicis out of scope for gopy; extension modules are Go packages registered viastdlibinit/registry.go._PyImport_IsDefaultImportFuncis inlined at the call site invm/eval_import.gobecause gopy has no monkey-patching surface yet.
CPython 3.14 changes
_PyImport_Stategainedimport_funcas a cached field (previously fetched frombuiltinson every import). This shaves one dict lookup per import statement.- The
modules_by_indexlist is now pre-allocated to the knownm_indexhigh watermark at interpreter startup, reducing realloc churn on startup-heavy workloads. _PyImport_LoadDynamicsignature is unchanged from 3.12, but the internal audit-hook call was moved earlier to fire beforedlopenrather than after.