|
6 | 6 | import functools
|
7 | 7 | import importlib
|
8 | 8 | import os
|
| 9 | +import stat |
9 | 10 | from pathlib import Path
|
10 | 11 | import sys
|
11 | 12 | from typing import AbstractSet
|
@@ -443,6 +444,20 @@ def pytest_collection_modifyitems(items: List[nodes.Item], config: Config) -> No
|
443 | 444 | items[:] = remaining
|
444 | 445 |
|
445 | 446 |
|
| 447 | +def _is_junction(path: Path) -> bool: |
| 448 | + if sys.version_info >= (3, 12): |
| 449 | + return os.path.isjunction(path) |
| 450 | + |
| 451 | + if hasattr(os.stat_result, 'st_reparse_tag'): |
| 452 | + try: |
| 453 | + st = os.lstat(path) |
| 454 | + except (OSError, ValueError, AttributeError): |
| 455 | + return False |
| 456 | + return bool(st.st_reparse_tag == stat.IO_REPARSE_TAG_MOUNT_POINT) |
| 457 | + |
| 458 | + return False |
| 459 | + |
| 460 | + |
446 | 461 | class FSHookProxy:
|
447 | 462 | def __init__(
|
448 | 463 | self,
|
@@ -907,9 +922,16 @@ def collect(self) -> Iterator[Union[nodes.Item, nodes.Collector]]:
|
907 | 922 | if isinstance(matchparts[0], Path):
|
908 | 923 | is_match = node.path == matchparts[0]
|
909 | 924 | if sys.platform == "win32" and not is_match:
|
910 |
| - # In case the file paths do not match, fallback to samefile() to |
| 925 | + # In case the file paths do not match, |
911 | 926 | # account for short-paths on Windows (#11895).
|
912 |
| - is_match = os.path.samefile(node.path, matchparts[0]) |
| 927 | + same_file = os.path.samefile(node.path, matchparts[0]) |
| 928 | + # we don't want to find links, so we at least |
| 929 | + # exclude symlinks to regular directories |
| 930 | + is_match = ( |
| 931 | + same_file and |
| 932 | + os.path.islink(node.path) == os.path.islink(matchparts[0]) |
| 933 | + ) |
| 934 | + |
913 | 935 | # Name part e.g. `TestIt` in `/a/b/test_file.py::TestIt::test_it`.
|
914 | 936 | else:
|
915 | 937 | # TODO: Remove parametrized workaround once collection structure contains
|
|
0 commit comments