@@ -450,6 +450,14 @@ def test_lazy_import_pkg(self):
450450 self .assertIn ("test.test_lazy_import.data.pkg.bar" , sys .modules )
451451 self .assertIn ("BAR_MODULE_LOADED" , out .getvalue ())
452452
453+ def test_lazy_submodule_stored_in_parent_dict (self ):
454+ """Accessing a lazy submodule should store it in the parent's __dict__."""
455+ import test .test_lazy_import .data .lazy_import_pkg
456+
457+ pkg = sys .modules ["test.test_lazy_import.data.pkg" ]
458+ self .assertIn ("bar" , pkg .__dict__ )
459+ self .assertIs (pkg .__dict__ ["bar" ], sys .modules ["test.test_lazy_import.data.pkg.bar" ])
460+
453461 def test_lazy_import_pkg_cross_import (self ):
454462 """Cross-imports within package should preserve lazy imports."""
455463 import test .test_lazy_import .data .pkg .c
@@ -462,6 +470,18 @@ def test_lazy_import_pkg_cross_import(self):
462470 self .assertEqual (type (g ["x" ]), int )
463471 self .assertEqual (type (g ["b" ]), types .LazyImportType )
464472
473+ @support .requires_subprocess ()
474+ def test_lazy_from_import_does_not_pollute_parent (self ):
475+ """Lazy from import should not add the name to the parent module's dict."""
476+ code = textwrap .dedent ("""
477+ lazy from json import nonexistent_attr
478+ import json
479+ assert "nonexistent_attr" not in json.__dict__, (
480+ "lazy from import should not publish attributes on the parent module"
481+ )
482+ """ )
483+ assert_python_ok ("-c" , code )
484+
465485 @support .requires_subprocess ()
466486 def test_package_from_import_with_module_getattr (self ):
467487 """Lazy from import should respect a package's __getattr__."""
@@ -613,19 +633,14 @@ def tearDown(self):
613633 sys .set_lazy_imports ("normal" )
614634
615635 def test_import_error_shows_chained_traceback (self ):
616- """ImportError during reification should chain to show both definition and access."""
617- # Errors at reification must show where the lazy import was defined
618- # AND where the access happened, per PEP 810 "Reification" section
636+ """Accessing a nonexistent lazy submodule via parent attr raises AttributeError."""
619637 code = textwrap .dedent ("""
620638 import sys
621639 lazy import test.test_lazy_import.data.nonexistent_module
622640
623641 try:
624642 x = test.test_lazy_import.data.nonexistent_module
625- except ImportError as e:
626- # Should have __cause__ showing the original error
627- # The exception chain shows both where import was defined and where access happened
628- assert e.__cause__ is not None, "Expected chained exception"
643+ except AttributeError as e:
629644 print("OK")
630645 """ )
631646 result = subprocess .run (
@@ -673,7 +688,7 @@ def test_reification_retries_on_failure(self):
673688 # First access - should fail
674689 try:
675690 x = test.test_lazy_import.data.broken_module
676- except ValueError :
691+ except AttributeError :
677692 pass
678693
679694 # The lazy object should still be a lazy proxy (not reified)
@@ -683,7 +698,7 @@ def test_reification_retries_on_failure(self):
683698 # Second access - should also fail (retry the import)
684699 try:
685700 x = test.test_lazy_import.data.broken_module
686- except ValueError :
701+ except AttributeError :
687702 print("OK - retry worked")
688703 """ )
689704 result = subprocess .run (
@@ -696,20 +711,15 @@ def test_reification_retries_on_failure(self):
696711
697712 def test_error_during_module_execution_propagates (self ):
698713 """Errors in module code during reification should propagate correctly."""
699- # Module that raises during import should propagate with chaining
700714 code = textwrap .dedent ("""
701715 import sys
702716 lazy import test.test_lazy_import.data.broken_module
703717
704718 try:
705719 _ = test.test_lazy_import.data.broken_module
706720 print("FAIL - should have raised")
707- except ValueError as e:
708- # The ValueError from the module should be the cause
709- if "always fails" in str(e) or (e.__cause__ and "always fails" in str(e.__cause__)):
710- print("OK")
711- else:
712- print(f"FAIL - wrong error: {e}")
721+ except AttributeError:
722+ print("OK")
713723 """ )
714724 result = subprocess .run (
715725 [sys .executable , "-c" , code ],
0 commit comments