Skip to content

test_glob: test_selflink() fails randomly on Linux #109959

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
vstinner opened this issue Sep 27, 2023 · 21 comments
Closed

test_glob: test_selflink() fails randomly on Linux #109959

vstinner opened this issue Sep 27, 2023 · 21 comments
Labels
stdlib Python modules in the Lib dir tests Tests in the Lib/test dir type-bug An unexpected behavior, bug, or error

Comments

@vstinner
Copy link
Member

vstinner commented Sep 27, 2023

AMD64 RHEL8 Refleaks 3.x:

0:09:26 load avg: 9.08 [406/463/1] test_glob failed (1 failure) -- running (7): (...)
beginning 6 repetitions
123456
.....test test_glob failed -- Traceback (most recent call last):
  File "/home/buildbot/buildarea/3.x.cstratak-RHEL8-x86_64.refleak/build/Lib/test/test_glob.py", line 396, in test_selflink
    self.assertIn(path, results)
AssertionError:
'dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/' not found in
{'dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/',
 'dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/',
 'dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/',
 'dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/',
 'dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/',
 'dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/',
 'dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/',
 'dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/',
 'dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/'}

The tested path dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link has one less link/ than the first path of the test: 'dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/'.

# Tested path
>>> len('dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/'.split('/'))
33

# First (shorted) expected path
>>> len('dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/'.split('/'))
34

build: https://buildbot.python.org/all/#/builders/259/builds/892

Linked PRs

@vstinner
Copy link
Member Author

vstinner commented Nov 8, 2023

I didn't see this failure recently, I close the issue.

@vstinner vstinner closed this as completed Nov 8, 2023
@encukou
Copy link
Member

encukou commented Jun 19, 2024

Seen recently in a PR CI run.

@ncoghlan
Copy link
Contributor

Dumping the expected value and the set content from the failed run.

The shortest value in the set has one more link/ entry than the expected value that the test is looking for, so it's definitely the same failure mode, but the absolute numbers are different (27 vs 28 here, rather than the 33 vs 34 that Victor saw):

AssertionError:
'dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/'
not found in
{
 'dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/',
 'dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/',
 'dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/',
 'dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/',
 'dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/',
 'dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/',
 'dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/',
 'dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/',
 'dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/',
 'dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/',
 'dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/',
 'dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/',
 'dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/',
 'dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/',
 'dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/'
}
>>> s = ... pasted set data ...
>>> expected = 'dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/'
>>> expected in s
False
>>> expected + 'link/' in s
True
>>> len(expected.split("/"))
27

@encukou
Copy link
Member

encukou commented Jun 19, 2024

I assume it gets up to a max path length, so the OS & CWD will affect that number.

@ncoghlan
Copy link
Contributor

That was my first thought too, but it doesn't seem to be the case, since all the paths in the set were accessible on disk (the set being checked is derived from the glob result). The largest value in the set will be dictated by the OS and CWD, but the missing value isn't the largest one, it's somewhere in the middle. In the latest failure, the remaining 15 iterations would have worked:

>>> for remaining in sorted(s):
...     print(remaining.count("/"), end=", ")
...
27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41,

I also confirmed both tracebacks are pointing at the same assertion:

To get the reported failures:

  • every entry up to the failing depth was in the set (otherwise the expected value would be shorter, as the loop starts at depth 0)
  • this path is shorter than the possible maximum path length, otherwise the set would be empty

@ncoghlan
Copy link
Contributor

ncoghlan commented Jun 19, 2024

I'm tempted to change the way this test works to build a full set of expected values and then do a set comparison rather than checking the entries one by one. Something like:

            # Check "**/" (or platform equivalent) has no duplicates or skipped entries
            results = glob.glob(os.path.join('**', ''), recursive=True)
            self.assertEqual(len(results), len(set(results)))
            results = set(results)
            max_depth = len(results)
            expected_results = {}
            for depth in range(max_depth):
                expected_results.add(os.path.join(*(['dir'] + ['link'] * depth + [''])))
            self.assertEqual(results, expected_results)

(and similarly for the other two search loops)

It won't fix the problem, but it should make it clearer how many entries are actually missing when the test does fail (since it will only show the diff, rather than all the remaining entries after the first missing entry).

@ncoghlan
Copy link
Contributor

It's hard to see any possible source of discrepancies between "directories scanned" and "paths reported" in a recursive glob, since _rlistdir tries to unconditionally recurse down into every reported name: https://github.com/python/cpython/blob/main/Lib/glob.py#L197

The check for whether that recursion operation is valid then appears to just be whether os.scandir throws OSError or not: https://github.com/python/cpython/blob/main/Lib/glob.py#L152

@barneygale
Copy link
Contributor

barneygale commented Jun 29, 2024

What a mysterious bug!

The pattern in question ("**/") triggers this bit of code:

cpython/Lib/glob.py

Lines 54 to 60 in 6b280a8

if not pathname or recursive and _isrecursive(pathname[:2]):
try:
s = next(it) # skip empty string
if s:
it = itertools.chain((s,), it)
except StopIteration:
pass

I suppose it's possible there's a bug in itertools.chain(), or the way iglob() uses it.

(edit: removing the topic-pathlib label as pathlib isn't relevant here)

@vstinner
Copy link
Member Author

Hypothesis tests on Ubuntu: https://github.com/python/cpython/actions/runs/12415820872/job/34663070210?pr=128097

FAIL: test_selflink (test.test_glob.SymlinkLoopGlobTests.test_selflink)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/runner/work/cpython/cpython-ro-srcdir/Lib/test/test_glob.py", line 543, in test_selflink
    self.assertIn(path, results)
    ~~~~~~~~~~~~~^^^^^^^^^^^^^^^
AssertionError: 'dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/file' not found in {'dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/file', 'dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/file', 'dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/file'}

@picnixz
Copy link
Member

picnixz commented Dec 25, 2024

I suppose it's possible there's a bug in itertools.chain(), or the way iglob() uses it.

I entirely removed the use of itertools.chain() and used a yield and yield from construction. The test also seems to be flaky (on some PRs, I just need to rerun it) so I'm not entirely sure that this the right solution (though it would eliminate the question of whether chain() has a bug or not).

@barneygale
Copy link
Contributor

barneygale commented Dec 28, 2024

New theory: _iterdir() can call os.close(fd) even if it didn't open the fd itself, and _rlistdir() calls _iterdir() repeatedly with the same file descriptor. _glob2() passes the file descriptor to _isdir().

If the fd was closed prematurely, then _isdir() will suppress an OSError and return false, which might explain why a directory would be missing from the results, but its children present

@barneygale
Copy link
Contributor

Nevermind - the test doesn't use pass dir_fd!

@vstinner vstinner changed the title test_glob: test_selflink() failed on AMD64 RHEL8 Refleaks 3.x test_glob: test_selflink() fails randomly on Linux Jan 6, 2025
srinivasreddy pushed a commit to srinivasreddy/cpython that referenced this issue Jan 8, 2025
@vstinner
Copy link
Member Author

vstinner commented Jan 8, 2025

Debug logs (length of the current working directory) when the test fails. Logs from Tests / Hypothesis tests on Ubuntu CI.

test_selflink (test.test_glob.SymlinkLoopGlobTests.test_selflink) ...

cwd: /home/runner/work/cpython/cpython-builddir/build/test_python_17625æ/@test_17625_tmpæ_dir (88 chars)
cwdb: b'/home/runner/work/cpython/cpython-builddir/build/test_python_17625\xc3\xa6/@test_17625_tmp\xc3\xa6_dir' (90 bytes)
FAIL

======================================================================
FAIL: test_selflink (test.test_glob.SymlinkLoopGlobTests.test_selflink)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/runner/work/cpython/cpython-ro-srcdir/Lib/test/test_glob.py", line 550, in test_selflink
    self.assertIn(path, results)
    ~~~~~~~~~~~~~^^^^^^^^^^^^^^^
AssertionError: 'dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/file' not found in {'dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/file', 'dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/file', 'dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/file', 'dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/file', 'dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/file', 'dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/file', 'dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/file', 'dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/file', 'dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/file', 'dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/file', 'dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/file', 'dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/file', 'dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/file', 'dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/file', 'dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/file', 'dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/file', 'dir/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/link/file'}

@vstinner
Copy link
Member Author

vstinner commented Jan 8, 2025

If I run the test in a loop on a busy system, it fails randomly. The problem is that the ELOOP error is not deterministic.

Sometimes, the issue occurs on glob._isdir(), sometimes on  glob._lexists(): they return False on ELOOP error. genericpath.isdir() and genericpath.lexists() return False on any OSError.

strace:

...
newfstatat(AT_FDCWD, "dir/link/.../link/link/file", {st_mode=S_IFREG|0755, st_size=0, ...}, AT_SYMLINK_NOFOLLOW) = 0
newfstatat(AT_FDCWD, "dir/link/.../link/link/link/file", 0x7ffe16a37fd0, AT_SYMLINK_NOFOLLOW) = -1 ELOOP
newfstatat(AT_FDCWD, "dir/link/.../link/link/link/link/file", {st_mode=S_IFREG|0755, st_size=0, ...}, AT_SYMLINK_NOFOLLOW) = 0
...

I would expect that once we reach the maximum number of symlinks, stat() fails with ELOOP, longer path would also fail with ELOOP, but it's not the case.


The Linux tool find doesn't visit link/ symlink even if it's a link to a directory (which was already visited). Maybe the problem is that glob.glob() does recurse into symlink directories. Maybe it should ignore loops.

@vstinner
Copy link
Member Author

vstinner commented Jan 8, 2025

Simpler reproducer:

import os
import sys

MAX_LINKS = 40
TEMPDIR = 'tempdir'


def BUG():
    print()
    print("BUUUUUUUUUUUUUUUG")
    print("BUUUUUUUUUUUUUUUG")
    print("BUUUUUUUUUUUUUUUG")
    sys.exit(1)


def check_lexists_bug():
    bug = False
    for depth in range(1, MAX_LINKS+2):
        path = 'link/' * depth

        err = None
        try:
            st = os.stat(path, follow_symlinks=True)
        except OSError as exc:
            err = exc
            lexists = False
        else:
            lexists = True

        if not lexists:
            lexists = f'{lexists} <============ {err!r}'
            if depth < MAX_LINKS:
                bug = True
        print(f'Depth {depth}: lexists? {lexists}')
        if not lexists:
            break
    if bug:
        BUG()
    print()


run = 1
while True:
    print(f"=== Run {run} ===")
    old_dir = os.getcwd()
    os.mkdir(TEMPDIR)
    try:
        os.chdir(TEMPDIR)
        open('file', 'wb').close()
        os.symlink(os.path.join('..', TEMPDIR), 'link')

        check_lexists_bug()
    finally:
        os.unlink('file')
        os.unlink('link')
        os.chdir(old_dir)
        os.rmdir(TEMPDIR)

    run += 1

Example of bug: lexists() fails at depth 33 but works at depths before and after. The bug occurs randomly on a busy system. On an idle system, the bug doesn't show up. For example, I run ./python -m test -j0 -r to stress my system to trigger the bug.

(...)

=== Run 14371 ===
Depth 1: lexists? True
Depth 2: lexists? True
Depth 3: lexists? True
Depth 4: lexists? True
Depth 5: lexists? True
Depth 6: lexists? True
Depth 7: lexists? True
Depth 8: lexists? True
Depth 9: lexists? True
Depth 10: lexists? True
Depth 11: lexists? True
Depth 12: lexists? True
Depth 13: lexists? True
Depth 14: lexists? True
Depth 15: lexists? True
Depth 16: lexists? True
Depth 17: lexists? True
Depth 18: lexists? True
Depth 19: lexists? True
Depth 20: lexists? True
Depth 21: lexists? True
Depth 22: lexists? True
Depth 23: lexists? True
Depth 24: lexists? True
Depth 25: lexists? True
Depth 26: lexists? True
Depth 27: lexists? True
Depth 28: lexists? True
Depth 29: lexists? True
Depth 30: lexists? True
Depth 31: lexists? True
Depth 32: lexists? True
Depth 33: lexists? False <============ OSError(40, 'Too many levels of symbolic links')
Depth 34: lexists? True
Depth 35: lexists? True
Depth 36: lexists? True
Depth 37: lexists? True
Depth 38: lexists? True
Depth 39: lexists? True
Depth 40: lexists? True
Depth 41: lexists? False <============ OSError(40, 'Too many levels of symbolic links')

BUUUUUUUUUUUUUUUG
BUUUUUUUUUUUUUUUG
BUUUUUUUUUUUUUUUG

Another example where lexists() fails at multiple depths:

(...)

=== Run 2191 ===
Depth 1: lexists? True
Depth 2: lexists? True
Depth 3: lexists? True
Depth 4: lexists? True
Depth 5: lexists? True
Depth 6: lexists? True
Depth 7: lexists? True
Depth 8: lexists? True
Depth 9: lexists? True
Depth 10: lexists? True
Depth 11: lexists? True
Depth 12: lexists? True
Depth 13: lexists? True
Depth 14: lexists? True
Depth 15: lexists? True
Depth 16: lexists? True
Depth 17: lexists? True
Depth 18: lexists? True
Depth 19: lexists? True
Depth 20: lexists? True
Depth 21: lexists? True
Depth 22: lexists? True
Depth 23: lexists? True
Depth 24: lexists? False <============ OSError(40, 'Too many levels of symbolic links')
Depth 25: lexists? True
Depth 26: lexists? True
Depth 27: lexists? True
Depth 28: lexists? False <============ OSError(40, 'Too many levels of symbolic links')
Depth 29: lexists? False <============ OSError(40, 'Too many levels of symbolic links')
Depth 30: lexists? True
Depth 31: lexists? True
Depth 32: lexists? True
Depth 33: lexists? True
Depth 34: lexists? True
Depth 35: lexists? True
Depth 36: lexists? True
Depth 37: lexists? True
Depth 38: lexists? True
Depth 39: lexists? True
Depth 40: lexists? True
Depth 41: lexists? False <============ OSError(40, 'Too many levels of symbolic links')

BUUUUUUUUUUUUUUUG
BUUUUUUUUUUUUUUUG
BUUUUUUUUUUUUUUUG

@barneygale
Copy link
Contributor

Does that imply that it's a glibc or kernel bug?

@vstinner
Copy link
Member Author

vstinner commented Jan 8, 2025

I can see the behavior at the syscall level, so it's the behavior of the Linux kernel. I don't know if it's a bug. I don't know if Linux has guarantee that all path operations support up to 40 levels of links.

@barneygale
Copy link
Contributor

My man path_resolution says:

In order to protect the kernel against stack overflow, and also to protect against denial of service, there are limits on the maximum recursion depth, and on the maximum number of symbolic links followed. An ELOOP error is returned when the maximum is exceeded ("Too many levels of symbolic links").

As currently implemented on Linux, the maximum number of symbolic links that will be followed while resolving a pathname is 40. In kernels before 2.6.18, the limit on the recursion depth was 5. Starting with Linux 2.6.18, this limit was raised to 8. In Linux 4.2, the kernel's pathname-resolution code was reworked to eliminate the use of recursion, so that the only limit that remains is the maximum of 40 resolutions for the entire pathname.

I'm on 5.15.0-126-generic FWIW.

Could SELinux somehow cause this?

@vstinner
Copy link
Member Author

vstinner commented Jan 8, 2025

Could SELinux somehow cause this?

I saw the issue on the Ubuntu Hypothesis CI, and Ubuntu doesn't use SELinux (but AppArmor).

vstinner added a commit to vstinner/cpython that referenced this issue Jan 14, 2025
miss-islington pushed a commit to miss-islington/cpython that referenced this issue Jan 14, 2025
…128812)

