diff --git a/changelog/7678.bugfix.rst b/changelog/7678.bugfix.rst new file mode 100644 index 00000000000..4adc6ffd119 --- /dev/null +++ b/changelog/7678.bugfix.rst @@ -0,0 +1,2 @@ +Fixed bug where ``ImportPathMismatchError`` would be raised for files compiled in +the host and loaded later from an UNC mounted path (Windows). diff --git a/src/_pytest/pathlib.py b/src/_pytest/pathlib.py index 6a36ae17ab2..8875a28f84b 100644 --- a/src/_pytest/pathlib.py +++ b/src/_pytest/pathlib.py @@ -543,7 +543,7 @@ def import_path( module_file = module_file[: -(len(os.path.sep + "__init__.py"))] try: - is_same = os.path.samefile(str(path), module_file) + is_same = _is_same(str(path), module_file) except FileNotFoundError: is_same = False @@ -553,6 +553,20 @@ def import_path( return mod +# Implement a special _is_same function on Windows which returns True if the two filenames +# compare equal, to circumvent os.path.samefile returning False for mounts in UNC (#7678). +if sys.platform.startswith("win"): + + def _is_same(f1: str, f2: str) -> bool: + return Path(f1) == Path(f2) or os.path.samefile(f1, f2) + + +else: + + def _is_same(f1: str, f2: str) -> bool: + return os.path.samefile(f1, f2) + + def resolve_package_path(path: Path) -> Optional[Path]: """Return the Python package path by looking for the last directory upwards which still contains an __init__.py. diff --git a/testing/test_pathlib.py b/testing/test_pathlib.py index 0507e3d6866..f60b9f26369 100644 --- a/testing/test_pathlib.py +++ b/testing/test_pathlib.py @@ -7,6 +7,7 @@ import py import pytest +from _pytest.monkeypatch import MonkeyPatch from _pytest.pathlib import bestrelpath from _pytest.pathlib import commonpath from _pytest.pathlib import ensure_deletable @@ -414,3 +415,23 @@ def test_visit_ignores_errors(tmpdir) -> None: "bar", "foo", ] + + +@pytest.mark.skipif(not sys.platform.startswith("win"), reason="Windows only") +def test_samefile_false_negatives(tmp_path: Path, monkeypatch: MonkeyPatch) -> None: + """ + import_file() should not raise ImportPathMismatchError if the paths are exactly + equal on Windows. It seems directories mounted as UNC paths make os.path.samefile + return False, even when they are clearly equal. + """ + module_path = tmp_path.joinpath("my_module.py") + module_path.write_text("def foo(): return 42") + monkeypatch.syspath_prepend(tmp_path) + + with monkeypatch.context() as mp: + # Forcibly make os.path.samefile() return False here to ensure we are comparing + # the paths too. Using a context to narrow the patch as much as possible given + # this is an important system function. + mp.setattr(os.path, "samefile", lambda x, y: False) + module = import_path(module_path) + assert getattr(module, "foo")() == 42