mirror of
https://github.com/python/cpython.git
synced 2025-12-11 03:20:01 +00:00
GH-89769: pathlib.Path.glob(): do not follow symlinks when checking for precise match (GH-29655)
Co-authored-by: Barney Gale <barney.gale@gmail.com>
This commit is contained in:
parent
c7c3a60c88
commit
af886ffa06
4 changed files with 25 additions and 9 deletions
|
|
@ -819,9 +819,14 @@ call fails (for example because the path doesn't exist).
|
||||||
.. versionchanged:: 3.10
|
.. versionchanged:: 3.10
|
||||||
The *follow_symlinks* parameter was added.
|
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()
|
>>> Path('.').exists()
|
||||||
True
|
True
|
||||||
|
|
@ -832,10 +837,8 @@ call fails (for example because the path doesn't exist).
|
||||||
>>> Path('nonexistentfile').exists()
|
>>> Path('nonexistentfile').exists()
|
||||||
False
|
False
|
||||||
|
|
||||||
.. note::
|
.. versionchanged:: 3.12
|
||||||
If the path points to a symlink, :meth:`exists` returns whether the
|
The *follow_symlinks* parameter was added.
|
||||||
symlink *points to* an existing file or directory.
|
|
||||||
|
|
||||||
|
|
||||||
.. method:: Path.expanduser()
|
.. method:: Path.expanduser()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -135,7 +135,8 @@ class _PreciseSelector(_Selector):
|
||||||
def _select_from(self, parent_path, is_dir, exists, scandir):
|
def _select_from(self, parent_path, is_dir, exists, scandir):
|
||||||
try:
|
try:
|
||||||
path = parent_path._make_child_relpath(self.name)
|
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):
|
for p in self.successor._select_from(path, is_dir, exists, scandir):
|
||||||
yield p
|
yield p
|
||||||
except PermissionError:
|
except PermissionError:
|
||||||
|
|
@ -1122,12 +1123,15 @@ class Path(PurePath):
|
||||||
|
|
||||||
# Convenience functions for querying the stat results
|
# Convenience functions for querying the stat results
|
||||||
|
|
||||||
def exists(self):
|
def exists(self, *, follow_symlinks=True):
|
||||||
"""
|
"""
|
||||||
Whether this path exists.
|
Whether this path exists.
|
||||||
|
|
||||||
|
This method normally follows symlinks; to check whether a symlink exists,
|
||||||
|
add the argument follow_symlinks=False.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
self.stat()
|
self.stat(follow_symlinks=follow_symlinks)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
if not _ignore_error(e):
|
if not _ignore_error(e):
|
||||||
raise
|
raise
|
||||||
|
|
|
||||||
|
|
@ -1700,6 +1700,8 @@ class _BasePathTest(object):
|
||||||
self.assertIs(True, (p / 'linkB').exists())
|
self.assertIs(True, (p / 'linkB').exists())
|
||||||
self.assertIs(True, (p / 'linkB' / 'fileB').exists())
|
self.assertIs(True, (p / 'linkB' / 'fileB').exists())
|
||||||
self.assertIs(False, (p / 'linkA' / 'bah').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 / 'foo').exists())
|
||||||
self.assertIs(False, P('/xyzzy').exists())
|
self.assertIs(False, P('/xyzzy').exists())
|
||||||
self.assertIs(False, P(BASE + '\udfff').exists())
|
self.assertIs(False, P(BASE + '\udfff').exists())
|
||||||
|
|
@ -1806,6 +1808,8 @@ class _BasePathTest(object):
|
||||||
_check(p.glob("*/fileB"), ['dirB/fileB'])
|
_check(p.glob("*/fileB"), ['dirB/fileB'])
|
||||||
else:
|
else:
|
||||||
_check(p.glob("*/fileB"), ['dirB/fileB', 'linkB/fileB'])
|
_check(p.glob("*/fileB"), ['dirB/fileB', 'linkB/fileB'])
|
||||||
|
if os_helper.can_symlink():
|
||||||
|
_check(p.glob("brokenLink"), ['brokenLink'])
|
||||||
|
|
||||||
if not os_helper.can_symlink():
|
if not os_helper.can_symlink():
|
||||||
_check(p.glob("*/"), ["dirA", "dirB", "dirC", "dirE"])
|
_check(p.glob("*/"), ["dirA", "dirB", "dirC", "dirE"])
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
Loading…
Add table
Add a link
Reference in a new issue