Skip to content

(Windows) passing a file path collects the whole suite, on a network drive where st_ino is 0 #14864

Description

@giampaolo

Hi,
when developing psutil on Windows, I do it by mapping X: to a sshfs share, so from Linux I can SSH into Windows and run tests there. Problem: pytest ignores the file I pass and collects everything:

$ python -m pytest tests/test_memleaks.py --collect-only -o addopts=""
platform win32 -- Python 3.14.6, pytest-9.1.1, pluggy-1.6.0
rootdir: X:\svn\psutil
collected 961 items

That file ( tests/test_memleaks.py ) has 130 tests, while 961 is the whole repo. Using absolute paths does the same.

I think the culprit is samefile_nofollow(), which Session.collect() falls back to on Windows when two paths don't compare equal. On this drive every file has st_ino == 0, so it says any two files are the same one:

$ python -c "
import os
a = os.stat('tests/test_memleaks.py')
b = os.stat('tests/test_process.py')
print(a.st_dev, a.st_ino)
print(b.st_dev, b.st_ino)
print(os.path.samestat(a, b))
"
3816903231 0
3816903231 0
True

So every node matches, nothing gets pruned, and I get the whole tree. The name parts still work, which fits: :: filtering happens after the path level.

$ python -m pytest "tests/test_memleaks.py::TestProcess" --collect-only -q
tests/test_windows.py::TestProcess::test_wait
175 tests collected

That's TestProcess from every file, not the one I asked for.

Maybe the fallback should just bail out when either st_ino is 0, since it can't tell anything apart at that point:

 def samefile_nofollow(p1: Path, p2: Path) -> bool:
-    return os.path.samestat(p1.lstat(), p2.lstat())
+    s1, s2 = p1.lstat(), p2.lstat()
+    if not s1.st_ino or not s2.st_ino:
+        return False
+    return os.path.samestat(s1, s2)

Returning False falls back to plain path comparison, which is what the other platforms do anyway. The short-path case this was added for (#11895) is on local NTFS, so it keeps working.

-m and -k work fine. Anything taking a path (--ignore, --deselect, plain args) doesn't.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions