]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
GH-104996: Defer joining of `pathlib.PurePath()` arguments. (GH-104999)
authorBarney Gale <barney.gale@gmail.com>
Wed, 7 Jun 2023 22:27:06 +0000 (23:27 +0100)
committerGitHub <noreply@github.com>
Wed, 7 Jun 2023 22:27:06 +0000 (23:27 +0100)
Joining of arguments is moved to `_load_parts`, which is called when a
normalized path is needed.

Lib/pathlib.py
Misc/NEWS.d/next/Library/2023-05-26-21-24-06.gh-issue-104996.aaW78g.rst [new file with mode: 0644]

index 89c7b1ebdf727f6834d13580b6261f42cefa355f..d8c597f1027f301452566982996604f0a060068c 100644 (file)
@@ -195,10 +195,10 @@ def _select_unique(paths):
     yielded = set()
     try:
         for path in paths:
-            raw_path = path._raw_path
-            if raw_path not in yielded:
+            path_str = str(path)
+            if path_str not in yielded:
                 yield path
-                yielded.add(raw_path)
+                yielded.add(path_str)
     finally:
         yielded.clear()
 
@@ -247,9 +247,9 @@ class PurePath:
     """
 
     __slots__ = (
-        # The `_raw_path` slot stores an unnormalized string path. This is set
+        # The `_raw_paths` slot stores unnormalized string paths. This is set
         # in the `__init__()` method.
-        '_raw_path',
+        '_raw_paths',
 
         # The `_drv`, `_root` and `_tail_cached` slots store parsed and
         # normalized parts of the path. They are set when any of the `drive`,
@@ -306,10 +306,11 @@ class PurePath:
         paths = []
         for arg in args:
             if isinstance(arg, PurePath):
-                path = arg._raw_path
                 if arg._flavour is ntpath and self._flavour is posixpath:
                     # GH-103631: Convert separators for backwards compatibility.
-                    path = path.replace('\\', '/')
+                    paths.extend(path.replace('\\', '/') for path in arg._raw_paths)
+                else:
+                    paths.extend(arg._raw_paths)
             else:
                 try:
                     path = os.fspath(arg)
@@ -320,13 +321,8 @@ class PurePath:
                         "argument should be a str or an os.PathLike "
                         "object where __fspath__ returns a str, "
                         f"not {type(path).__name__!r}")
-            paths.append(path)
-        if len(paths) == 0:
-            self._raw_path = ''
-        elif len(paths) == 1:
-            self._raw_path = paths[0]
-        else:
-            self._raw_path = self._flavour.join(*paths)
+                paths.append(path)
+        self._raw_paths = paths
 
     def with_segments(self, *pathsegments):
         """Construct a new path object from any number of path-like objects.
@@ -356,7 +352,14 @@ class PurePath:
         return drv, root, parsed
 
     def _load_parts(self):
-        drv, root, tail = self._parse_path(self._raw_path)
+        paths = self._raw_paths
+        if len(paths) == 0:
+            path = ''
+        elif len(paths) == 1:
+            path = paths[0]
+        else:
+            path = self._flavour.join(*paths)
+        drv, root, tail = self._parse_path(path)
         self._drv = drv
         self._root = root
         self._tail_cached = tail
@@ -687,10 +690,17 @@ class PurePath:
     def is_absolute(self):
         """True if the path is absolute (has both a root and, if applicable,
         a drive)."""
-        # ntpath.isabs() is defective - see GH-44626 .
         if self._flavour is ntpath:
+            # ntpath.isabs() is defective - see GH-44626.
             return bool(self.drive and self.root)
-        return self._flavour.isabs(self._raw_path)
+        elif self._flavour is posixpath:
+            # Optimization: work with raw paths on POSIX.
+            for path in self._raw_paths:
+                if path.startswith('/'):
+                    return True
+            return False
+        else:
+            return self._flavour.isabs(str(self))
 
     def is_reserved(self):
         """Return True if the path contains one of the special names reserved
diff --git a/Misc/NEWS.d/next/Library/2023-05-26-21-24-06.gh-issue-104996.aaW78g.rst b/Misc/NEWS.d/next/Library/2023-05-26-21-24-06.gh-issue-104996.aaW78g.rst
new file mode 100644 (file)
index 0000000..8b81b68
--- /dev/null
@@ -0,0 +1,2 @@
+Improve performance of :class:`pathlib.PurePath` initialisation by
+deferring joining of paths when multiple arguments are given.