]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
GH-117546: Fix symlink resolution in `os.path.realpath('loop/../link')` (#117568)
authorBarney Gale <barney.gale@gmail.com>
Wed, 10 Apr 2024 17:17:18 +0000 (18:17 +0100)
committerGitHub <noreply@github.com>
Wed, 10 Apr 2024 17:17:18 +0000 (18:17 +0100)
Continue resolving symlink targets after encountering a symlink loop, which
matches coreutils `realpath` behaviour.

Doc/library/os.path.rst
Lib/posixpath.py
Lib/test/test_posixpath.py
Misc/NEWS.d/next/Library/2024-04-05-13-38-53.gh-issue-117546.lWjhHE.rst [new file with mode: 0644]

index fcf4b6d68e018ce3a934f01b8b88b905ee00eb21..ebeb3bb50b8b1f7be6e488f96e9c1bd4089e5737 100644 (file)
@@ -409,9 +409,8 @@ the :mod:`glob` module.)
    style names such as ``C:\\PROGRA~1`` to ``C:\\Program Files``.
 
    If a path doesn't exist or a symlink loop is encountered, and *strict* is
-   ``True``, :exc:`OSError` is raised. If *strict* is ``False``, the path is
-   resolved as far as possible and any remainder is appended without checking
-   whether it exists.
+   ``True``, :exc:`OSError` is raised. If *strict* is ``False`` these errors
+   are ignored, and so the result might be missing or otherwise inaccessible.
 
    .. note::
       This function emulates the operating system's procedure for making a path
index 79e65587e66282a8cbf9b6d1f046587e485083e6..8fd49cdc35890854c8c7edc05d3f15e191726ed2 100644 (file)
@@ -431,11 +431,6 @@ symbolic links encountered in the path."""
     # the same links.
     seen = {}
 
-    # Whether we're calling lstat() and readlink() to resolve symlinks. If we
-    # encounter an OSError for a symlink loop in non-strict mode, this is
-    # switched off.
-    querying = True
-
     while rest:
         name = rest.pop()
         if name is None:
@@ -453,9 +448,6 @@ symbolic links encountered in the path."""
             newpath = path + name
         else:
             newpath = path + sep + name
-        if not querying:
-            path = newpath
-            continue
         try:
             st = os.lstat(newpath)
             if not stat.S_ISLNK(st.st_mode):
@@ -477,11 +469,8 @@ symbolic links encountered in the path."""
             if strict:
                 # Raise OSError(errno.ELOOP)
                 os.stat(newpath)
-            else:
-                # Return already resolved part + rest of the path unchanged.
-                path = newpath
-                querying = False
-                continue
+            path = newpath
+            continue
         seen[newpath] = None # not resolved symlink
         target = os.readlink(newpath)
         if target.startswith(sep):
index ff78410738022db78744afc238f414e22d5ef421..248fe2cc5d5ca8dd3f49e77101269c5b0d8777d6 100644 (file)
@@ -484,7 +484,7 @@ class PosixPathTest(unittest.TestCase):
             self.assertEqual(realpath(ABSTFN+"1/../x"), dirname(ABSTFN) + "/x")
             os.symlink(ABSTFN+"x", ABSTFN+"y")
             self.assertEqual(realpath(ABSTFN+"1/../" + basename(ABSTFN) + "y"),
-                             ABSTFN + "y")
+                             ABSTFN + "x")
             self.assertEqual(realpath(ABSTFN+"1/../" + basename(ABSTFN) + "1"),
                              ABSTFN + "1")
 
diff --git a/Misc/NEWS.d/next/Library/2024-04-05-13-38-53.gh-issue-117546.lWjhHE.rst b/Misc/NEWS.d/next/Library/2024-04-05-13-38-53.gh-issue-117546.lWjhHE.rst
new file mode 100644 (file)
index 0000000..9762991
--- /dev/null
@@ -0,0 +1,2 @@
+Fix issue where :func:`os.path.realpath` stopped resolving symlinks after
+encountering a symlink loop on POSIX.