Skip to content

Commit be7e49f

Browse files
committed
Close #15386: There was a loophole that meant importlib.machinery and imp would sometimes reference an uninitialised copy of importlib._bootstrap
1 parent 818b118 commit be7e49f

File tree

5 files changed

+27
-5
lines changed

5 files changed

+27
-5
lines changed

Lib/importlib/__init__.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,21 @@
22
__all__ = ['__import__', 'import_module', 'invalidate_caches']
33

44
# Bootstrap help #####################################################
5-
import imp
5+
6+
# Until bootstrapping is complete, DO NOT import any modules that attempt
7+
# to import importlib._bootstrap (directly or indirectly). Since this
8+
# partially initialised package would be present in sys.modules, those
9+
# modules would get an uninitialised copy of the source version, instead
10+
# of a fully initialised version (either the frozen one or the one
11+
# initialised below if the frozen one is not available).
12+
import _imp # Just the builtin component, NOT the full Python module
613
import sys
714

815
try:
916
import _frozen_importlib as _bootstrap
1017
except ImportError:
1118
from . import _bootstrap
12-
_bootstrap._setup(sys, imp)
19+
_bootstrap._setup(sys, _imp)
1320
else:
1421
# importlib._bootstrap is the built-in import, ensure we don't create
1522
# a second copy of the module.
@@ -22,6 +29,8 @@
2229
_w_long = _bootstrap._w_long
2330
_r_long = _bootstrap._r_long
2431

32+
# Fully bootstrapped at this point, import whatever you like, circular
33+
# dependencies and startup overhead minimisation permitting :)
2534

2635
# Public API #########################################################
2736

Lib/pkgutil.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
import os
44
import sys
5-
import imp
65
import importlib
6+
import imp
77
import os.path
88
from warnings import warn
99
from types import ModuleType

Lib/runpy.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212

1313
import os
1414
import sys
15+
import importlib.machinery # importlib first so we can test #15386 via -m
1516
import imp
16-
import importlib.machinery
1717
from pkgutil import read_code, get_loader, get_importer
1818

1919
__all__ = [

Lib/test/regrtest.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,9 @@
165165
option '-uall,-gui'.
166166
"""
167167

168+
# We import importlib *ASAP* in order to test #15386
169+
import importlib
170+
168171
import builtins
169172
import faulthandler
170173
import getopt

Lib/test/test_import.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
# We import importlib *ASAP* in order to test #15386
2+
import importlib
13
import builtins
24
import imp
35
from importlib.test.import_ import test_suite as importlib_import_test_suite
46
from importlib.test.import_ import util as importlib_util
5-
import importlib
67
import marshal
78
import os
89
import platform
@@ -777,6 +778,15 @@ def test_frozen_importlib_is_bootstrap(self):
777778
self.assertEqual(mod.__package__, 'importlib')
778779
self.assertTrue(mod.__file__.endswith('_bootstrap.py'), mod.__file__)
779780

781+
def test_there_can_be_only_one(self):
782+
# Issue #15386 revealed a tricky loophole in the bootstrapping
783+
# This test is technically redundant, since the bug caused importing
784+
# this test module to crash completely, but it helps prove the point
785+
from importlib import machinery
786+
mod = sys.modules['_frozen_importlib']
787+
self.assertIs(machinery.FileFinder, mod.FileFinder)
788+
self.assertIs(imp.new_module, mod.new_module)
789+
780790

781791
class ImportTracebackTests(unittest.TestCase):
782792

0 commit comments

Comments
 (0)