Skip to content

Commit 8d9b95d

Browse files
authored
Fix collection of short paths on Windows (#11936)
Passing a short path in the command line was causing the matchparts check to fail, because ``Path(short_path) != Path(long_path)``. Using ``os.path.samefile`` as fallback ensures the comparsion works on Windows when comparing short/long paths. Fix #11895
1 parent 010ce2a commit 8d9b95d

File tree

3 files changed

+33
-0
lines changed

3 files changed

+33
-0
lines changed

changelog/11895.bugfix.rst

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix collection on Windows where initial paths contain the short version of a path (for example ``c:\PROGRA~1\tests``).

src/_pytest/main.py

+4
Original file line numberDiff line numberDiff line change
@@ -906,6 +906,10 @@ def collect(self) -> Iterator[Union[nodes.Item, nodes.Collector]]:
906906
# Path part e.g. `/a/b/` in `/a/b/test_file.py::TestIt::test_it`.
907907
if isinstance(matchparts[0], Path):
908908
is_match = node.path == matchparts[0]
909+
if sys.platform == "win32" and not is_match:
910+
# In case the file paths do not match, fallback to samefile() to
911+
# account for short-paths on Windows (#11895).
912+
is_match = os.path.samefile(node.path, matchparts[0])
909913
# Name part e.g. `TestIt` in `/a/b/test_file.py::TestIt::test_it`.
910914
else:
911915
# TODO: Remove parametrized workaround once collection structure contains

testing/test_collection.py

+28
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44
import pprint
55
import shutil
66
import sys
7+
import tempfile
78
import textwrap
89
from typing import List
910

11+
from _pytest.assertion.util import running_on_ci
1012
from _pytest.config import ExitCode
1113
from _pytest.fixtures import FixtureRequest
1214
from _pytest.main import _in_venv
@@ -1759,3 +1761,29 @@ def test_foo(): assert True
17591761

17601762
assert result.ret == ExitCode.OK
17611763
assert result.parseoutcomes() == {"passed": 1}
1764+
1765+
1766+
@pytest.mark.skipif(not sys.platform.startswith("win"), reason="Windows only")
1767+
def test_collect_short_file_windows(pytester: Pytester) -> None:
1768+
"""Reproducer for #11895: short paths not colleced on Windows."""
1769+
short_path = tempfile.mkdtemp()
1770+
if "~" not in short_path: # pragma: no cover
1771+
if running_on_ci():
1772+
# On CI, we are expecting that under the current GitHub actions configuration,
1773+
# tempfile.mkdtemp() is producing short paths, so we want to fail to prevent
1774+
# this from silently changing without us noticing.
1775+
pytest.fail(
1776+
f"tempfile.mkdtemp() failed to produce a short path on CI: {short_path}"
1777+
)
1778+
else:
1779+
# We want to skip failing this test locally in this situation because
1780+
# depending on the local configuration tempfile.mkdtemp() might not produce a short path:
1781+
# For example, user might have configured %TEMP% exactly to avoid generating short paths.
1782+
pytest.skip(
1783+
f"tempfile.mkdtemp() failed to produce a short path: {short_path}, skipping"
1784+
)
1785+
1786+
test_file = Path(short_path).joinpath("test_collect_short_file_windows.py")
1787+
test_file.write_text("def test(): pass", encoding="UTF-8")
1788+
result = pytester.runpytest(short_path)
1789+
assert result.parseoutcomes() == {"passed": 1}

0 commit comments

Comments
 (0)