https://github.com/python/cpython/commit/1092cfb20179ac7dd6a2c3c6f8a57ecc1732c777
commit: 1092cfb20179ac7dd6a2c3c6f8a57ecc1732c777
branch: main
author: Barney Gale <[email protected]>
committer: barneygale <[email protected]>
date: 2024-01-09T19:50:23Z
summary:

GH-113528: Deoptimise `pathlib._abc.PathBase.resolve()` (#113782)

Replace use of `_from_parsed_parts()` with `with_segments()` in
`resolve()`.

No effect on `Path.resolve()`, which uses `os.path.realpath()`.

files:
M Lib/pathlib/_abc.py

diff --git a/Lib/pathlib/_abc.py b/Lib/pathlib/_abc.py
index 0e442ae4809c36..caa84fc40559f7 100644
--- a/Lib/pathlib/_abc.py
+++ b/Lib/pathlib/_abc.py
@@ -406,6 +406,23 @@ def __rtruediv__(self, key):
         except TypeError:
             return NotImplemented
 
+    @property
+    def _stack(self):
+        """
+        Split the path into a 2-tuple (anchor, parts), where *anchor* is the
+        uppermost parent of the path (equivalent to path.parents[-1]), and
+        *parts* is a reversed list of parts following the anchor.
+        """
+        split = self.pathmod.split
+        path = str(self)
+        parent, name = split(path)
+        names = []
+        while path != parent:
+            names.append(name)
+            path = parent
+            parent, name = split(path)
+        return path, names
+
     @property
     def parent(self):
         """The logical parent of the path."""
@@ -911,16 +928,6 @@ def readlink(self):
         self._unsupported("readlink")
     readlink._supported = False
 
-    def _split_stack(self):
-        """
-        Split the path into a 2-tuple (anchor, parts), where *anchor* is the
-        uppermost parent of the path (equivalent to path.parents[-1]), and
-        *parts* is a reversed list of parts following the anchor.
-        """
-        if not self._tail:
-            return self, []
-        return self._from_parsed_parts(self.drive, self.root, []), 
self._tail[::-1]
-
     def resolve(self, strict=False):
         """
         Make the path absolute, resolving all symlinks on the way and also
@@ -928,11 +935,15 @@ def resolve(self, strict=False):
         """
         if self._resolving:
             return self
-        path, parts = self._split_stack()
+        path_root, parts = self._stack
+        path = self.with_segments(path_root)
         try:
             path = path.absolute()
         except UnsupportedOperation:
-            pass
+            path_tail = []
+        else:
+            path_root, path_tail = path._stack
+            path_tail.reverse()
 
         # If the user has *not* overridden the `readlink()` method, then 
symlinks are unsupported
         # and (in non-strict mode) we can improve performance by not calling 
`stat()`.
@@ -940,31 +951,37 @@ def resolve(self, strict=False):
         link_count = 0
         while parts:
             part = parts.pop()
+            if not part or part == '.':
+                continue
             if part == '..':
-                if not path._tail:
-                    if path.root:
+                if not path_tail:
+                    if path_root:
                         # Delete '..' segment immediately following root
                         continue
-                elif path._tail[-1] != '..':
+                elif path_tail[-1] != '..':
                     # Delete '..' segment and its predecessor
-                    path = path.parent
+                    path_tail.pop()
                     continue
-            next_path = path._make_child_relpath(part)
+            path_tail.append(part)
             if querying and part != '..':
-                next_path._resolving = True
+                path = self.with_segments(path_root + 
self.pathmod.sep.join(path_tail))
+                path._resolving = True
                 try:
-                    st = next_path.stat(follow_symlinks=False)
+                    st = path.stat(follow_symlinks=False)
                     if S_ISLNK(st.st_mode):
                         # Like Linux and macOS, raise OSError(errno.ELOOP) if 
too many symlinks are
                         # encountered during resolution.
                         link_count += 1
                         if link_count >= self._max_symlinks:
                             raise OSError(ELOOP, "Too many symbolic links in 
path", str(self))
-                        target, target_parts = 
next_path.readlink()._split_stack()
+                        target_root, target_parts = path.readlink()._stack
                         # If the symlink target is absolute (like 
'/etc/hosts'), set the current
                         # path to its uppermost parent (like '/').
-                        if target.root:
-                            path = target
+                        if target_root:
+                            path_root = target_root
+                            path_tail.clear()
+                        else:
+                            path_tail.pop()
                         # Add the symlink target's reversed tail parts (like 
['hosts', 'etc']) to
                         # the stack of unresolved path parts.
                         parts.extend(target_parts)
@@ -976,9 +993,7 @@ def resolve(self, strict=False):
                         raise
                     else:
                         querying = False
-                next_path._resolving = False
-            path = next_path
-        return path
+        return self.with_segments(path_root + self.pathmod.sep.join(path_tail))
 
     def symlink_to(self, target, target_is_directory=False):
         """

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]

Reply via email to