https://github.com/python/cpython/commit/37bd893a22b784d573b71df5417d855dc32dee62
commit: 37bd893a22b784d573b71df5417d855dc32dee62
branch: main
author: Barney Gale <[email protected]>
committer: barneygale <[email protected]>
date: 2024-01-06T21:17:51Z
summary:

GH-113528: Deoptimise `pathlib._abc.PurePathBase.parent` (#113530)

Replace use of `_from_parsed_parts()` with `with_segments()`, and move
assignments to `_drv`, `_root`, _tail_cached` and `_str` slots into
`PurePath`.

files:
M Lib/pathlib/__init__.py
M Lib/pathlib/_abc.py

diff --git a/Lib/pathlib/__init__.py b/Lib/pathlib/__init__.py
index 765a1425d0305a..d83f29283c7354 100644
--- a/Lib/pathlib/__init__.py
+++ b/Lib/pathlib/__init__.py
@@ -11,6 +11,7 @@
 import posixpath
 import sys
 import warnings
+from _collections_abc import Sequence
 
 try:
     import pwd
@@ -31,6 +32,35 @@
     ]
 
 
+class _PathParents(Sequence):
+    """This object provides sequence-like access to the logical ancestors
+    of a path.  Don't try to construct it yourself."""
+    __slots__ = ('_path', '_drv', '_root', '_tail')
+
+    def __init__(self, path):
+        self._path = path
+        self._drv = path.drive
+        self._root = path.root
+        self._tail = path._tail
+
+    def __len__(self):
+        return len(self._tail)
+
+    def __getitem__(self, idx):
+        if isinstance(idx, slice):
+            return tuple(self[i] for i in range(*idx.indices(len(self))))
+
+        if idx >= len(self) or idx < -len(self):
+            raise IndexError(idx)
+        if idx < 0:
+            idx += len(self)
+        return self._path._from_parsed_parts(self._drv, self._root,
+                                             self._tail[:-idx - 1])
+
+    def __repr__(self):
+        return "<{}.parents>".format(type(self._path).__name__)
+
+
 UnsupportedOperation = _abc.UnsupportedOperation
 
 
@@ -95,7 +125,6 @@ def __init__(self, *args):
                 paths.append(path)
         # Avoid calling super().__init__, as an optimisation
         self._raw_paths = paths
-        self._resolving = False
 
     def __reduce__(self):
         # Using the parts tuple helps share interned path parts
@@ -166,6 +195,23 @@ def __ge__(self, other):
             return NotImplemented
         return self._parts_normcase >= other._parts_normcase
 
+    @property
+    def parent(self):
+        """The logical parent of the path."""
+        drv = self.drive
+        root = self.root
+        tail = self._tail
+        if not tail:
+            return self
+        return self._from_parsed_parts(drv, root, tail[:-1])
+
+    @property
+    def parents(self):
+        """A sequence of this path's logical parents."""
+        # The value of this property should not be cached on the path object,
+        # as doing so would introduce a reference cycle.
+        return _PathParents(self)
+
     @property
     def name(self):
         """The final path component, if any."""
diff --git a/Lib/pathlib/_abc.py b/Lib/pathlib/_abc.py
index 1ce371308827fd..aca2bd5b85146d 100644
--- a/Lib/pathlib/_abc.py
+++ b/Lib/pathlib/_abc.py
@@ -2,7 +2,6 @@
 import ntpath
 import posixpath
 import sys
-from _collections_abc import Sequence
 from errno import ENOENT, ENOTDIR, EBADF, ELOOP, EINVAL
 from itertools import chain
 from stat import S_ISDIR, S_ISLNK, S_ISREG, S_ISSOCK, S_ISBLK, S_ISCHR, 
S_ISFIFO
@@ -138,35 +137,6 @@ class UnsupportedOperation(NotImplementedError):
     pass
 
 
-class _PathParents(Sequence):
-    """This object provides sequence-like access to the logical ancestors
-    of a path.  Don't try to construct it yourself."""
-    __slots__ = ('_path', '_drv', '_root', '_tail')
-
-    def __init__(self, path):
-        self._path = path
-        self._drv = path.drive
-        self._root = path.root
-        self._tail = path._tail
-
-    def __len__(self):
-        return len(self._tail)
-
-    def __getitem__(self, idx):
-        if isinstance(idx, slice):
-            return tuple(self[i] for i in range(*idx.indices(len(self))))
-
-        if idx >= len(self) or idx < -len(self):
-            raise IndexError(idx)
-        if idx < 0:
-            idx += len(self)
-        return self._path._from_parsed_parts(self._drv, self._root,
-                                             self._tail[:-idx - 1])
-
-    def __repr__(self):
-        return "<{}.parents>".format(type(self._path).__name__)
-
-
 class PurePathBase:
     """Base class for pure path objects.
 
@@ -442,21 +412,26 @@ def __rtruediv__(self, key):
     @property
     def parent(self):
         """The logical parent of the path."""
-        drv = self.drive
-        root = self.root
-        tail = self._tail
-        if not tail:
-            return self
-        path = self._from_parsed_parts(drv, root, tail[:-1])
-        path._resolving = self._resolving
-        return path
+        path = str(self)
+        parent = self.pathmod.dirname(path)
+        if path != parent:
+            parent = self.with_segments(parent)
+            parent._resolving = self._resolving
+            return parent
+        return self
 
     @property
     def parents(self):
         """A sequence of this path's logical parents."""
-        # The value of this property should not be cached on the path object,
-        # as doing so would introduce a reference cycle.
-        return _PathParents(self)
+        dirname = self.pathmod.dirname
+        path = str(self)
+        parent = dirname(path)
+        parents = []
+        while path != parent:
+            parents.append(self.with_segments(parent))
+            path = parent
+            parent = dirname(path)
+        return tuple(parents)
 
     def is_absolute(self):
         """True if the path is absolute (has both a root and, if applicable,

_______________________________________________
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