]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
GH-89769: `pathlib.Path.glob()`: do not follow symlinks when checking for precise...
authorandrei kulakov <andrei.avk@gmail.com>
Wed, 3 May 2023 03:50:10 +0000 (23:50 -0400)
committerGitHub <noreply@github.com>
Wed, 3 May 2023 03:50:10 +0000 (04:50 +0100)
Co-authored-by: Barney Gale <barney.gale@gmail.com>
Doc/library/pathlib.rst
Lib/pathlib.py
Lib/test/test_pathlib.py
Misc/NEWS.d/next/Library/2021-11-19-23-37-18.bpo-45606.UW5XE1.rst [new file with mode: 0644]

index 8e91936680fab895b302a3f8b3c56ecdb73c7a96..4847ac24c775134736b18a9d8cce7a20a97f43fb 100644 (file)
@@ -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()
 
index c69089f4e1bc5d19c6438a2ede99f6dcf4bf957b..dee19d1f89ad848d439a97a3eddca345b9cf5c48 100644 (file)
@@ -135,7 +135,8 @@ class _PreciseSelector(_Selector):
     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 @@ class Path(PurePath):
 
     # 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
index 9902b7242205f31ef4677bbb45b7dbf59b67dd53..620d480e37e2baf8908f8547e75188cadf6dc100 100644 (file)
@@ -1700,6 +1700,8 @@ class _BasePathTest(object):
             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 @@ class _BasePathTest(object):
             _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 (file)
index 0000000..531f472
--- /dev/null
@@ -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.