Skip to content

Commit 572dfcd

Browse files
committed
Compare also paths on Windows when considering ImportPathMismatchError
On Windows, os.path.samefile returns false for paths mounted in UNC paths which point to the same location. I couldn't reproduce the actual case reported, but looking at the code it seems this commit should fix the issue. Fix #7678 Fix #8076
1 parent 902739c commit 572dfcd

File tree

3 files changed

+38
-1
lines changed

3 files changed

+38
-1
lines changed

changelog/7678.bugfix.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fixed bug where ``ImportPathMismatchError`` would be raised for files compiled in
2+
the host and loaded later from an UNC mounted path (Windows).

src/_pytest/pathlib.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,7 @@ def import_path(
543543
module_file = module_file[: -(len(os.path.sep + "__init__.py"))]
544544

545545
try:
546-
is_same = os.path.samefile(str(path), module_file)
546+
is_same = _is_same(str(path), module_file)
547547
except FileNotFoundError:
548548
is_same = False
549549

@@ -553,6 +553,20 @@ def import_path(
553553
return mod
554554

555555

556+
# Implement a special _is_same function on Windows which returns True if the two filenames
557+
# compare equal, to circumvent os.path.samefile returning False for mounts in UNC (#7678).
558+
if sys.platform.startswith("win"):
559+
560+
def _is_same(f1: str, f2: str) -> bool:
561+
return Path(f1) == Path(f2) or os.path.samefile(f1, f2)
562+
563+
564+
else:
565+
566+
def _is_same(f1: str, f2: str) -> bool:
567+
return os.path.samefile(f1, f2)
568+
569+
556570
def resolve_package_path(path: Path) -> Optional[Path]:
557571
"""Return the Python package path by looking for the last
558572
directory upwards which still contains an __init__.py.

testing/test_pathlib.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import py
88

99
import pytest
10+
from _pytest.monkeypatch import MonkeyPatch
1011
from _pytest.pathlib import bestrelpath
1112
from _pytest.pathlib import commonpath
1213
from _pytest.pathlib import ensure_deletable
@@ -414,3 +415,23 @@ def test_visit_ignores_errors(tmpdir) -> None:
414415
"bar",
415416
"foo",
416417
]
418+
419+
420+
@pytest.mark.skipif(not sys.platform.startswith("win"), reason="Windows only")
421+
def test_samefile_false_negatives(tmp_path: Path, monkeypatch: MonkeyPatch) -> None:
422+
"""
423+
import_file() should not raise ImportPathMismatchError if the paths are exactly
424+
equal on Windows. It seems directories mounted as UNC paths make os.path.samefile
425+
return False, even when they are clearly equal.
426+
"""
427+
module_path = tmp_path.joinpath("my_module.py")
428+
module_path.write_text("def foo(): return 42")
429+
monkeypatch.syspath_prepend(tmp_path)
430+
431+
with monkeypatch.context() as mp:
432+
# Forcibly make os.path.samefile() return False here to ensure we are comparing
433+
# the paths too. Using a context to narrow the patch as much as possible given
434+
# this is an important system function.
435+
mp.setattr(os.path, "samefile", lambda x, y: False)
436+
module = import_path(module_path)
437+
assert getattr(module, "foo")() == 42

0 commit comments

Comments
 (0)