Skip to content

Commit 3db717c

Browse files
committed
TEST: Reproduce gh-114763
1 parent d57f4eb commit 3db717c

File tree

1 file changed

+38
-2
lines changed

1 file changed

+38
-2
lines changed

Lib/test/test_importlib/test_lazy.py

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
from importlib import abc
33
from importlib import util
44
import sys
5+
import time
6+
import threading
57
import types
68
import unittest
79

@@ -40,6 +42,7 @@ class TestingImporter(abc.MetaPathFinder, abc.Loader):
4042
module_name = 'lazy_loader_test'
4143
mutated_name = 'changed'
4244
loaded = None
45+
load_count = 0
4346
source_code = 'attr = 42; __name__ = {!r}'.format(mutated_name)
4447

4548
def find_spec(self, name, path, target=None):
@@ -48,8 +51,10 @@ def find_spec(self, name, path, target=None):
4851
return util.spec_from_loader(name, util.LazyLoader(self))
4952

5053
def exec_module(self, module):
54+
time.sleep(0.01) # Simulate a slow load.
5155
exec(self.source_code, module.__dict__)
5256
self.loaded = module
57+
self.load_count += 1
5358

5459

5560
class LazyLoaderTests(unittest.TestCase):
@@ -59,8 +64,9 @@ def test_init(self):
5964
# Classes that don't define exec_module() trigger TypeError.
6065
util.LazyLoader(object)
6166

62-
def new_module(self, source_code=None):
63-
loader = TestingImporter()
67+
def new_module(self, source_code=None, loader=None):
68+
if loader is None:
69+
loader = TestingImporter()
6470
if source_code is not None:
6571
loader.source_code = source_code
6672
spec = util.spec_from_loader(TestingImporter.module_name,
@@ -140,6 +146,36 @@ def test_module_already_in_sys(self):
140146
# Force the load; just care that no exception is raised.
141147
module.__name__
142148

149+
def test_module_load_race(self):
150+
with test_util.uncache(TestingImporter.module_name):
151+
loader = TestingImporter()
152+
module = self.new_module(loader=loader)
153+
assert loader.load_count == 0
154+
155+
class RaisingThread(threading.Thread):
156+
exc = None
157+
def run(self):
158+
try:
159+
super().run()
160+
except Exception as exc:
161+
self.exc = exc
162+
163+
def access_module():
164+
return module.attr
165+
166+
threads = []
167+
for _ in range(2):
168+
threads.append(thread := RaisingThread(target=access_module))
169+
thread.start()
170+
171+
# Races could cause errors
172+
for thread in threads:
173+
thread.join()
174+
assert thread.exc is None
175+
176+
# Or multiple load attempts
177+
assert loader.load_count == 1
178+
143179

144180
if __name__ == '__main__':
145181
unittest.main()

0 commit comments

Comments
 (0)