Skip to content

Commit 8cff128

Browse files
fix AttributeError crash when using --import-mode=importlib (#13029)
Only parent modules with the `__path__` attribute can be used by the `find_spec` function, and most of the standard library does not meet this condition. Fixes #13026 .
1 parent ecde993 commit 8cff128

File tree

3 files changed

+36
-1
lines changed

3 files changed

+36
-1
lines changed

changelog/13026.bugfix.rst

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed :class:`AttributeError` crash when using ``--import-mode=importlib`` when top-level directory same name as another module of the standard library.

src/_pytest/pathlib.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -668,7 +668,10 @@ def _import_module_using_spec(
668668
parent_module: ModuleType | None = None
669669
if parent_module_name:
670670
parent_module = sys.modules.get(parent_module_name)
671-
if parent_module is None:
671+
# If the parent_module lacks the `__path__` attribute, AttributeError when finding a submodule's spec,
672+
# requiring re-import according to the path.
673+
need_reimport = not hasattr(parent_module, "__path__")
674+
if parent_module is None or need_reimport:
672675
# Get parent_location based on location, get parent_path based on path.
673676
if module_path.name == "__init__.py":
674677
# If the current module is in a package,

testing/test_pathlib.py

+31
Original file line numberDiff line numberDiff line change
@@ -922,6 +922,37 @@ def test_my_test():
922922
result = pytester.runpytest("--import-mode=importlib")
923923
result.stdout.fnmatch_lines("* 1 passed *")
924924

925+
@pytest.mark.parametrize("name", ["code", "time", "math"])
926+
def test_importlib_same_name_as_stl(
927+
self, pytester, ns_param: bool, tmp_path: Path, name: str
928+
):
929+
"""Import a namespace package with the same name as the standard library (#13026)."""
930+
file_path = pytester.path / f"{name}/foo/test_demo.py"
931+
file_path.parent.mkdir(parents=True)
932+
file_path.write_text(
933+
dedent(
934+
"""
935+
def test_demo():
936+
pass
937+
"""
938+
),
939+
encoding="utf-8",
940+
)
941+
942+
# unit test
943+
__import__(name) # import standard library
944+
945+
import_path( # import user files
946+
file_path,
947+
mode=ImportMode.importlib,
948+
root=pytester.path,
949+
consider_namespace_packages=ns_param,
950+
)
951+
952+
# E2E test
953+
result = pytester.runpytest("--import-mode=importlib")
954+
result.stdout.fnmatch_lines("* 1 passed *")
955+
925956
def create_installed_doctests_and_tests_dir(
926957
self, path: Path, monkeypatch: MonkeyPatch
927958
) -> tuple[Path, Path, Path]:

0 commit comments

Comments
 (0)