Python/frozen.c
cpython 3.14 @ ab2d84fe1023/Python/frozen.c
The frozen module table. During interpreter startup, before
sys.path exists and before importlib has been imported, the interpreter
needs to import _frozen_importlib (the pure-Python importlib._bootstrap
module) and _frozen_importlib_external (importlib._bootstrap_external).
Those modules cannot be loaded from disk because the path-based importer
does not exist yet. The solution is to compile them to bytecode at CPython
build time, embed the marshal'd code objects as C byte arrays, and register
them in this file's tables.
Python/frozen.c contains three tables: _PyImport_FrozenBootstrap for
the two modules needed in the very first import step,
_PyImport_FrozenStdlib for a larger set of stdlib modules that are frozen
for startup performance (PEP 451 bootstrap, zipimport, _collections_abc,
_abc, etc.), and _PyImport_FrozenAliases which maps alternate module
names (e.g. __hello__) to entries in the other two tables.
Each table is a null-terminated array of struct _frozen entries with
three fields: name (C string), code (pointer to a byte array), and
size (byte count of the marshal'd code object, negative for a package).
PyImport_ImportFrozenModule in Python/import.c drives the lookup:
it walks both bootstrap and stdlib tables, then aliases, to find a match
by name; unmarshals the code object via PyMarshal_ReadObjectFromString;
and executes it in a freshly created module namespace.
Map
| Lines | Symbol | Role | gopy |
|---|---|---|---|
| 1-30 | file header / includes | Includes Python.h and the generated frozen_data headers produced by Tools/freeze/. | stdlibinit/registry.go |
| 31-80 | _PyImport_FrozenBootstrap | Two-entry table: _frozen_importlib and _frozen_importlib_external. These are the only modules available during phase-one init. | stdlibinit/registry.go:FrozenBootstrap |
| 81-140 | _PyImport_FrozenStdlib | Larger table of stdlib modules frozen for performance (e.g. zipimport, abc, _collections_abc, _sitebuiltins, genericpath). | stdlibinit/registry.go:FrozenStdlib |
| 141-175 | _PyImport_FrozenAliases | Alias table: maps alternate names (__hello__, __phello__, __phello__._framework) to entries in the other tables. | stdlibinit/registry.go:FrozenAliases |
| 176-200 | terminator / PyImport_FrozenModules | The public PyImport_FrozenModules pointer used by the C extension API; defaults to _PyImport_FrozenBootstrap. Embedding applications may redirect it to a custom table. | stdlibinit/registry.go:FrozenModules |
Reading
Frozen table layout (lines 31 to 140)
cpython 3.14 @ ab2d84fe1023/Python/frozen.c#L31-140
/* Bootstrap frozen modules */
static const struct _frozen _PyImport_FrozenBootstrap[] = {
{"_frozen_importlib", _Py_M__importlib__bootstrap,
(int)sizeof(_Py_M__importlib__bootstrap)},
{"_frozen_importlib_external", _Py_M__importlib__bootstrap_external,
(int)sizeof(_Py_M__importlib__bootstrap_external)},
{0, 0, 0} /* sentinel */
};
The struct _frozen layout is:
struct _frozen {
const char *name;
const unsigned char *code;
int size;
int is_package; /* added 3.11 */
PyObject *(*get_code)(void); /* added 3.13, lazy-load hook */
};
size is positive for modules and negative for packages (the absolute
value is the byte count). In 3.11 the is_package flag was added
explicitly so the sign convention is no longer the only indicator. In 3.13
a get_code hook was added to allow lazy unmarshalling; when non-NULL it is
called instead of treating code as a pre-marshalled buffer.
The byte arrays (e.g. _Py_M__importlib__bootstrap) are generated by
Tools/freeze/freeze.py and written to Python/frozen_modules/ at build
time. They are never shipped as .pyc files; they are always linked
directly into the interpreter binary.
PyImport_ImportFrozenModule (lines in Python/import.c)
cpython 3.14 @ ab2d84fe1023/Python/import.c#L3501-3700
int
PyImport_ImportFrozenModuleObject(PyObject *name)
{
/* 1. Search bootstrap table, then stdlib, then aliases */
const struct _frozen *p = _PyImport_FindFrozen(name);
if (p == NULL) return 0; /* not found */
/* 2. Unmarshal the code object */
PyObject *co = PyMarshal_ReadObjectFromString(
(const char *)p->code, p->size);
if (co == NULL) return -1;
/* 3. Execute in a new module namespace */
PyObject *m = PyImport_AddModuleObject(name);
...
PyObject *d = PyModule_GetDict(m);
...
PyObject *v = PyEval_EvalCode(co, d, d);
Py_DECREF(co);
if (v == NULL) return -1;
Py_DECREF(v);
return 1; /* found and executed */
}
_PyImport_FindFrozen walks _PyImport_FrozenBootstrap, then
_PyImport_FrozenStdlib, then resolves aliases from
_PyImport_FrozenAliases before returning NULL. The unmarshalling is a
direct PyMarshal_ReadObjectFromString call, which is safe at this stage
because the marshal module is implemented entirely in C.
The returned integer follows the "found" protocol: 0 means not a frozen
module (try the next finder), 1 means found and executed successfully,
-1 means found but execution raised an exception.
Bootstrap sequence
The order in which frozen modules are needed during startup is:
_PyImport_FrozenBootstrap[0](_frozen_importlib) is executed in_PyImport_InitCorebefore any other import. It installs the bootstrap finder and loader intosys.meta_path._PyImport_FrozenBootstrap[1](_frozen_importlib_external) is executed next, adding the path-based finder tosys.meta_path. After this point,.pyand.pycfiles onsys.pathcan be imported.- The stdlib frozen modules are available via the normal frozen finder from this point on; they are imported lazily on first use, not eagerly at startup.
In gopy, stdlibinit/registry.go stores the equivalent tables as Go
slices of a FrozenEntry struct and drives the same three-step sequence
inside InitCore.
CPython 3.14 changes worth noting
The get_code hook field was added to struct _frozen in 3.13 to support
deferred unmarshalling for the larger stdlib frozen modules (gh-100950).
_PyImport_FrozenStdlib grew significantly in 3.11 (PEP 451 performance
work) and again in 3.12. The is_package flag made the negative-size
convention for packages redundant; both are still supported for
backward-compatibility with embedding code that hand-builds frozen tables.