How _frozen_importlib Bootstraps Python Imports
Python relies on its own importlib package to find,
load, and manage modules, which creates a classic chicken-and-egg
dilemma: the interpreter needs an import system to load the Python files
that define the import system. To resolve this circular dependency,
Python embeds a pre-compiled, bytecode version of its core import
machinery directly into the CPython binary under the module name
_frozen_importlib. This article explains how
_frozen_importlib bridges the gap during interpreter
startup, enabling a self-hosting import system to initialize without
relying on external filesystem imports.
The Bootstrapping Dilemma
Historically, Python's import machinery was implemented entirely in
C. With Python 3.3, the core import logic was rewritten in pure Python
via importlib (specifically
importlib._bootstrap and
importlib._bootstrap_external). While writing the logic in
Python makes the import machinery easier to maintain, customize, and
test, it creates a fundamental bootstrapping problem. At interpreter
startup, basic capabilities such as parsing file paths, reading files
from disk, and compiling Python source code to bytecode depend on the
very import machinery that has not yet been loaded.
What Is a Frozen Module?
A frozen module in CPython is a Python module whose compiled bytecode is embedded directly inside the CPython executable or shared library as a static C array of bytes. Because the bytecode is compiled at build time, the interpreter can load and execute it without:
- Reading anything from the filesystem.
- Initializing file I/O subsystems.
- Invoking the Python parser or bytecode compiler.
During the CPython build process, a helper tool compiles
Lib/importlib/_bootstrap.py into bytecode and transforms
those bytes into a C header file. When CPython is compiled, this
bytecode becomes baked directly into the C runtime.
The Startup and Bootstrapping Sequence
CPython uses _frozen_importlib to bootstrap the runtime
through a carefully ordered sequence:
- Primitive C Initialization: The interpreter starts
by initializing fundamental C-level structures, memory allocators, and
built-in modules like
sysandbuiltins. - Loading
_frozen_importlib: The runtime invokes a minimal C-level loader capable exclusively of executing frozen bytecode arrays stored in memory. It executes the frozen bytecode ofimportlib._bootstrap, exposing it as the module_frozen_importlib. - Setting Up Core Importers: Once executed,
_frozen_importlibestablishes foundational classes (such asModuleSpec) and registers primitive finders ontosys.meta_path. These initial finders only know how to load built-in modules (BuiltinImporter) and other frozen modules (FrozenImporter). - Loading External Import Machinery: With primitive
importing operational, the runtime loads
_frozen_importlib_external(the frozen version ofimportlib._bootstrap_external). This module provides file-system-aware classes, includingFileFinder,SourceFileLoader, andPathFinder. - Enabling Filesystem Imports: The external importers
are appended to
sys.meta_path, allowing Python to locate.pyand.pycfiles on disk, inspectsys.path, and resolve standard packages.
Finalizing the Import System
After _frozen_importlib and
_frozen_importlib_external are initialized, the
bootstrapping phase ends. The runtime can now import standard library
modules directly from disk.
When user code later runs import importlib, the standard
library module simply re-exports the components already defined in
memory by _frozen_importlib. This architecture allows
Python to maintain an import subsystem written almost entirely in
high-level Python code while avoiding runtime circular dependencies
during startup.