Skip to content

Commit

Permalink
importlib: set module as attribute in its parent module
Browse files Browse the repository at this point in the history
  • Loading branch information
nicoddemus committed Apr 20, 2024
1 parent ad95d59 commit 70dd4b0
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 0 deletions.
1 change: 1 addition & 0 deletions changelog/12194.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed a bug with ``--importmode=importlib`` and ``--doctest-modules`` where child modules did not appear as attributes in parent modules.
15 changes: 15 additions & 0 deletions src/_pytest/pathlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -641,11 +641,26 @@ def _import_module_using_spec(
spec.loader.exec_module(mod) # type: ignore[union-attr]
if insert_modules:
insert_missing_modules(sys.modules, module_name)
_set_name_in_parent(mod)
return mod

return None


def _set_name_in_parent(module: ModuleType) -> None:
"""
Sets an attribute in the module's parent pointing to the module itself (#12194).
Based on https://github.com/python/cpython/blob/73906d5c908c1e0b73c5436faeff7d93698fc074/Lib/importlib/_bootstrap.py#L1335-L1342.
"""
parent, _, name = module.__name__.rpartition(".")
if not parent:
return
parent_module = sys.modules.get(parent)
if parent_module is not None:
setattr(sys.modules[parent], name, module)


def spec_matches_module_path(
module_spec: Optional[ModuleSpec], module_path: Path
) -> bool:
Expand Down
25 changes: 25 additions & 0 deletions testing/test_pathlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -1126,6 +1126,31 @@ def test_safe_exists(tmp_path: Path) -> None:
assert safe_exists(p) is False


def test_import_sets_module_as_attribute(pytester: Pytester) -> None:
"""Regression test for #12194."""
pytester.path.joinpath("foo/bar/baz").mkdir(parents=True)
pytester.path.joinpath("foo/__init__.py").touch()
pytester.path.joinpath("foo/bar/__init__.py").touch()
pytester.path.joinpath("foo/bar/baz/__init__.py").touch()
f = pytester.makepyfile(
"""
import foo
from foo.bar import baz
foo.bar.baz
def test_foo() -> None:
pass
"""
)

pytester.syspathinsert()
result = pytester.runpython(f)
assert result.ret == 0

result = pytester.runpytest("--import-mode=importlib", "--doctest-modules")
assert result.ret == 0


class TestNamespacePackages:
"""Test import_path support when importing from properly namespace packages."""

Expand Down

0 comments on commit 70dd4b0

Please sign in to comment.