[Python-checkins] GH-89769: `pathlib.Path.glob()`: do not follow symlinks when checking for precise match (GH-29655)
barneygale
webhook-mailer at python.org
Tue May 2 23:50:17 EDT 2023
https://github.com/python/cpython/commit/af886ffa0612124598b5e8174c3b1be0f60eab38
commit: af886ffa0612124598b5e8174c3b1be0f60eab38
branch: main
author: andrei kulakov <andrei.avk at gmail.com>
committer: barneygale <barney.gale at gmail.com>
date: 2023-05-03T04:50:10+01:00
summary:
GH-89769: `pathlib.Path.glob()`: do not follow symlinks when checking for precise match (GH-29655)
Co-authored-by: Barney Gale <barney.gale at gmail.com>
files:
A Misc/NEWS.d/next/Library/2021-11-19-23-37-18.bpo-45606.UW5XE1.rst
M Doc/library/pathlib.rst
M Lib/pathlib.py
M Lib/test/test_pathlib.py
diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst
index 8e91936680fa..4847ac24c775 100644
--- a/Doc/library/pathlib.rst
+++ b/Doc/library/pathlib.rst
@@ -819,9 +819,14 @@ call fails (for example because the path doesn't exist).
.. versionchanged:: 3.10
The *follow_symlinks* parameter was added.
-.. method:: Path.exists()
+.. method:: Path.exists(*, follow_symlinks=True)
- Whether the path points to an existing file or directory::
+ Return ``True`` if the path points to an existing file or directory.
+
+ This method normally follows symlinks; to check if a symlink exists, add
+ the argument ``follow_symlinks=False``.
+
+ ::
>>> Path('.').exists()
True
@@ -832,10 +837,8 @@ call fails (for example because the path doesn't exist).
>>> Path('nonexistentfile').exists()
False
- .. note::
- If the path points to a symlink, :meth:`exists` returns whether the
- symlink *points to* an existing file or directory.
-
+ .. versionchanged:: 3.12
+ The *follow_symlinks* parameter was added.
.. method:: Path.expanduser()
diff --git a/Lib/pathlib.py b/Lib/pathlib.py
index c69089f4e1bc..dee19d1f89ad 100644
--- a/Lib/pathlib.py
+++ b/Lib/pathlib.py
@@ -135,7 +135,8 @@ def __init__(self, name, child_parts, flavour):
def _select_from(self, parent_path, is_dir, exists, scandir):
try:
path = parent_path._make_child_relpath(self.name)
- if (is_dir if self.dironly else exists)(path):
+ follow = is_dir(path) if self.dironly else exists(path, follow_symlinks=False)
+ if follow:
for p in self.successor._select_from(path, is_dir, exists, scandir):
yield p
except PermissionError:
@@ -1122,12 +1123,15 @@ def hardlink_to(self, target):
# Convenience functions for querying the stat results
- def exists(self):
+ def exists(self, *, follow_symlinks=True):
"""
Whether this path exists.
+
+ This method normally follows symlinks; to check whether a symlink exists,
+ add the argument follow_symlinks=False.
"""
try:
- self.stat()
+ self.stat(follow_symlinks=follow_symlinks)
except OSError as e:
if not _ignore_error(e):
raise
diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py
index 9902b7242205..620d480e37e2 100644
--- a/Lib/test/test_pathlib.py
+++ b/Lib/test/test_pathlib.py
@@ -1700,6 +1700,8 @@ def test_exists(self):
self.assertIs(True, (p / 'linkB').exists())
self.assertIs(True, (p / 'linkB' / 'fileB').exists())
self.assertIs(False, (p / 'linkA' / 'bah').exists())
+ self.assertIs(False, (p / 'brokenLink').exists())
+ self.assertIs(True, (p / 'brokenLink').exists(follow_symlinks=False))
self.assertIs(False, (p / 'foo').exists())
self.assertIs(False, P('/xyzzy').exists())
self.assertIs(False, P(BASE + '\udfff').exists())
@@ -1806,6 +1808,8 @@ def _check(glob, expected):
_check(p.glob("*/fileB"), ['dirB/fileB'])
else:
_check(p.glob("*/fileB"), ['dirB/fileB', 'linkB/fileB'])
+ if os_helper.can_symlink():
+ _check(p.glob("brokenLink"), ['brokenLink'])
if not os_helper.can_symlink():
_check(p.glob("*/"), ["dirA", "dirB", "dirC", "dirE"])
diff --git a/Misc/NEWS.d/next/Library/2021-11-19-23-37-18.bpo-45606.UW5XE1.rst b/Misc/NEWS.d/next/Library/2021-11-19-23-37-18.bpo-45606.UW5XE1.rst
new file mode 100644
index 000000000000..531f47292200
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2021-11-19-23-37-18.bpo-45606.UW5XE1.rst
@@ -0,0 +1,5 @@
+Fixed the bug in :meth:`pathlib.Path.glob` -- previously a dangling symlink
+would not be found by this method when the pattern is an exact match, but
+would be found when the pattern contains a wildcard or the recursive
+wildcard (``**``). With this change, a dangling symlink will be found in
+both cases.
More information about the Python-checkins
mailing list