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
| Lines | Symbol | Role | gopy |
|---|---|---|---|
| 1-60 | Module docstring, bootstrap import, __all__ | Imports the frozen _bootstrap module using the C-level _imp interface; re-exports __spec__, __loader__, etc. | (stdlib pending) |
| 61-100 | import_module, _bootstrap._gcd_import | Public import_module(name, package) resolves relative names then delegates to _bootstrap._gcd_import. | (stdlib pending) |
| 101-160 | reload | Validates 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_module | Per-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_spec | Core 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_module | Creates the module object, inserts it into sys.modules, calls exec_module, and handles cleanup on failure. | (stdlib pending) |
| 1-400 | FileFinder, _get_spec | Caches directory contents; maps suffix lists to loader classes; invalidated by sys.path_importer_cache clears. | (stdlib pending) |
| 401-800 | SourceFileLoader, get_code, source_to_code, _validate_bytecode_header | Reads .py, compiles with compile(), validates or regenerates .pyc cache; stores bytecode under __pycache__. | (stdlib pending) |
| 801-1100 | PathFinder, find_spec, _path_hooks, _path_importer_cache | Iterates sys.path entries; consults sys.path_hooks to get a FileFinder; caches finders in sys.path_importer_cache. | (stdlib pending) |
| 1-200 | spec_from_file_location, spec_from_loader, module_from_spec, find_spec | Utility 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.