Skip to content

Commit 31ffd94

Browse files
committed
importlib: set module as attribute in its parent module
This needs to be done manually, not being done as part of the import mechanism: https://github.com/python/cpython/blob/73906d5c908c1e0b73c5436faeff7d93698fc074/Lib/importlib/_bootstrap.py#L1335-L1342 Fix pytest-dev#12194
1 parent 089116b commit 31ffd94

File tree

3 files changed

+41
-0
lines changed

3 files changed

+41
-0
lines changed

changelog/12194.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed a bug with ``--importmode=importlib`` and ``--doctest-modules`` where child modules did not appear as attributes in parent modules.

src/_pytest/pathlib.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -641,11 +641,26 @@ def _import_module_using_spec(
641641
spec.loader.exec_module(mod) # type: ignore[union-attr]
642642
if insert_modules:
643643
insert_missing_modules(sys.modules, module_name)
644+
_set_name_in_parent(mod)
644645
return mod
645646

646647
return None
647648

648649

650+
def _set_name_in_parent(module: ModuleType) -> None:
651+
"""
652+
Sets an attribute in the module's parent pointing to the module itself (#12194).
653+
654+
Based on https://github.com/python/cpython/blob/73906d5c908c1e0b73c5436faeff7d93698fc074/Lib/importlib/_bootstrap.py#L1335-L1342.
655+
"""
656+
parent, _, name = module.__name__.rpartition(".")
657+
if not parent:
658+
return
659+
parent_module = sys.modules.get(parent)
660+
if parent_module is not None:
661+
setattr(sys.modules[parent], name, module)
662+
663+
649664
def spec_matches_module_path(
650665
module_spec: Optional[ModuleSpec], module_path: Path
651666
) -> bool:

testing/test_pathlib.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1126,6 +1126,31 @@ def test_safe_exists(tmp_path: Path) -> None:
11261126
assert safe_exists(p) is False
11271127

11281128

1129+
def test_import_sets_module_as_attribute(pytester: Pytester) -> None:
1130+
"""Regression test for #12194."""
1131+
pytester.path.joinpath("foo/bar/baz").mkdir(parents=True)
1132+
pytester.path.joinpath("foo/__init__.py").touch()
1133+
pytester.path.joinpath("foo/bar/__init__.py").touch()
1134+
pytester.path.joinpath("foo/bar/baz/__init__.py").touch()
1135+
f = pytester.makepyfile(
1136+
"""
1137+
import foo
1138+
from foo.bar import baz
1139+
foo.bar.baz
1140+
1141+
def test_foo() -> None:
1142+
pass
1143+
"""
1144+
)
1145+
1146+
pytester.syspathinsert()
1147+
result = pytester.runpython(f)
1148+
assert result.ret == 0
1149+
1150+
result = pytester.runpytest("--import-mode=importlib", "--doctest-modules")
1151+
assert result.ret == 0
1152+
1153+
11291154
class TestNamespacePackages:
11301155
"""Test import_path support when importing from properly namespace packages."""
11311156

0 commit comments

Comments
 (0)