From 7a88c124f96f2c3acd9b8dff0bf57c56783872f5 Mon Sep 17 00:00:00 2001 From: Barney Gale Date: Fri, 29 Jul 2022 20:53:38 +0100 Subject: [PATCH 1/3] gh-94909: fix joining of absolute and relative Windows paths in pathlib --- Lib/pathlib.py | 20 ++----------------- Lib/test/test_pathlib.py | 4 ++++ ...2-07-29-20-58-37.gh-issue-94909.YjMusj.rst | 2 ++ 3 files changed, 8 insertions(+), 18 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2022-07-29-20-58-37.gh-issue-94909.YjMusj.rst diff --git a/Lib/pathlib.py b/Lib/pathlib.py index 2aee71742b23e4..8bd6167651ff22 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -65,10 +65,8 @@ def parse_parts(self, parts): sep = self.sep altsep = self.altsep drv = root = '' - it = reversed(parts) - for part in it: - if not part: - continue + if parts: + part = self.pathmod.join(*parts) if altsep: part = part.replace(altsep, sep) drv, root, rel = self.splitroot(part) @@ -79,20 +77,6 @@ def parse_parts(self, parts): else: if rel and rel != '.': parsed.append(sys.intern(rel)) - if drv or root: - if not drv: - # If no drive is present, try to find one in the previous - # parts. This makes the result of parsing e.g. - # ("C:", "/", "a") reasonably intuitive. - for part in it: - if not part: - continue - if altsep: - part = part.replace(altsep, sep) - drv = self.splitroot(part)[0] - if drv: - break - break if drv or root: parsed.append(drv + root) parsed.reverse() diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py index e14b0fca55360f..507d95208ff166 100644 --- a/Lib/test/test_pathlib.py +++ b/Lib/test/test_pathlib.py @@ -136,6 +136,10 @@ def test_parse_parts(self): check(['a', '/b', 'c'], ('', '\\', ['\\', 'b', 'c'])) check(['Z:/a', '/b', 'c'], ('Z:', '\\', ['Z:\\', 'b', 'c'])) check(['//?/Z:/a', '/b', 'c'], ('\\\\?\\Z:', '\\', ['\\\\?\\Z:\\', 'b', 'c'])) + # Joining with the same drive => the first path is appended to if + # the second path is relative. + check(['c:/a/b', 'c:x/y'], ('c:', '\\', ['c:\\', 'a', 'b', 'x', 'y'])) + check(['c:/a/b', 'c:/x/y'], ('c:', '\\', ['c:\\', 'x', 'y'])) def test_splitroot(self): f = self.flavour.splitroot diff --git a/Misc/NEWS.d/next/Library/2022-07-29-20-58-37.gh-issue-94909.YjMusj.rst b/Misc/NEWS.d/next/Library/2022-07-29-20-58-37.gh-issue-94909.YjMusj.rst new file mode 100644 index 00000000000000..b6d853888101c2 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-07-29-20-58-37.gh-issue-94909.YjMusj.rst @@ -0,0 +1,2 @@ +Fix incorrect joining of relative Windows paths with drives in +:class:`pathlib.PurePath` initializer. From 643d20ecdfdf65a02fea6836ee9cbbaef28d1bc2 Mon Sep 17 00:00:00 2001 From: Barney Gale Date: Fri, 29 Jul 2022 21:12:48 +0100 Subject: [PATCH 2/3] Simplify `parse_parts()` implementation. --- Lib/pathlib.py | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/Lib/pathlib.py b/Lib/pathlib.py index 8bd6167651ff22..5480332b84706f 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -61,25 +61,16 @@ def __init__(self): self.join = self.sep.join def parse_parts(self, parts): - parsed = [] + if not parts: + return '', '', [] sep = self.sep altsep = self.altsep - drv = root = '' - if parts: - part = self.pathmod.join(*parts) - if altsep: - part = part.replace(altsep, sep) - drv, root, rel = self.splitroot(part) - if sep in rel: - for x in reversed(rel.split(sep)): - if x and x != '.': - parsed.append(sys.intern(x)) - else: - if rel and rel != '.': - parsed.append(sys.intern(rel)) - if drv or root: - parsed.append(drv + root) - parsed.reverse() + path = self.pathmod.join(*parts) + if altsep: + path = path.replace(altsep, sep) + drv, root, rel = self.splitroot(path) + parsed = [drv + root] + rel.split(sep) + parsed = [sys.intern(x) for x in parsed if x and x != '.'] return drv, root, parsed def join_parsed_parts(self, drv, root, parts, drv2, root2, parts2): From 9838a0de7b9b54f5e087f2b6f79b326b8e7fef1e Mon Sep 17 00:00:00 2001 From: Barney Gale Date: Fri, 5 Aug 2022 23:40:17 +0100 Subject: [PATCH 3/3] Update Lib/pathlib.py Co-authored-by: Brett Cannon --- Lib/pathlib.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/pathlib.py b/Lib/pathlib.py index 5480332b84706f..15601dd0e15e4f 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -69,8 +69,8 @@ def parse_parts(self, parts): if altsep: path = path.replace(altsep, sep) drv, root, rel = self.splitroot(path) - parsed = [drv + root] + rel.split(sep) - parsed = [sys.intern(x) for x in parsed if x and x != '.'] + unfiltered_parsed = [drv + root] + rel.split(sep) + parsed = [sys.intern(x) for x in unfiltered_parsed if x and x != '.'] return drv, root, parsed def join_parsed_parts(self, drv, root, parts, drv2, root2, parts2):