The Python Language Reference, Release 3.9.5 5.4 Loading 5.4. Loading 53 If and when a module spec is found, the import machinery will use it (and the loader it contains) when loading the module. Here is an approximation of what happens during the loading portion of import: Note the following details: Changed in version 3.4: The import system has taken over the boilerplate responsibilities of loaders. These were previously performed by the$_{importlib.abc.Loader.load_module()}$ method. 2 The importlib implementation avoids using the return value directly. Instead, it gets the module object by looking the module name up in sys.modules . The indirect effect of this is that an imported module may replace itself in sys.modules . This is implementation-specific behavior that is not guaranteed to work in other Python implementations. • If there is an existing module object with the given name in$_{sys.modules}$ , import will have already returned it. • The module will exist in$_{sys.modules}$ before the loader executes the module code. This is crucial because the module code may (directly or indirectly) import itself; adding it to$_{sys.modules}$ beforehand prevents unbounded recursion in the worst case and multiple loading in the best. • If loading fails, the failing module – and only the failing module – gets removed from$_{sys.modules}$ . Any module already in the$_{sys.modules}$ cache, and any module that was successfully loaded as a side-effect, must remain in the cache. This contrasts with reloading where even the failing module is left in$_{sys.modules}$ . • After the module is created but before execution, the import machinery sets the import-related module at- tributes (“_init_module_attrs” in the pseudo-code example above), as summarized in a later section . • Module execution is the key moment of loading in which the module’s namespace gets populated. Execution is entirely delegated to the loader, which gets to decide what gets populated and how. • The module created during loading and passed to exec_module() may not be the one returned at the end of import$^{2}$. module= None if spec.loader is not None and hasattr(spec .loader,'create_module '): # It is assumed 'exec_module' will also be defined on the loader. module=spec.loader.create_module(spec) if module is None : module=ModuleType(spec.name) # The import-related module attributes get set here: _init_module_attrs(spec, module) if spec.loader is None : # unsupported raise ImportError if spec.origin is None and spec.submodule_search_locations is not None : # namespace package sys.modules[spec.name] =module elif not hasattr(spec .loader,'exec_module '): module=spec.loader.load_module(spec .name) # Set __loader__ and __package__ if missing. else : sys.modules[spec.name] =module try : spec.loader.exec_module(module) except BaseException: try : del sys.modules[spec.name] except KeyError : pass raise return sys.modules[spec.name]