Lib/importlib/machinery.py
cpython 3.14 @ ab2d84fe1023/Lib/importlib/machinery.py
importlib.machinery is a thin re-export module. Every name it exposes is
actually defined in _bootstrap.py or _bootstrap_external.py (the frozen
bootstrap files that the interpreter loads before the normal import system is
available). The file's own contribution is all_suffixes() and the deprecation
shim for DEBUG_BYTECODE_SUFFIXES / OPTIMIZED_BYTECODE_SUFFIXES.
Map
| Lines | Symbol | Role |
|---|---|---|
| 3 | ModuleSpec | Module specification object; holds name, loader, origin, cached, submodule_search_locations, has_location |
| 4 | BuiltinImporter | Meta-path finder/loader for built-in C modules (sys, _io, etc.) |
| 5 | FrozenImporter | Meta-path finder/loader for frozen (bytecode-embedded) modules |
| 6-10 | SOURCE_SUFFIXES, BYTECODE_SUFFIXES, EXTENSION_SUFFIXES | Lists of recognised file suffixes for each loader category |
| 11 | WindowsRegistryFinder | Meta-path finder that looks up modules in the Windows registry (no-op on other platforms) |
| 12 | PathFinder | Meta-path finder that searches sys.path using PathEntryFinder hooks |
| 13 | FileFinder | Path-entry finder backed by the filesystem; caches directory listings and dispatches to the appropriate loader by suffix |
| 14 | SourceFileLoader | Loads .py source files; handles bytecode caching in __pycache__ |
| 15 | SourcelessFileLoader | Loads .pyc files that have no corresponding source |
| 16 | ExtensionFileLoader | Loads .so / .pyd C extension modules via _imp.create_dynamic / _imp.exec_dynamic |
| 17 | AppleFrameworkLoader | macOS-specific subclass of ExtensionFileLoader for framework bundles |
| 18 | NamespaceLoader | Loader for namespace packages (directories with no __init__.py) |
| 21-23 | all_suffixes() | Returns the concatenation of all three suffix lists; useful for membership tests |
| 34-50 | __getattr__ | Module-level __getattr__ that issues a DeprecationWarning when DEBUG_BYTECODE_SUFFIXES or OPTIMIZED_BYTECODE_SUFFIXES is accessed |
Reading
ModuleSpec (defined in _bootstrap.py:565)
ModuleSpec is the single object that carries everything the import system
needs to know about a module before it is loaded. Its most-used attributes are
origin (the file path or "built-in"), cached (the .pyc path, derived
lazily), submodule_search_locations (non-None for packages), and has_location
(controls whether __file__ is set on the module).
# CPython: Lib/importlib/_bootstrap.py:565 ModuleSpec
class ModuleSpec:
def __init__(self, name, loader, *, origin=None, loader_state=None,
is_package=None):
self.name = name
self.loader = loader
self.origin = origin
self.loader_state = loader_state
self.submodule_search_locations = [] if is_package else None
self._set_fileattr = False
self._cached = None
cached is a lazy property: it calls _bootstrap_external._get_cached(self.origin)
only the first time it is read, avoiding disk access during spec construction.
BuiltinImporter and FrozenImporter (defined in _bootstrap.py:974, 1026)
Both are stateless (all methods are @classmethod or @staticmethod). They
implement find_spec, create_module, and exec_module, which is the full
two-phase load protocol introduced in Python 3.4.
# CPython: Lib/importlib/_bootstrap.py:985 BuiltinImporter.find_spec
@classmethod
def find_spec(cls, fullname, path=None, target=None):
if _imp.is_builtin(fullname):
return spec_from_loader(fullname, cls, origin=cls._ORIGIN)
else:
return None
# CPython: Lib/importlib/_bootstrap.py:992 BuiltinImporter.create_module
@staticmethod
def create_module(spec):
"""Create a built-in module"""
if spec.name not in sys.builtin_module_names:
raise ImportError(f'{spec.name!r} is not a built-in module',
name=spec.name)
return _call_with_frames_removed(_imp.create_builtin, spec)
SourceFileLoader and ExtensionFileLoader (defined in _bootstrap_external.py:962, 1032)
SourceFileLoader is the loader most Python files go through. Its key
contribution over the base SourceLoader is path_stats, which reads
st_mtime and st_size from the filesystem to decide whether a cached .pyc
is still valid.
# CPython: Lib/importlib/_bootstrap_external.py:962 SourceFileLoader
class SourceFileLoader(FileLoader, SourceLoader):
"""Concrete implementation of SourceLoader using the file system."""
def path_stats(self, path):
"""Return the metadata for the path."""
st = _path_stat(path)
return {'mtime': st.st_mtime, 'size': st.st_size}
ExtensionFileLoader delegates directly to _imp.create_dynamic and
_imp.exec_dynamic, which call the C-level PyImport_ExecCodeModuleWithPathnames
pathway:
# CPython: Lib/importlib/_bootstrap_external.py:1051 ExtensionFileLoader.create_module
def create_module(self, spec):
"""Create an uninitialized extension module"""
module = _bootstrap._call_with_frames_removed(
_imp.create_dynamic, spec)
return module
# CPython: Lib/importlib/_bootstrap_external.py:1059 ExtensionFileLoader.exec_module
def exec_module(self, module):
"""Initialize an extension module"""
_bootstrap._call_with_frames_removed(_imp.exec_dynamic, module)
FileFinder (defined in _bootstrap_external.py:1322)
FileFinder caches directory listings keyed by st_mtime. On a cache miss it
calls _fill_cache() to re-read the directory. For each candidate it iterates
self._loaders (a flat list of (suffix, loader_class) pairs built from the
loader_details passed to __init__) to find the first matching suffix.
# CPython: Lib/importlib/_bootstrap_external.py:1331 FileFinder.__init__
def __init__(self, path, *loader_details):
loaders = []
for loader, suffixes in loader_details:
loaders.extend((suffix, loader) for suffix in suffixes)
self._loaders = loaders
self.path = _path_abspath(path) if path and path != '.' else _os.getcwd()
self._path_mtime = -1
self._path_cache = set()
self._relaxed_path_cache = set()
Deprecation shim for DEBUG/OPTIMIZED_BYTECODE_SUFFIXES
The module's __getattr__ hook intercepts attribute access so that accessing
the now-deprecated constants triggers a DeprecationWarning pointing users to
BYTECODE_SUFFIXES instead.
# CPython: Lib/importlib/machinery.py:34 __getattr__
def __getattr__(name):
import warnings
if name == 'DEBUG_BYTECODE_SUFFIXES':
warnings.warn('importlib.machinery.DEBUG_BYTECODE_SUFFIXES is '
'deprecated; use importlib.machinery.BYTECODE_SUFFIXES '
'instead.',
DeprecationWarning, stacklevel=2)
return _DEBUG_BYTECODE_SUFFIXES
...
raise AttributeError(f'module {__name__!r} has no attribute {name!r}')
gopy notes
ModuleSpecis the central data structure the entire import system passes around. It must be a first-class gopy object before any loader can be wired. The definition is in_bootstrap.py:565; port that class in full, including the lazycachedproperty and thehas_locationsetter.BuiltinImporterandFrozenImportercan be ported as stateless structs with class-method equivalents. Theircreate_modulecalls_imp.create_builtin/_imp.create_dynamic, which map to gopy's existing C-extension init paths.SOURCE_SUFFIXES,BYTECODE_SUFFIXES, andEXTENSION_SUFFIXESare simple string slices. gopy should expose them as package-level variables in theimportlib/machinerymodule wrapper.FileFinderis the most complex piece here. Its directory-listing cache and case-insensitive mode (_relax_case()on Windows/macOS) require filesystem abstractions. Port the full class rather than just thefind_specentry point.AppleFrameworkLoaderandWindowsRegistryFinderare platform-specific. They can be no-op stubs on Linux and treated as dead code on other platforms for the initial port.