Skip to main content

_bootstrap.py

_bootstrap.py is the frozen core of Python's import system. It is baked into the interpreter at build time (make regen-importlib) and executes before the file system is accessible. No injected globals may be referenced at class or module level — only inside function bodies, after _setup() has run.

Map

LinesSymbolRole
23–27_object_nameReturns __qualname__ of any object for error messages
62–135_WeakValueDictionaryStripped-down weak-value dict used for _blocking_on
226–390_ModuleLockPer-module RLock with deadlock detection
392–410_DummyModuleLockSingle-threaded fallback
429–463_get_module_lockGets or creates the lock for a module name
466–480_lock_unlock_moduleAcquires then releases — waits for initialising modules
483–491_call_with_frames_removedSentinel for remove_importlib_frames in import.c
565–663ModuleSpecData class carrying name, loader, origin, search paths
736–806_init_module_attrsSets __name__, __loader__, __package__, __spec__, __file__
914–955_load_unlockedCreates, registers, and executes a module from a spec
974–1023BuiltinImporterMeta-path finder/loader for sys.builtin_module_names
1026–1216FrozenImporterMeta-path finder/loader for frozen bytecode modules
1308–1354_find_and_load_unlockedResolves parent, calls _find_spec, calls _load_unlocked
1360–1391_find_and_loadHot path — fast-returns cached modules, acquires lock otherwise
1394–1406_gcd_importGreatest-common-denominator import; entry point for __import__
1517–1554_setupInjects sys, _imp, _thread, _warnings, _weakref

Reading

The main import dispatch

_find_and_load is the function Python/import.c calls for every import statement. The fast path avoids locking entirely when the module is already in sys.modules and fully initialised.

# CPython: Lib/importlib/_bootstrap.py:1360 _find_and_load
def _find_and_load(name, import_):
module = sys.modules.get(name, _NEEDS_LOADING)
if (module is _NEEDS_LOADING or
getattr(getattr(module, "__spec__", None), "_initializing", False)):
with _ModuleLockManager(name):
module = sys.modules.get(name, _NEEDS_LOADING)
if module is _NEEDS_LOADING:
return _find_and_load_unlocked(name, import_)
_lock_unlock_module(name)
...
return module

Setting module attributes

_init_module_attrs populates the dunder attributes that user code reads. It is called from both _load_unlocked and _exec (the reload path).

# CPython: Lib/importlib/_bootstrap.py:736 _init_module_attrs
def _init_module_attrs(spec, module, *, override=False):
if (override or getattr(module, '__name__', None) is None):
try:
module.__name__ = spec.name
except AttributeError:
pass
# __loader__, __package__, __spec__, __path__, __file__, __cached__
# each follow the same override-or-skip pattern
...
return module

Frame stripping

_call_with_frames_removed is a deliberate no-op. Its real work happens on the C side: remove_importlib_frames in Python/import.c walks the traceback and strips every consecutive importlib frame that ends with a call to this function, keeping tracebacks clean for users.

# CPython: Lib/importlib/_bootstrap.py:483 _call_with_frames_removed
def _call_with_frames_removed(f, *args, **kwds):
return f(*args, **kwds)

Per-module locking and deadlock detection

_ModuleLock.acquire checks for deadlocks by walking the _blocking_on graph before blocking. If thread A holds lock X and is waiting on lock Y while thread B holds Y and is waiting on X, _has_deadlocked returns True and a _DeadlockError is raised. CPython accepts partial initialisation rather than deadlocking on circular imports across threads.

# CPython: Lib/importlib/_bootstrap.py:288 _ModuleLock.has_deadlock
def has_deadlock(self):
return _has_deadlocked(
target_id=_thread.get_ident(),
seen_ids=set(),
candidate_ids=[self.owner],
blocking_on=_blocking_on,
)

gopy notes

  • ModuleSpec maps to objects/modulespec.go (planned). Fields name, loader, origin, submodule_search_locations, and _set_fileattr must all be exported.
  • _call_with_frames_removed has no Go equivalent yet. The VM simply calls the function directly; frame stripping is a future cleanup pass.
  • _ModuleLock deadlock detection depends on _thread.get_ident() and a live _blocking_on dict. gopy uses goroutine-local state; the lock model will need a mapping from goroutine ID to pending locks.
  • BuiltinImporter and FrozenImporter are the first two entries on sys.meta_path. gopy populates these in vm/eval_import.go.

CPython 3.14 changes

  • _blocking_on was converted to a _WeakValueDictionary (GH-106176) so that thread-local lock lists are collected as soon as the thread exits, preventing a memory leak present in earlier releases.
  • _find_spec now copies sys.meta_path before iterating (GH-130094) to guard against finders that mutate the list during traversal.
  • _ModuleLock now uses an RLock internally instead of a plain Lock, fixing re-entrant import deadlocks caused by signal handlers and the GC running __del__ during import.