Skip to content

Commit 2f7415c

Browse files
authored
Add child modules as attributes of parent modules. (#10338)
Failing to add child modules as attributes of parent module will prevent them from being accessible through parent module. Fix #10337
1 parent ba60649 commit 2f7415c

File tree

3 files changed

+27
-1
lines changed

3 files changed

+27
-1
lines changed

changelog/10337.bugfix.rst

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fixed but that fake intermediate modules generated by ``--import-mode=importlib`` would not include the
2+
child modules as attributes of the parent modules.

src/_pytest/pathlib.py

+13-1
Original file line numberDiff line numberDiff line change
@@ -633,6 +633,9 @@ def insert_missing_modules(modules: Dict[str, ModuleType], module_name: str) ->
633633
otherwise "src.tests.test_foo" is not importable by ``__import__``.
634634
"""
635635
module_parts = module_name.split(".")
636+
child_module: Union[ModuleType, None] = None
637+
module: Union[ModuleType, None] = None
638+
child_name: str = ""
636639
while module_name:
637640
if module_name not in modules:
638641
try:
@@ -642,13 +645,22 @@ def insert_missing_modules(modules: Dict[str, ModuleType], module_name: str) ->
642645
# ourselves to fall back to creating a dummy module.
643646
if not sys.meta_path:
644647
raise ModuleNotFoundError
645-
importlib.import_module(module_name)
648+
module = importlib.import_module(module_name)
646649
except ModuleNotFoundError:
647650
module = ModuleType(
648651
module_name,
649652
doc="Empty module created by pytest's importmode=importlib.",
650653
)
654+
else:
655+
module = modules[module_name]
656+
if child_module:
657+
# Add child attribute to the parent that can reference the child
658+
# modules.
659+
if not hasattr(module, child_name):
660+
setattr(module, child_name, child_module)
651661
modules[module_name] = module
662+
# Keep track of the child module while moving up the tree.
663+
child_module, child_name = module, module_name.rpartition(".")[-1]
652664
module_parts.pop(-1)
653665
module_name = ".".join(module_parts)
654666

testing/test_pathlib.py

+12
Original file line numberDiff line numberDiff line change
@@ -592,3 +592,15 @@ def test_insert_missing_modules(
592592
modules = {}
593593
insert_missing_modules(modules, "")
594594
assert modules == {}
595+
596+
def test_parent_contains_child_module_attribute(
597+
self, monkeypatch: MonkeyPatch, tmp_path: Path
598+
):
599+
monkeypatch.chdir(tmp_path)
600+
# Use 'xxx' and 'xxy' as parent names as they are unlikely to exist and
601+
# don't end up being imported.
602+
modules = {"xxx.tests.foo": ModuleType("xxx.tests.foo")}
603+
insert_missing_modules(modules, "xxx.tests.foo")
604+
assert sorted(modules) == ["xxx", "xxx.tests", "xxx.tests.foo"]
605+
assert modules["xxx"].tests is modules["xxx.tests"]
606+
assert modules["xxx.tests"].foo is modules["xxx.tests.foo"]

0 commit comments

Comments
 (0)