-
-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Fix handling recursive symlinks #7956
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -314,3 +314,4 @@ Xuecong Liao | |
| Yoav Caspi | ||
| Zac Hatfield-Dodds | ||
| Zoltán Máté | ||
| Zsolt Cserna | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| Fixed handling of recursive symlinks when collecting tests. |
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -9,6 +9,10 @@ | |||||||
| import uuid | ||||||||
| import warnings | ||||||||
| from enum import Enum | ||||||||
| from errno import EBADF | ||||||||
| from errno import ELOOP | ||||||||
| from errno import ENOENT | ||||||||
| from errno import ENOTDIR | ||||||||
| from functools import partial | ||||||||
| from os.path import expanduser | ||||||||
| from os.path import expandvars | ||||||||
|
|
@@ -37,6 +41,24 @@ | |||||||
|
|
||||||||
| _AnyPurePath = TypeVar("_AnyPurePath", bound=PurePath) | ||||||||
|
|
||||||||
| # The following function, variables and comments were | ||||||||
| # copied from cpython 3.9 Lib/pathlib.py file. | ||||||||
|
|
||||||||
| # EBADF - guard against macOS `stat` throwing EBADF | ||||||||
| _IGNORED_ERRORS = (ENOENT, ENOTDIR, EBADF, ELOOP) | ||||||||
|
|
||||||||
| _IGNORED_WINERRORS = ( | ||||||||
| 21, # ERROR_NOT_READY - drive exists but is not accessible | ||||||||
| 1921, # ERROR_CANT_RESOLVE_FILENAME - fix for broken symlink pointing to itself | ||||||||
| ) | ||||||||
|
|
||||||||
|
|
||||||||
| def _ignore_error(exception): | ||||||||
| return ( | ||||||||
| getattr(exception, "errno", None) in _IGNORED_ERRORS | ||||||||
| or getattr(exception, "winerror", None) in _IGNORED_WINERRORS | ||||||||
| ) | ||||||||
|
|
||||||||
|
|
||||||||
| def get_lock_path(path: _AnyPurePath) -> _AnyPurePath: | ||||||||
| return path.joinpath(".lock") | ||||||||
|
|
@@ -555,8 +577,23 @@ def visit( | |||||||
|
|
||||||||
| Entries at each directory level are sorted. | ||||||||
| """ | ||||||||
| entries = sorted(os.scandir(path), key=lambda entry: entry.name) | ||||||||
|
|
||||||||
| # Skip entries with symlink loops and other brokenness, so the caller doesn't | ||||||||
| # have to deal with it. | ||||||||
| entries = [] | ||||||||
| for entry in os.scandir(path): | ||||||||
| try: | ||||||||
| entry.is_file() | ||||||||
| except OSError as err: | ||||||||
| if _ignore_error(err): | ||||||||
| continue | ||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In such case, when the directory have n entries where 1 entry is broken, the caller will not able to see any entries but the error itself. In such case, the caller won't be able to ignore or somehow workaround the error.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree with both the first and second part. |
||||||||
| raise | ||||||||
| entries.append(entry) | ||||||||
|
|
||||||||
| entries.sort(key=lambda entry: entry.name) | ||||||||
|
|
||||||||
| yield from entries | ||||||||
|
|
||||||||
| for entry in entries: | ||||||||
| if entry.is_dir(follow_symlinks=False) and recurse(entry): | ||||||||
| yield from visit(entry.path, recurse) | ||||||||
|
|
||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would add a comment here, something like this: