Skip to content

Commit a781980

Browse files
authored
Update importlib to 3.14.5 (RustPython#8013)
1 parent 6a860b6 commit a781980

7 files changed

Lines changed: 82 additions & 10 deletions

File tree

Lib/importlib/_bootstrap.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1375,6 +1375,14 @@ def _find_and_load(name, import_):
13751375
# NOTE: because of this, initializing must be set *before*
13761376
# putting the new module in sys.modules.
13771377
_lock_unlock_module(name)
1378+
else:
1379+
# Verify the module is still in sys.modules. Another thread may have
1380+
# removed it (due to import failure) between our sys.modules.get()
1381+
# above and the _initializing check. If removed, we retry the import
1382+
# to preserve normal semantics: the caller gets the exception from
1383+
# the actual import failure rather than a synthetic error.
1384+
if sys.modules.get(name) is not module:
1385+
return _find_and_load(name, import_)
13781386

13791387
if module is None:
13801388
message = f'import of {name} halted; None in sys.modules'

Lib/importlib/_bootstrap_external.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -946,7 +946,7 @@ def get_filename(self, fullname):
946946

947947
def get_data(self, path):
948948
"""Return the data from path as raw bytes."""
949-
if isinstance(self, (SourceLoader, ExtensionFileLoader)):
949+
if isinstance(self, (SourceLoader, SourcelessFileLoader, ExtensionFileLoader)):
950950
with _io.open_code(str(path)) as file:
951951
return file.read()
952952
else:

Lib/test/test_importlib/frozen/test_loader.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
from test.support import captured_stdout, import_helper, STDLIB_DIR
66
import contextlib
77
import os.path
8-
import sys
98
import types
109
import unittest
1110
import warnings
@@ -76,7 +75,7 @@ def test_module(self):
7675
self.assertHasAttr(module, '__spec__')
7776
self.assertEqual(module.__spec__.loader_state.origname, name)
7877

79-
@unittest.skipIf(sys.platform == "win32", "TODO: RUSTPYTHON")
78+
@unittest.skipIf(__import__("sys").platform == "win32", "TODO: RUSTPYTHON")
8079
def test_package(self):
8180
name = '__phello__'
8281
module, output = self.exec_module(name)
@@ -147,7 +146,7 @@ def test_get_source(self):
147146
result = self.machinery.FrozenImporter.get_source('__hello__')
148147
self.assertIsNone(result)
149148

150-
@unittest.skipIf(sys.platform == "win32", "TODO: RUSTPYTHON")
149+
@unittest.skipIf(__import__("sys").platform == "win32", "TODO: RUSTPYTHON")
151150
def test_is_package(self):
152151
# Should be able to tell what is a package.
153152
test_for = (('__hello__', False), ('__phello__', True),

Lib/test/test_importlib/resources/test_resource.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import os
21
import unittest
32

43
from . import util

Lib/test/test_importlib/test_locks.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,8 @@ def test_all_locks(self):
155155

156156
# TODO: RUSTPYTHON; dead weakref module locks not cleaned up in frozen bootstrap
157157
Frozen_LifetimeTests.test_all_locks = unittest.skip("TODO: RUSTPYTHON")(
158-
Frozen_LifetimeTests.test_all_locks)
159-
158+
Frozen_LifetimeTests.test_all_locks
159+
)
160160

161161
def setUpModule():
162162
thread_info = threading_helper.threading_setup()

Lib/test/test_importlib/test_threaded_import.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,72 @@ def test_multiprocessing_pool_circular_import(self, size):
263263
'partial', 'pool_in_threads.py')
264264
script_helper.assert_python_ok(fn)
265265

266+
@unittest.expectedFailure # TODO: RUSTPYTHON
267+
def test_import_failure_race_condition(self):
268+
# Regression test for race condition where a thread could receive
269+
# a partially-initialized module when another thread's import fails.
270+
# The race occurs when:
271+
# 1. Thread 1 starts importing, adds module to sys.modules
272+
# 2. Thread 2 sees the module in sys.modules
273+
# 3. Thread 1's import fails, removes module from sys.modules
274+
# 4. Thread 2 should NOT return the stale module reference
275+
os.mkdir(TESTFN)
276+
self.addCleanup(shutil.rmtree, TESTFN)
277+
sys.path.insert(0, TESTFN)
278+
self.addCleanup(sys.path.remove, TESTFN)
279+
280+
# Create a module that partially initializes then fails
281+
modname = 'failing_import_module'
282+
with open(os.path.join(TESTFN, modname + '.py'), 'w') as f:
283+
f.write('''
284+
import time
285+
PARTIAL_ATTR = 'initialized'
286+
time.sleep(0.05) # Widen race window
287+
raise RuntimeError("Intentional import failure")
288+
''')
289+
self.addCleanup(forget, modname)
290+
importlib.invalidate_caches()
291+
292+
errors = []
293+
results = []
294+
295+
def do_import(delay=0):
296+
time.sleep(delay)
297+
try:
298+
mod = __import__(modname)
299+
# If we got a module, verify it's in sys.modules
300+
if modname not in sys.modules:
301+
errors.append(
302+
f"Got module {mod!r} but {modname!r} not in sys.modules"
303+
)
304+
elif sys.modules[modname] is not mod:
305+
errors.append(
306+
f"Got different module than sys.modules[{modname!r}]"
307+
)
308+
else:
309+
results.append(('success', mod))
310+
except RuntimeError:
311+
results.append(('RuntimeError',))
312+
except Exception as e:
313+
errors.append(f"Unexpected exception: {e}")
314+
315+
# Run multiple iterations to increase chance of hitting the race
316+
for _ in range(10):
317+
errors.clear()
318+
results.clear()
319+
if modname in sys.modules:
320+
del sys.modules[modname]
321+
322+
t1 = threading.Thread(target=do_import, args=(0,))
323+
t2 = threading.Thread(target=do_import, args=(0.01,))
324+
t1.start()
325+
t2.start()
326+
t1.join()
327+
t2.join()
328+
329+
# Neither thread should have errors about stale modules
330+
self.assertEqual(errors, [], f"Race condition detected: {errors}")
331+
266332

267333
def setUpModule():
268334
thread_info = threading_helper.threading_setup()

Lib/test/test_importlib/util.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@
1515
import tempfile
1616
import types
1717

18-
try:
19-
_testsinglephase = import_helper.import_module("_testsinglephase")
18+
try: # TODO: RUSTPYTHON
19+
import_helper.import_module("_testmultiphase")
2020
except unittest.SkipTest:
21-
_testsinglephase = None # TODO: RUSTPYTHON
21+
_testmultiphase = None
2222

2323

2424
BUILTINS = types.SimpleNamespace()

0 commit comments

Comments
 (0)