Skip to content

Commit

Permalink
bpo-42403: Simplify importlib external bootstrap (pythonGH-23397)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
vstinner authored Nov 19, 2020
1 parent 7d9d25d commit 3390347
Show file tree
Hide file tree
Showing 6 changed files with 4,503 additions and 4,546 deletions.
2 changes: 1 addition & 1 deletion Lib/importlib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
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'
Expand Down
7 changes: 7 additions & 0 deletions Lib/importlib/_bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,15 @@

# 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__']:
Expand Down
111 changes: 47 additions & 64 deletions Lib/importlib/_bootstrap_external.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,36 @@
# 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'
Expand All @@ -42,6 +72,8 @@ def _relax_case():
return False
return _relax_case

_relax_case = _make_relax_case()


def _pack_uint32(x):
"""Convert a 32-bit integer to little-endian."""
Expand Down Expand Up @@ -294,7 +326,11 @@ def _write_atomic(path, data, mode=0o666):
_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.
Expand Down Expand Up @@ -469,15 +505,18 @@ def _check_name_wrapper(self, name=None, *args, **kwargs):
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

Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -1060,10 +1099,6 @@ def get_source(self, fullname):
return None


# Filled in by _setup().
EXTENSION_SUFFIXES = []


class ExtensionFileLoader(FileLoader, _LoaderBasics):

"""Loader for extension modules.
Expand Down Expand Up @@ -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)
Original file line number Diff line number Diff line change
@@ -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.
Loading

0 comments on commit 3390347

Please sign in to comment.