(cherry picked from commit 1153e66)

Co-authored-by: Victor Stinner <vstinner@python.org>
vstinner added a commit that referenced this issue Jan 14, 2025
… (#128821)

gh-109959: Skip test_glob.test_selflink() flaky test (GH-128812)
(cherry picked from commit 1153e66)

Co-authored-by: Victor Stinner <vstinner@python.org>
miss-islington pushed a commit to miss-islington/cpython that referenced this issue Jan 14, 2025
…128812)

(cherry picked from commit 1153e66)

Co-authored-by: Victor Stinner <vstinner@python.org>
@vstinner
Copy link
Member Author

The flaky test is now skipped. Since the root issue comes from the Linux kernel, maybe we should just remove the test. Or the glob module should be modified to detect symlink loops and don't visit broken links. I close the issue.

vstinner added a commit that referenced this issue Jan 14, 2025
… (#128834)

gh-109959: Skip test_glob.test_selflink() flaky test (GH-128812)
(cherry picked from commit 1153e66)

Co-authored-by: Victor Stinner <vstinner@python.org>
@barneygale
Copy link
Contributor

FWIW this might be fixed by #116392, because it no longer stat()s paths discovered by os.scandir()

vstinner added a commit to vstinner/cpython that referenced this issue Feb 25, 2025
The test is not reliable, it fails randomly on Linux:
python#109959 (comment)
vstinner added a commit that referenced this issue Feb 27, 2025
The test is not reliable, it fails randomly on Linux:
#109959 (comment)
seehwan pushed a commit to seehwan/cpython that referenced this issue Apr 16, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
stdlib Python modules in the Lib dir tests Tests in the Lib/test dir type-bug An unexpected behavior, bug, or error
Projects
None yet
Development

No branches or pull requests

5 participants