Skip to content

Commit 3390347

Browse files
authored
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.
1 parent 7d9d25d commit 3390347

File tree

6 files changed

+4503
-4546
lines changed

6 files changed

+4503
-4546
lines changed

Lib/importlib/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
import _frozen_importlib_external as _bootstrap_external
3535
except ImportError:
3636
from . import _bootstrap_external
37-
_bootstrap_external._setup(_bootstrap)
37+
_bootstrap_external._set_bootstrap_module(_bootstrap)
3838
_bootstrap._bootstrap_external = _bootstrap_external
3939
else:
4040
_bootstrap_external.__name__ = 'importlib._bootstrap_external'

Lib/importlib/_bootstrap.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,15 @@
2222

2323
# Bootstrap-related code ######################################################
2424

25+
# Modules injected manually by _setup()
26+
_thread = None
27+
_warnings = None
28+
_weakref = None
29+
30+
# Import done by _install_external_importers()
2531
_bootstrap_external = None
2632

33+
2734
def _wrap(new, old):
2835
"""Simple substitute for functools.update_wrapper."""
2936
for replace in ['__module__', '__name__', '__qualname__', '__doc__']:

Lib/importlib/_bootstrap_external.py

Lines changed: 47 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,36 @@
1919
# reference any injected objects! This includes not only global code but also
2020
# anything specified at the class level.
2121

22+
# Module injected manually by _set_bootstrap_module()
23+
_bootstrap = None
24+
25+
# Import builtin modules
26+
import _imp
27+
import _io
28+
import sys
29+
import _warnings
30+
import marshal
31+
32+
33+
_MS_WINDOWS = (sys.platform == 'win32')
34+
if _MS_WINDOWS:
35+
import nt as _os
36+
import winreg
37+
else:
38+
import posix as _os
39+
40+
41+
if _MS_WINDOWS:
42+
path_separators = ['\\', '/']
43+
else:
44+
path_separators = ['/']
45+
# Assumption made in _path_join()
46+
assert all(len(sep) == 1 for sep in path_separators)
47+
path_sep = path_separators[0]
48+
path_separators = ''.join(path_separators)
49+
_pathseps_with_colon = {f':{s}' for s in path_separators}
50+
51+
2252
# Bootstrap-related code ######################################################
2353
_CASE_INSENSITIVE_PLATFORMS_STR_KEY = 'win',
2454
_CASE_INSENSITIVE_PLATFORMS_BYTES_KEY = 'cygwin', 'darwin'
@@ -42,6 +72,8 @@ def _relax_case():
4272
return False
4373
return _relax_case
4474

75+
_relax_case = _make_relax_case()
76+
4577

4678
def _pack_uint32(x):
4779
"""Convert a 32-bit integer to little-endian."""
@@ -294,7 +326,11 @@ def _write_atomic(path, data, mode=0o666):
294326
_PYCACHE = '__pycache__'
295327
_OPT = 'opt-'
296328

297-
SOURCE_SUFFIXES = ['.py'] # _setup() adds .pyw as needed.
329+
SOURCE_SUFFIXES = ['.py']
330+
if _MS_WINDOWS:
331+
SOURCE_SUFFIXES.append('.pyw')
332+
333+
EXTENSION_SUFFIXES = _imp.extension_suffixes()
298334

299335
BYTECODE_SUFFIXES = ['.pyc']
300336
# Deprecated.
@@ -469,15 +505,18 @@ def _check_name_wrapper(self, name=None, *args, **kwargs):
469505
raise ImportError('loader for %s cannot handle %s' %
470506
(self.name, name), name=name)
471507
return method(self, name, *args, **kwargs)
472-
try:
508+
509+
# FIXME: @_check_name is used to define class methods before the
510+
# _bootstrap module is set by _set_bootstrap_module().
511+
if _bootstrap is not None:
473512
_wrap = _bootstrap._wrap
474-
except NameError:
475-
# XXX yuck
513+
else:
476514
def _wrap(new, old):
477515
for replace in ['__module__', '__name__', '__qualname__', '__doc__']:
478516
if hasattr(old, replace):
479517
setattr(new, replace, getattr(old, replace))
480518
new.__dict__.update(old.__dict__)
519+
481520
_wrap(_check_name_wrapper, method)
482521
return _check_name_wrapper
483522

