Skip to content

Commit 0409481

Browse files
committed
Add more tests
1 parent e6cb131 commit 0409481

File tree

1 file changed

+154
-0
lines changed

1 file changed

+154
-0
lines changed

Lib/test/test_import/test_lazy_imports.py

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,160 @@ def access_modules(idx):
519519
self.assertEqual(result.returncode, 0, f"stdout: {result.stdout}, stderr: {result.stderr}")
520520
self.assertIn("OK", result.stdout)
521521

522+
def test_concurrent_lazy_modules_set_updates(self):
523+
"""Multiple threads creating lazy imports should safely update sys.lazy_modules."""
524+
code = textwrap.dedent("""
525+
import sys
526+
import threading
527+
528+
num_threads = 16
529+
iterations = 50
530+
errors = []
531+
barrier = threading.Barrier(num_threads)
532+
533+
def create_lazy_imports(idx):
534+
try:
535+
barrier.wait()
536+
for i in range(iterations):
537+
exec(f"lazy import json as json_{idx}_{i}", globals())
538+
exec(f"lazy import os as os_{idx}_{i}", globals())
539+
except Exception as e:
540+
errors.append((idx, e))
541+
542+
threads = [
543+
threading.Thread(target=create_lazy_imports, args=(i,))
544+
for i in range(num_threads)
545+
]
546+
547+
for t in threads:
548+
t.start()
549+
for t in threads:
550+
t.join()
551+
552+
assert not errors, f"Errors: {errors}"
553+
assert isinstance(sys.lazy_modules, set), "sys.lazy_modules is not a set"
554+
print("OK")
555+
""")
556+
557+
result = subprocess.run(
558+
[sys.executable, "-c", code],
559+
capture_output=True,
560+
text=True
561+
)
562+
self.assertEqual(result.returncode, 0, f"stdout: {result.stdout}, stderr: {result.stderr}")
563+
self.assertIn("OK", result.stdout)
564+
565+
def test_concurrent_reification_same_module_high_contention(self):
566+
"""High contention: many threads reifying the exact same lazy import."""
567+
code = textwrap.dedent("""
568+
import sys
569+
import threading
570+
import types
571+
572+
sys.set_lazy_imports("all")
573+
574+
lazy import json
575+
576+
num_threads = 20
577+
results = [None] * num_threads
578+
errors = []
579+
barrier = threading.Barrier(num_threads)
580+
581+
def access_json(idx):
582+
try:
583+
barrier.wait()
584+
for _ in range(100):
585+
_ = json.dumps
586+
_ = json.loads
587+
results[idx] = json
588+
except Exception as e:
589+
errors.append((idx, e))
590+
591+
threads = [
592+
threading.Thread(target=access_json, args=(i,))
593+
for i in range(num_threads)
594+
]
595+
596+
for t in threads:
597+
t.start()
598+
for t in threads:
599+
t.join()
600+
601+
assert not errors, f"Errors: {errors}"
602+
assert all(r is not None for r in results), "Some threads got None"
603+
first = results[0]
604+
assert all(r is first for r in results), "Inconsistent module objects"
605+
assert not isinstance(first, types.LazyImportType), "Got lazy import instead of module"
606+
print("OK")
607+
""")
608+
609+
result = subprocess.run(
610+
[sys.executable, "-c", code],
611+
capture_output=True,
612+
text=True
613+
)
614+
self.assertEqual(result.returncode, 0, f"stdout: {result.stdout}, stderr: {result.stderr}")
615+
self.assertIn("OK", result.stdout)
616+
617+
def test_concurrent_reification_with_module_attribute_access(self):
618+
"""Threads racing to reify and immediately access module attributes."""
619+
code = textwrap.dedent("""
620+
import sys
621+
import threading
622+
623+
sys.set_lazy_imports("all")
624+
625+
lazy import collections
626+
lazy import functools
627+
lazy import itertools
628+
629+
num_threads = 12
630+
results = {}
631+
errors = []
632+
barrier = threading.Barrier(num_threads)
633+
634+
def stress_lazy_imports(idx):
635+
try:
636+
barrier.wait()
637+
for _ in range(50):
638+
_ = collections.OrderedDict
639+
_ = functools.partial
640+
_ = itertools.chain
641+
_ = collections.defaultdict
642+
_ = functools.lru_cache
643+
_ = itertools.islice
644+
results[idx] = (
645+
type(collections).__name__,
646+
type(functools).__name__,
647+
type(itertools).__name__,
648+
)
649+
except Exception as e:
650+
errors.append((idx, e))
651+
652+
threads = [
653+
threading.Thread(target=stress_lazy_imports, args=(i,))
654+
for i in range(num_threads)
655+
]
656+
657+
for t in threads:
658+
t.start()
659+
for t in threads:
660+
t.join()
661+
662+
assert not errors, f"Errors: {errors}"
663+
for idx, types_tuple in results.items():
664+
assert all(t == 'module' for t in types_tuple), f"Thread {idx}: {types_tuple}"
665+
print("OK")
666+
""")
667+
668+
result = subprocess.run(
669+
[sys.executable, "-c", code],
670+
capture_output=True,
671+
text=True
672+
)
673+
self.assertEqual(result.returncode, 0, f"stdout: {result.stdout}, stderr: {result.stderr}")
674+
self.assertIn("OK", result.stdout)
675+
522676

523677
if __name__ == '__main__':
524678
unittest.main()

0 commit comments

Comments
 (0)