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
This commit is contained in:
Bruno Oliveira 2020-12-12 08:49:58 -03:00
parent 902739cfc3
commit 572dfcd160
3 changed files with 38 additions and 1 deletions

View File

@ -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).

View File

@ -543,7 +543,7 @@ def import_path(
module_file = module_file[: -(len(os.path.sep + "__init__.py"))] module_file = module_file[: -(len(os.path.sep + "__init__.py"))]
try: try:
is_same = os.path.samefile(str(path), module_file) is_same = _is_same(str(path), module_file)
except FileNotFoundError: except FileNotFoundError:
is_same = False is_same = False
@ -553,6 +553,20 @@ def import_path(
return mod 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]: def resolve_package_path(path: Path) -> Optional[Path]:
"""Return the Python package path by looking for the last """Return the Python package path by looking for the last
directory upwards which still contains an __init__.py. directory upwards which still contains an __init__.py.

View File

@ -7,6 +7,7 @@ from textwrap import dedent
import py import py
import pytest import pytest
from _pytest.monkeypatch import MonkeyPatch
from _pytest.pathlib import bestrelpath from _pytest.pathlib import bestrelpath
from _pytest.pathlib import commonpath from _pytest.pathlib import commonpath
from _pytest.pathlib import ensure_deletable from _pytest.pathlib import ensure_deletable
@ -414,3 +415,23 @@ def test_visit_ignores_errors(tmpdir) -> None:
"bar", "bar",
"foo", "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