@@ -713,7 +752,7 @@ class WindowsRegistryFinder:
713752
REGISTRY_KEY_DEBUG = (
714753
'Software\\Python\\PythonCore\\{sys_version}'
715754
'\\Modules\\{fullname}\\Debug')
716-
DEBUG_BUILD = False # Changed in _setup()
755+
DEBUG_BUILD = (_MS_WINDOWS and '_d.pyd' in EXTENSION_SUFFIXES)
717756

718757
@classmethod
719758
def _open_registry(cls, key):
@@ -1060,10 +1099,6 @@ def get_source(self, fullname):
10601099
return None
10611100

10621101

1063-
# Filled in by _setup().
1064-
EXTENSION_SUFFIXES = []
1065-
1066-
10671102
class ExtensionFileLoader(FileLoader, _LoaderBasics):
10681103

10691104
"""Loader for extension modules.
@@ -1552,66 +1587,14 @@ def _get_supported_file_loaders():
15521587
return [extensions, source, bytecode]
15531588

15541589

1555-
def _setup(_bootstrap_module):
1556-
"""Setup the path-based importers for importlib by importing needed
1557-
built-in modules and injecting them into the global namespace.
1558-
1559-
Other components are extracted from the core bootstrap module.
1560-
1561-
"""
1562-
global sys, _imp, _bootstrap
1590+
def _set_bootstrap_module(_bootstrap_module):
1591+
global _bootstrap
15631592
_bootstrap = _bootstrap_module
1564-
sys = _bootstrap.sys
1565-
_imp = _bootstrap._imp
1566-
1567-
self_module = sys.modules[__name__]
1568-
1569-
# Directly load the os module (needed during bootstrap).
1570-
os_details = ('posix', ['/']), ('nt', ['\\', '/'])
1571-
for builtin_os, path_separators in os_details:
1572-
# Assumption made in _path_join()
1573-
assert all(len(sep) == 1 for sep in path_separators)
1574-
path_sep = path_separators[0]
1575-
if builtin_os in sys.modules:
1576-
os_module = sys.modules[builtin_os]
1577-
break
1578-
else:
1579-
try:
1580-
os_module = _bootstrap._builtin_from_name(builtin_os)
1581-
break
1582-
except ImportError:
1583-
continue
1584-
else:
1585-
raise ImportError('importlib requires posix or nt')
1586-
1587-
setattr(self_module, '_os', os_module)
1588-
setattr(self_module, 'path_sep', path_sep)
1589-
setattr(self_module, 'path_separators', ''.join(path_separators))
1590-
setattr(self_module, '_pathseps_with_colon', {f':{s}' for s in path_separators})
1591-
1592-
# Directly load built-in modules needed during bootstrap.
1593-
builtin_names = ['_io', '_warnings', 'marshal']
1594-
if builtin_os == 'nt':
1595-
builtin_names.append('winreg')
1596-
for builtin_name in builtin_names:
1597-
if builtin_name not in sys.modules:
1598-
builtin_module = _bootstrap._builtin_from_name(builtin_name)
1599-
else:
1600-
builtin_module = sys.modules[builtin_name]
1601-
setattr(self_module, builtin_name, builtin_module)
1602-
1603-
# Constants
1604-
setattr(self_module, '_relax_case', _make_relax_case())
1605-
EXTENSION_SUFFIXES.extend(_imp.extension_suffixes())
1606-
if builtin_os == 'nt':
1607-
SOURCE_SUFFIXES.append('.pyw')
1608-
if '_d.pyd' in EXTENSION_SUFFIXES:
1609-
WindowsRegistryFinder.DEBUG_BUILD = True
16101593

16111594

16121595
def _install(_bootstrap_module):
16131596
"""Install the path-based import components."""
1614-
_setup(_bootstrap_module)
1597+
_set_bootstrap_module(_bootstrap_module)
16151598
supported_loaders = _get_supported_file_loaders()
16161599
sys.path_hooks.extend([FileFinder.path_hook(*supported_loaders)])
16171600
sys.meta_path.append(PathFinder)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Simplify the :mod:`importlib` external bootstrap code:
2+
``importlib._bootstrap_external`` now uses regular imports to import builtin
3+
modules. When it is imported, the builtin :func:`__import__()` function is
4+
already fully working and so can be used to import builtin modules like
5+
:mod:`sys`. Patch by Victor Stinner.

0 commit comments

Comments
 (0)