diff --git a/Lib/pathlib.py b/Lib/pathlib.py index 5c1c71ecec2805..c99b854f5e2087 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -358,15 +358,43 @@ def with_segments(self, *pathsegments): return type(self)(*pathsegments) @classmethod - def _parse_path(cls, path): - if not path: + def _parse_paths(cls, paths): + if not paths: return '', '', [] sep = cls.pathmod.sep altsep = cls.pathmod.altsep - if altsep: - path = path.replace(altsep, sep) - drv, root, rel = cls.pathmod.splitroot(path) - if not root and drv.startswith(sep) and not drv.endswith(sep): + splitroot = cls.pathmod.splitroot + + # Join paths like ntpath.join(), but without concatenating strings. + drv, root, tail = '', '', [] + for path in paths: + if altsep: + path = path.replace(altsep, sep) + p_drv, p_root, p_rel = splitroot(path) + p_tail = p_rel.split(sep) + if p_root: + if p_drv: + drv = p_drv + root = p_root + tail = p_tail + elif p_drv and p_drv != drv: + if p_drv.lower() != drv.lower(): + drv = p_drv + root = p_root + tail = p_tail + else: + drv = p_drv + tail.extend(p_tail) + else: + tail.extend(p_tail) + + # Normalize UNC path. + if drv and not root and drv[-1] not in ':\\': + if any(tail): + # Join onto partial UNC drive - must join and re-split. + parts = [drv] + [x for x in tail if x] + drv, root, rel = splitroot(sep.join(parts)) + tail = rel.split(sep) drv_parts = drv.split(sep) if len(drv_parts) == 4 and drv_parts[2] not in '?.': # e.g. //server/share @@ -374,18 +402,11 @@ def _parse_path(cls, path): elif len(drv_parts) == 6: # e.g. //?/unc/server/share root = sep - parsed = [sys.intern(str(x)) for x in rel.split(sep) if x and x != '.'] + parsed = [sys.intern(str(x)) for x in tail if x and x != '.'] return drv, root, parsed def _load_parts(self): - paths = self._raw_paths - if len(paths) == 0: - path = '' - elif len(paths) == 1: - path = paths[0] - else: - path = self.pathmod.join(*paths) - drv, root, tail = self._parse_path(path) + drv, root, tail = self._parse_paths(self._raw_paths) self._drv = drv self._root = root self._tail_cached = tail @@ -1661,7 +1682,7 @@ def expanduser(self): homedir = os.path.expanduser(self._tail[0]) if homedir[:1] == "~": raise RuntimeError("Could not determine home directory.") - drv, root, tail = self._parse_path(homedir) + drv, root, tail = self._parse_paths((homedir,)) return self._from_parsed_parts(drv, root, tail + self._tail[1:]) return self diff --git a/Misc/NEWS.d/next/Library/2023-06-08-01-14-07.gh-issue-104996.77mt8h.rst b/Misc/NEWS.d/next/Library/2023-06-08-01-14-07.gh-issue-104996.77mt8h.rst new file mode 100644 index 00000000000000..7f4f79210b839e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-06-08-01-14-07.gh-issue-104996.77mt8h.rst @@ -0,0 +1,2 @@ +Improve performance of path joining in :class:`pathlib.PurePath` by +implementing a variant of :func:`os.path.join`.