Skip to content

Commit ae25f1c

Browse files
authored
[3.12] GH-104996: Defer joining of pathlib.PurePath() arguments. (GH-104999) (GH-105483)
Joining of arguments is moved to `_load_parts`, which is called when a normalized path is needed. (cherry picked from commit ffeaec7)
1 parent 4b2263e commit ae25f1c

File tree

2 files changed

+26
-14
lines changed

2 files changed

+26
-14
lines changed

Lib/pathlib.py

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -293,9 +293,9 @@ class PurePath(object):
293293
"""
294294

295295
__slots__ = (
296-
# The `_raw_path` slot stores an unnormalized string path. This is set
296+
# The `_raw_paths` slot stores unnormalized string paths. This is set
297297
# in the `__init__()` method.
298-
'_raw_path',
298+
'_raw_paths',
299299

300300
# The `_drv`, `_root` and `_tail_cached` slots store parsed and
301301
# normalized parts of the path. They are set when any of the `drive`,
@@ -352,10 +352,11 @@ def __init__(self, *args):
352352
paths = []
353353
for arg in args:
354354
if isinstance(arg, PurePath):
355-
path = arg._raw_path
356355
if arg._flavour is ntpath and self._flavour is posixpath:
357356
# GH-103631: Convert separators for backwards compatibility.
358-
path = path.replace('\\', '/')
357+
paths.extend(path.replace('\\', '/') for path in arg._raw_paths)
358+
else:
359+
paths.extend(arg._raw_paths)
359360
else:
360361
try:
361362
path = os.fspath(arg)
@@ -366,13 +367,8 @@ def __init__(self, *args):
366367
"argument should be a str or an os.PathLike "
367368
"object where __fspath__ returns a str, "
368369
f"not {type(path).__name__!r}")
369-
paths.append(path)
370-
if len(paths) == 0:
371-
self._raw_path = ''
372-
elif len(paths) == 1:
373-
self._raw_path = paths[0]
374-
else:
375-
self._raw_path = self._flavour.join(*paths)
370+
paths.append(path)
371+
self._raw_paths = paths
376372

377373
def with_segments(self, *pathsegments):
378374
"""Construct a new path object from any number of path-like objects.
@@ -402,7 +398,14 @@ def _parse_path(cls, path):
402398
return drv, root, parsed
403399

404400
def _load_parts(self):
405-
drv, root, tail = self._parse_path(self._raw_path)
401+
paths = self._raw_paths
402+
if len(paths) == 0:
403+
path = ''
404+
elif len(paths) == 1:
405+
path = paths[0]
406+
else:
407+
path = self._flavour.join(*paths)
408+
drv, root, tail = self._parse_path(path)
406409
self._drv = drv
407410
self._root = root
408411
self._tail_cached = tail
@@ -733,10 +736,17 @@ def parents(self):
733736
def is_absolute(self):
734737
"""True if the path is absolute (has both a root and, if applicable,
735738
a drive)."""
736-
# ntpath.isabs() is defective - see GH-44626 .
737739
if self._flavour is ntpath:
740+
# ntpath.isabs() is defective - see GH-44626.
738741
return bool(self.drive and self.root)
739-
return self._flavour.isabs(self._raw_path)
742+
elif self._flavour is posixpath:
743+
# Optimization: work with raw paths on POSIX.
744+
for path in self._raw_paths:
745+
if path.startswith('/'):
746+
return True
747+
return False
748+
else:
749+
return self._flavour.isabs(str(self))
740750

741751
def is_reserved(self):
742752
"""Return True if the path contains one of the special names reserved
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Improve performance of :class:`pathlib.PurePath` initialisation by
2+
deferring joining of paths when multiple arguments are given.

0 commit comments

Comments
 (0)