bpo-42403: Simplify importlib external bootstrap (GH-23397)

Simplify the importlib external bootstrap code:
importlib._bootstrap_external now uses regular imports to import
builtin modules. When it is imported, the builtin __import__()
function is already fully working and so can be used to import
builtin modules like sys.
This commit is contained in:
Victor Stinner 2020-11-19 13:43:43 +01:00 committed by GitHub
parent 7d9d25dbed
commit 3390347aa0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 4516 additions and 4559 deletions

View File

@ -34,7 +34,7 @@ try:
import _frozen_importlib_external as _bootstrap_external
except ImportError:
from . import _bootstrap_external
_bootstrap_external._setup(_bootstrap)
_bootstrap_external._set_bootstrap_module(_bootstrap)
_bootstrap._bootstrap_external = _bootstrap_external
else:
_bootstrap_external.__name__ = 'importlib._bootstrap_external'

View File

@ -22,8 +22,15 @@ work. One should use importlib as the public-facing version of this module.
# Bootstrap-related code ######################################################
# Modules injected manually by _setup()
_thread = None
_warnings = None
_weakref = None
# Import done by _install_external_importers()
_bootstrap_external = None
def _wrap(new, old):
"""Simple substitute for functools.update_wrapper."""
for replace in ['__module__', '__name__', '__qualname__', '__doc__']:

View File

@ -19,6 +19,36 @@ work. One should use importlib as the public-facing version of this module.
# reference any injected objects! This includes not only global code but also
# anything specified at the class level.
# Module injected manually by _set_bootstrap_module()
_bootstrap = None
# Import builtin modules
import _imp
import _io
import sys
import _warnings
import marshal
_MS_WINDOWS = (sys.platform == 'win32')
if _MS_WINDOWS:
import nt as _os
import winreg
else:
import posix as _os
if _MS_WINDOWS:
path_separators = ['\\', '/']
else:
path_separators = ['/']
# Assumption made in _path_join()
assert all(len(sep) == 1 for sep in path_separators)
path_sep = path_separators[0]
path_separators = ''.join(path_separators)
_pathseps_with_colon = {f':{s}' for s in path_separators}
# Bootstrap-related code ######################################################
_CASE_INSENSITIVE_PLATFORMS_STR_KEY = 'win',
_CASE_INSENSITIVE_PLATFORMS_BYTES_KEY = 'cygwin', 'darwin'
@ -42,6 +72,8 @@ def _make_relax_case():
return False
return _relax_case
_relax_case = _make_relax_case()
def _pack_uint32(x):
"""Convert a 32-bit integer to little-endian."""
@ -294,7 +326,11 @@ _RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c
_PYCACHE = '__pycache__'
_OPT = 'opt-'
SOURCE_SUFFIXES = ['.py'] # _setup() adds .pyw as needed.
SOURCE_SUFFIXES = ['.py']
if _MS_WINDOWS:
SOURCE_SUFFIXES.append('.pyw')
EXTENSION_SUFFIXES = _imp.extension_suffixes()
BYTECODE_SUFFIXES = ['.pyc']
# Deprecated.
@ -469,15 +505,18 @@ def _check_name(method):
raise ImportError('loader for %s cannot handle %s' %
(self.name, name), name=name)
return method(self, name, *args, **kwargs)
try:
# FIXME: @_check_name is used to define class methods before the
# _bootstrap module is set by _set_bootstrap_module().
if _bootstrap is not None:
_wrap = _bootstrap._wrap
except NameError:
# XXX yuck
else:
def _wrap(new, old):
for replace in ['__module__', '__name__', '__qualname__', '__doc__']:
if hasattr(old, replace):
setattr(new, replace, getattr(old, replace))
new.__dict__.update(old.__dict__)
_wrap(_check_name_wrapper, method)
return _check_name_wrapper
@ -713,7 +752,7 @@ class WindowsRegistryFinder:
REGISTRY_KEY_DEBUG = (
'Software\\Python\\PythonCore\\{sys_version}'
'\\Modules\\{fullname}\\Debug')
DEBUG_BUILD = False # Changed in _setup()
DEBUG_BUILD = (_MS_WINDOWS and '_d.pyd' in EXTENSION_SUFFIXES)
@classmethod
def _open_registry(cls, key):
@ -1060,10 +1099,6 @@ class SourcelessFileLoader(FileLoader, _LoaderBasics):
return None
# Filled in by _setup().
EXTENSION_SUFFIXES = []
class ExtensionFileLoader(FileLoader, _LoaderBasics):
"""Loader for extension modules.
@ -1552,66 +1587,14 @@ def _get_supported_file_loaders():
return [extensions, source, bytecode]
def _setup(_bootstrap_module):
"""Setup the path-based importers for importlib by importing needed
built-in modules and injecting them into the global namespace.
Other components are extracted from the core bootstrap module.
"""
global sys, _imp, _bootstrap
def _set_bootstrap_module(_bootstrap_module):
global _bootstrap
_bootstrap = _bootstrap_module
sys = _bootstrap.sys
_imp = _bootstrap._imp
self_module = sys.modules[__name__]
# Directly load the os module (needed during bootstrap).
os_details = ('posix', ['/']), ('nt', ['\\', '/'])
for builtin_os, path_separators in os_details:
# Assumption made in _path_join()
assert all(len(sep) == 1 for sep in path_separators)
path_sep = path_separators[0]
if builtin_os in sys.modules:
os_module = sys.modules[builtin_os]
break
else:
try:
os_module = _bootstrap._builtin_from_name(builtin_os)
break
except ImportError:
continue
else:
raise ImportError('importlib requires posix or nt')
setattr(self_module, '_os', os_module)
setattr(self_module, 'path_sep', path_sep)
setattr(self_module, 'path_separators', ''.join(path_separators))
setattr(self_module, '_pathseps_with_colon', {f':{s}' for s in path_separators})
# Directly load built-in modules needed during bootstrap.
builtin_names = ['_io', '_warnings', 'marshal']
if builtin_os == 'nt':
builtin_names.append('winreg')
for builtin_name in builtin_names:
if builtin_name not in sys.modules:
builtin_module = _bootstrap._builtin_from_name(builtin_name)
else:
builtin_module = sys.modules[builtin_name]
setattr(self_module, builtin_name, builtin_module)
# Constants
setattr(self_module, '_relax_case', _make_relax_case())
EXTENSION_SUFFIXES.extend(_imp.extension_suffixes())
if builtin_os == 'nt':
SOURCE_SUFFIXES.append('.pyw')
if '_d.pyd' in EXTENSION_SUFFIXES:
WindowsRegistryFinder.DEBUG_BUILD = True
def _install(_bootstrap_module):
"""Install the path-based import components."""
_setup(_bootstrap_module)
_set_bootstrap_module(_bootstrap_module)
supported_loaders = _get_supported_file_loaders()
sys.path_hooks.extend([FileFinder.path_hook(*supported_loaders)])
sys.meta_path.append(PathFinder)

View File

@ -0,0 +1,5 @@
Simplify the :mod:`importlib` external bootstrap code:
``importlib._bootstrap_external`` now uses regular imports to import builtin
modules. When it is imported, the builtin :func:`__import__()` function is
already fully working and so can be used to import builtin modules like
:mod:`sys`. Patch by Victor Stinner.

3620
Python/importlib.h generated

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff