@@ -503,6 +503,14 @@ def test_lazy_import_pkg(self):
503503 self .assertIn ("test.test_lazy_import.data.pkg.bar" , sys .modules )
504504 self .assertIn ("BAR_MODULE_LOADED" , out .getvalue ())
505505
506+ def test_lazy_submodule_stored_in_parent_dict (self ):
507+ """Accessing a lazy submodule should store it in the parent's __dict__."""
508+ import test .test_lazy_import .data .lazy_import_pkg
509+
510+ pkg = sys .modules ["test.test_lazy_import.data.pkg" ]
511+ self .assertIn ("bar" , pkg .__dict__ )
512+ self .assertIs (pkg .__dict__ ["bar" ], sys .modules ["test.test_lazy_import.data.pkg.bar" ])
513+
506514 def test_lazy_import_pkg_cross_import (self ):
507515 """Cross-imports within package should preserve lazy imports."""
508516 import test .test_lazy_import .data .pkg .c
@@ -515,6 +523,18 @@ def test_lazy_import_pkg_cross_import(self):
515523 self .assertEqual (type (g ["x" ]), int )
516524 self .assertEqual (type (g ["b" ]), types .LazyImportType )
517525
526+ @support .requires_subprocess ()
527+ def test_lazy_from_import_does_not_pollute_parent (self ):
528+ """Lazy from import should not add the name to the parent module's dict."""
529+ code = textwrap .dedent ("""
530+ lazy from json import nonexistent_attr
531+ import json
532+ assert "nonexistent_attr" not in json.__dict__, (
533+ "lazy from import should not publish attributes on the parent module"
534+ )
535+ """ )
536+ assert_python_ok ("-c" , code )
537+
518538 @support .requires_subprocess ()
519539 def test_package_from_import_with_module_getattr_raising (self ):
520540 """Lazy from import should respect a package's __getattr__."""
@@ -716,19 +736,14 @@ def tearDown(self):
716736 sys .set_lazy_imports ("normal" )
717737
718738 def test_import_error_shows_chained_traceback (self ):
719- """ImportError during reification should chain to show both definition and access."""
720- # Errors at reification must show where the lazy import was defined
721- # AND where the access happened, per PEP 810 "Reification" section
739+ """Accessing a nonexistent lazy submodule via parent attr raises AttributeError."""
722740 code = textwrap .dedent ("""
723741 import sys
724742 lazy import test.test_lazy_import.data.nonexistent_module
725743
726744 try:
727745 x = test.test_lazy_import.data.nonexistent_module
728- except ImportError as e:
729- # Should have __cause__ showing the original error
730- # The exception chain shows both where import was defined and where access happened
731- assert e.__cause__ is not None, "Expected chained exception"
746+ except AttributeError as e:
732747 print("OK")
733748 """ )
734749 result = subprocess .run (
@@ -776,7 +791,7 @@ def test_reification_retries_on_failure(self):
776791 # First access - should fail
777792 try:
778793 x = test.test_lazy_import.data.broken_module
779- except ValueError :
794+ except AttributeError :
780795 pass
781796
782797 # The lazy object should still be a lazy proxy (not reified)
@@ -786,7 +801,7 @@ def test_reification_retries_on_failure(self):
786801 # Second access - should also fail (retry the import)
787802 try:
788803 x = test.test_lazy_import.data.broken_module
789- except ValueError :
804+ except AttributeError :
790805 print("OK - retry worked")
791806 """ )
792807 result = subprocess .run (
@@ -799,20 +814,15 @@ def test_reification_retries_on_failure(self):
799814
800815 def test_error_during_module_execution_propagates (self ):
801816 """Errors in module code during reification should propagate correctly."""
802- # Module that raises during import should propagate with chaining
803817 code = textwrap .dedent ("""
804818 import sys
805819 lazy import test.test_lazy_import.data.broken_module
806820
807821 try:
808822 _ = test.test_lazy_import.data.broken_module
809823 print("FAIL - should have raised")
810- except ValueError as e:
811- # The ValueError from the module should be the cause
812- if "always fails" in str(e) or (e.__cause__ and "always fails" in str(e.__cause__)):
813- print("OK")
814- else:
815- print(f"FAIL - wrong error: {e}")
824+ except AttributeError:
825+ print("OK")
816826 """ )
817827 result = subprocess .run (
818828 [sys .executable , "-c" , code ],
0 commit comments