Skip to content

Commit 187949e

Browse files
authored
gh-94909: fix joining of absolute and relative Windows paths in pathlib (GH-95450)
Have pathlib use `os.path.join()` to join arguments to the `PurePath` initialiser, which fixes a minor bug when handling relative paths with drives. Previously: ```python >>> from pathlib import PureWindowsPath >>> a = 'C:/a/b' >>> b = 'C:x/y' >>> PureWindowsPath(a, b) PureWindowsPath('C:x/y') ``` Now: ```python >>> PureWindowsPath(a, b) PureWindowsPath('C:/a/b/x/y') ```
1 parent a965db3 commit 187949e

File tree

3 files changed

+14
-33
lines changed

3 files changed

+14
-33
lines changed

Diff for: Lib/pathlib.py

+8-33
Original file line numberDiff line numberDiff line change
@@ -61,41 +61,16 @@ def __init__(self):
6161
self.join = self.sep.join
6262

6363
def parse_parts(self, parts):
64-
parsed = []
64+
if not parts:
65+
return '', '', []
6566
sep = self.sep
6667
altsep = self.altsep
67-
drv = root = ''
68-
it = reversed(parts)
69-
for part in it:
70-
if not part:
71-
continue
72-
if altsep:
73-
part = part.replace(altsep, sep)
74-
drv, root, rel = self.splitroot(part)
75-
if sep in rel:
76-
for x in reversed(rel.split(sep)):
77-
if x and x != '.':
78-
parsed.append(sys.intern(x))
79-
else:
80-
if rel and rel != '.':
81-
parsed.append(sys.intern(rel))
82-
if drv or root:
83-
if not drv:
84-
# If no drive is present, try to find one in the previous
85-
# parts. This makes the result of parsing e.g.
86-
# ("C:", "/", "a") reasonably intuitive.
87-
for part in it:
88-
if not part:
89-
continue
90-
if altsep:
91-
part = part.replace(altsep, sep)
92-
drv = self.splitroot(part)[0]
93-
if drv:
94-
break
95-
break
96-
if drv or root:
97-
parsed.append(drv + root)
98-
parsed.reverse()
68+
path = self.pathmod.join(*parts)
69+
if altsep:
70+
path = path.replace(altsep, sep)
71+
drv, root, rel = self.splitroot(path)
72+
unfiltered_parsed = [drv + root] + rel.split(sep)
73+
parsed = [sys.intern(x) for x in unfiltered_parsed if x and x != '.']
9974
return drv, root, parsed
10075

10176
def join_parsed_parts(self, drv, root, parts, drv2, root2, parts2):

Diff for: Lib/test/test_pathlib.py

+4
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,10 @@ def test_parse_parts(self):
136136
check(['a', '/b', 'c'], ('', '\\', ['\\', 'b', 'c']))
137137
check(['Z:/a', '/b', 'c'], ('Z:', '\\', ['Z:\\', 'b', 'c']))
138138
check(['//?/Z:/a', '/b', 'c'], ('\\\\?\\Z:', '\\', ['\\\\?\\Z:\\', 'b', 'c']))
139+
# Joining with the same drive => the first path is appended to if
140+
# the second path is relative.
141+
check(['c:/a/b', 'c:x/y'], ('c:', '\\', ['c:\\', 'a', 'b', 'x', 'y']))
142+
check(['c:/a/b', 'c:/x/y'], ('c:', '\\', ['c:\\', 'x', 'y']))
139143

140144
def test_splitroot(self):
141145
f = self.flavour.splitroot
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix incorrect joining of relative Windows paths with drives in
2+
:class:`pathlib.PurePath` initializer.

0 commit comments

Comments
 (0)