@@ -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
523677if __name__ == '__main__' :
524678 unittest .main ()
0 commit comments