Skip to main content

Lib/importlib/__init__.py

cpython 3.14 @ ab2d84fe1023/Lib/importlib/__init__.py

importlib is the public face of Python's import system. The heavy machinery lives in two frozen modules that are baked into the interpreter binary: _bootstrap.py (pure-Python, no I/O) and _bootstrap_external.py (file-system finders and loaders). The public importlib package re-exports the commonly used names and provides import_module and reload as convenience wrappers.

_bootstrap._find_and_load is the single function that __import__ calls for every import. It consults sys.modules for a cache hit, then walks sys.meta_path finders in order, and finally calls spec.loader.exec_module to execute the module. A per-module lock (_get_module_lock) prevents two threads from importing the same name simultaneously.

_bootstrap_external provides the file-system side: PathFinder implements the sys.meta_path hook that consults sys.path, SourceFileLoader compiles .py sources, SourcelessFileLoader handles .pyc only, and ExtensionFileLoader loads .so/.pyd extension modules.

Map

LinesSymbolRolegopy
1-60Module docstring, bootstrap import, __all__Imports the frozen _bootstrap module using the C-level _imp interface; re-exports __spec__, __loader__, etc.(stdlib pending)
61-100import_module, _bootstrap._gcd_importPublic import_module(name, package) resolves relative names then delegates to _bootstrap._gcd_import.(stdlib pending)
101-160reloadValidates the argument is an existing module, re-executes via loader.exec_module, updates sys.modules.(stdlib pending)
1-300_ModuleLock, _DummyModuleLock, _get_module_lock, _lock_unlock_modulePer-module reentrant locks preventing double-import races; _DummyModuleLock used when the GIL is absent or for built-ins.(stdlib pending)
301-600_find_and_load, _find_and_load_unlocked, _find_specCore import path: cache lookup in sys.modules, meta_path walk, spec resolution, and _load_unlocked.(stdlib pending)
601-900_load_unlocked, _call_with_frames_cleaned, module_from_spec, exec_moduleCreates the module object, inserts it into sys.modules, calls exec_module, and handles cleanup on failure.(stdlib pending)
1-400FileFinder, _get_specCaches directory contents; maps suffix lists to loader classes; invalidated by sys.path_importer_cache clears.(stdlib pending)
401-800SourceFileLoader, get_code, source_to_code, _validate_bytecode_headerReads .py, compiles with compile(), validates or regenerates .pyc cache; stores bytecode under __pycache__.(stdlib pending)
801-1100PathFinder, find_spec, _path_hooks, _path_importer_cacheIterates sys.path entries; consults sys.path_hooks to get a FileFinder; caches finders in sys.path_importer_cache.(stdlib pending)
1-200spec_from_file_location, spec_from_loader, module_from_spec, find_specUtility helpers for embedding and testing; find_spec is the public API for querying the import system without executing a module.(stdlib pending)

Reading

_find_and_load recursion guard (bootstrap lines 301 to 600)

cpython 3.14 @ ab2d84fe1023/Lib/importlib/_bootstrap.py#L301-600

def _find_and_load(name, import_):
module = sys.modules.get(name, _NEEDS_LOADING)
if module is _NEEDS_LOADING:
return _find_and_load_unlocked(name, import_)
if module is None:
message = (f'import of {name} halted; '
f'use of {name!r} during interpreter shutdown')
raise ModuleNotFoundError(message)
_lock_unlock_module(name)
return module

def _find_and_load_unlocked(name, import_):
path = None
parent = name.rpartition('.')[0]
if parent:
if parent not in sys.modules:
_call_with_frames_cleaned(import_, parent)
if name in sys.modules:
return sys.modules[name]
parent_module = sys.modules[parent]
try:
path = parent_module.__path__
except AttributeError:
msg = f'{parent!r} is not a package'
raise ModuleNotFoundError(msg, name=name) from None
spec = _find_spec(name, path, None)
if spec is None:
raise ModuleNotFoundError(f'No module named {name!r}', name=name)
else:
module = _load_unlocked(spec)
...
return module

_find_and_load first checks sys.modules. The sentinel value _NEEDS_LOADING (distinct from None) means the module has never been seen. None in sys.modules means import is blocked during interpreter shutdown. For a cache hit, _lock_unlock_module acquires and immediately releases the module lock to block callers that arrive while another thread's import is still in progress.

For a dotted name like a.b.c, the parent package a.b is imported first (recursively), then __path__ from that package is used to locate a.b.c. If the name appears in sys.modules after the parent import returns (because the parent's __init__ imported it as a side effect), the cached value is returned directly.

SourceFileLoader.get_code (bootstrap_external lines 401 to 800)

cpython 3.14 @ ab2d84fe1023/Lib/importlib/_bootstrap_external.py#L401-800

def get_code(self, fullname):
source_path = self.get_filename(fullname)
source_mtime = None
source_bytes = None
source_hash = None
check_source = True
try:
bytecode_path = cache_from_source(source_path)
except NotImplementedError:
bytecode_path = None
else:
try:
data = self.get_data(bytecode_path)
except OSError:
pass
else:
try:
code = _validate_bytecode_header(data, name=fullname,
path=bytecode_path)
...
return code
except (ImportError, EOFError):
pass
source_bytes = self.get_data(source_path)
code = self.source_to_code(source_bytes, source_path)
...
if bytecode_path is not None:
...
self.set_data(bytecode_path, data)
return code

get_code tries to serve the module from a .pyc file under __pycache__/. _validate_bytecode_header checks the magic number, flags byte (hash-based vs. timestamp-based), and either the source file mtime/size or a hash of the source bytes. If any check fails, it falls back to compiling the .py source and then writes a new .pyc. The write is best-effort: an OSError (e.g., read-only filesystem) is silently ignored.

PathFinder.find_spec (bootstrap_external lines 801 to 1100)

cpython 3.14 @ ab2d84fe1023/Lib/importlib/_bootstrap_external.py#L801-1100

@classmethod
def find_spec(cls, fullname, path=None, target=None):
if path is None:
path = sys.path
namespace_path = []
for entry in path:
if not isinstance(entry, str):
continue
finder = cls._path_importer_cache(entry)
spec = finder.find_spec(fullname, target)
if spec is None:
continue
if spec.submodule_search_locations is not None:
namespace_path.extend(spec.submodule_search_locations)
continue
return spec
if namespace_path:
spec = ModuleSpec(fullname, None)
spec.submodule_search_locations = _NamespacePath(
fullname, namespace_path, cls._get_spec)
return spec
return None

PathFinder implements PEP 451's find_spec for each sys.path entry. It retrieves a cached FileFinder from _path_importer_cache, or creates one by running sys.path_hooks in order. A spec with submodule_search_locations but no loader indicates a namespace package directory. After scanning all path entries, if any namespace directories were collected, a namespace package spec is returned with a _NamespacePath that re-runs the search on each attribute access.

gopy mirror

The vm/eval_import.go file implements the core IMPORT_NAME opcode evaluation. A full importlib port would need ModuleSpec, FileFinder, and SourceFileLoader as Go types. Currently gopy relies on the frozen bootstrap stubs; a full port is tracked as part of the stdlib expansion work in stdlib/MANIFEST.txt.