Skip to content

Commit 4e7af35

Browse files
patchback[bot]dongfangtianyu
andauthoredDec 12, 2024··
Fix ImportError crash when using --import-mode=importlib (#13053) (#13054)
Regression in #12716 In short: `PathFinder.find_spec` received the argument `/cow/moo` but loaded `/cow/moo/moo.py` instead. **Trigger conditions:** 1. `/cow/moo/moo.py` exists (a file and directory with the same name). 2. `/cow/moo/test_moo.py` exists (test case resides in the directory). When pytest loads test files in `importlib` mode, it continues recursive loading upward: - When loading `cow.moo`, it should return a namespace but unexpectedly returns a module. - When loading `cow.moo.moo`, it should return a module but unexpectedly returns a namespace. **Complete example:** [[GitHub repository](https://github.com/dongfangtianyu/pytest_importlib_issue)](https://github.com/dongfangtianyu/pytest_importlib_issue) - `main.py`: Reproduces the error. - `debug.py`: Demonstrates the behavior of `PathFinder.find_spec`. **Context:** #12592 (comment) #12592 (comment) --------- Co-authored-by: Bruno Oliveira <[email protected]> (cherry picked from commit 28e1e25) Co-authored-by: dongfangtianyu <[email protected]>
1 parent 179b690 commit 4e7af35

File tree

3 files changed

+39
-3
lines changed

3 files changed

+39
-3
lines changed
 

‎changelog/13053.bugfix.rst

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed a regression in pytest 8.3.4 where, when using ``--import-mode=importlib``, a directory containing py file with the same name would cause an ``ImportError``

‎src/_pytest/pathlib.py

+10-3
Original file line numberDiff line numberDiff line change
@@ -694,9 +694,16 @@ def _import_module_using_spec(
694694
# Checking with sys.meta_path first in case one of its hooks can import this module,
695695
# such as our own assertion-rewrite hook.
696696
for meta_importer in sys.meta_path:
697-
spec = meta_importer.find_spec(
698-
module_name, [str(module_location), str(module_path)]
699-
)
697+
module_name_of_meta = getattr(meta_importer.__class__, "__module__", "")
698+
if module_name_of_meta == "_pytest.assertion.rewrite" and module_path.is_file():
699+
# Import modules in subdirectories by module_path
700+
# to ensure assertion rewrites are not missed (#12659).
701+
find_spec_path = [str(module_location), str(module_path)]
702+
else:
703+
find_spec_path = [str(module_location)]
704+
705+
spec = meta_importer.find_spec(module_name, find_spec_path)
706+
700707
if spec_matches_module_path(spec, module_path):
701708
break
702709
else:

‎testing/test_pathlib.py

+28
Original file line numberDiff line numberDiff line change
@@ -1498,6 +1498,34 @@ def test():
14981498
]
14991499
)
15001500

1501+
def test_ns_multiple_levels_import_error(
1502+
self,
1503+
tmp_path: Path,
1504+
pytester: Pytester,
1505+
) -> None:
1506+
# Trigger condition 1: ns and file with the same name
1507+
file = pytester.path / "cow/moo/moo.py"
1508+
file.parent.mkdir(parents=True)
1509+
file.write_text("data=123", encoding="utf-8")
1510+
1511+
# Trigger condition 2: tests are located in ns
1512+
tests = pytester.path / "cow/moo/test_moo.py"
1513+
1514+
tests.write_text(
1515+
dedent(
1516+
"""
1517+
from cow.moo.moo import data
1518+
1519+
def test_moo():
1520+
print(data)
1521+
"""
1522+
),
1523+
encoding="utf-8",
1524+
)
1525+
1526+
result = pytester.runpytest("--import-mode=importlib")
1527+
assert result.ret == ExitCode.OK
1528+
15011529
@pytest.mark.parametrize("import_mode", ["prepend", "append", "importlib"])
15021530
def test_incorrect_namespace_package(
15031531
self,

0 commit comments

Comments
 (0)
Please sign in to comment.