Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit f5d2f22

Browse files
Issue #19456: ntpath.join() now joins relative paths correctly when a drive
is present.
2 parents 2393dca + c369c2c commit f5d2f22

3 files changed

Lines changed: 69 additions & 109 deletions

File tree

Lib/ntpath.py

Lines changed: 30 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -101,82 +101,36 @@ def isabs(s):
101101

102102

103103
# Join two (or more) paths.
104-
105-
def join(a, *p):
106-
"""Join two or more pathname components, inserting "\\" as needed.
107-
If any component is an absolute path, all previous path components
108-
will be discarded."""
109-
sep = _get_sep(a)
110-
seps = _get_bothseps(a)
111-
colon = _get_colon(a)
112-
path = a
113-
for b in p:
114-
b_wins = 0 # set to 1 iff b makes path irrelevant
115-
if not path:
116-
b_wins = 1
117-
118-
elif isabs(b):
119-
# This probably wipes out path so far. However, it's more
120-
# complicated if path begins with a drive letter. You get a+b
121-
# (minus redundant slashes) in these four cases:
122-
# 1. join('c:', '/a') == 'c:/a'
123-
# 2. join('//computer/share', '/a') == '//computer/share/a'
124-
# 3. join('c:/', '/a') == 'c:/a'
125-
# 4. join('//computer/share/', '/a') == '//computer/share/a'
126-
# But b wins in all of these cases:
127-
# 5. join('c:/a', '/b') == '/b'
128-
# 6. join('//computer/share/a', '/b') == '/b'
129-
# 7. join('c:', 'd:/') == 'd:/'
130-
# 8. join('c:', '//computer/share/') == '//computer/share/'
131-
# 9. join('//computer/share', 'd:/') == 'd:/'
132-
# 10. join('//computer/share', '//computer/share/') == '//computer/share/'
133-
# 11. join('c:/', 'd:/') == 'd:/'
134-
# 12. join('c:/', '//computer/share/') == '//computer/share/'
135-
# 13. join('//computer/share/', 'd:/') == 'd:/'
136-
# 14. join('//computer/share/', '//computer/share/') == '//computer/share/'
137-
b_prefix, b_rest = splitdrive(b)
138-
139-
# if b has a prefix, it always wins.
140-
if b_prefix:
141-
b_wins = 1
142-
else:
143-
# b doesn't have a prefix.
144-
# but isabs(b) returned true.
145-
# and therefore b_rest[0] must be a slash.
146-
# (but let's check that.)
147-
assert(b_rest and b_rest[0] in seps)
148-
149-
# so, b still wins if path has a rest that's more than a sep.
150-
# you get a+b if path_rest is empty or only has a sep.
151-
# (see cases 1-4 for times when b loses.)
152-
path_rest = splitdrive(path)[1]
153-
b_wins = path_rest and path_rest not in seps
154-
155-
if b_wins:
156-
path = b
157-
else:
158-
# Join, and ensure there's a separator.
159-
assert len(path) > 0
160-
if path[-1:] in seps:
161-
if b and b[:1] in seps:
162-
path += b[1:]
163-
else:
164-
path += b
165-
elif path[-1:] == colon:
166-
path += b
167-
elif b:
168-
if b[:1] in seps:
169-
path += b
170-
else:
171-
path += sep + b
172-
else:
173-
# path is not empty and does not end with a backslash,
174-
# but b is empty; since, e.g., split('a/') produces
175-
# ('a', ''), it's best if join() adds a backslash in
176-
# this case.
177-
path += sep
178-
179-
return path
104+
def join(path, *paths):
105+
sep = _get_sep(path)
106+
seps = _get_bothseps(path)
107+
colon = _get_colon(path)
108+
result_drive, result_path = splitdrive(path)
109+
for p in paths:
110+
p_drive, p_path = splitdrive(p)
111+
if p_path and p_path[0] in seps:
112+
# Second path is absolute
113+
if p_drive or not result_drive:
114+
result_drive = p_drive
115+
result_path = p_path
116+
continue
117+
elif p_drive and p_drive != result_drive:
118+
if p_drive.lower() != result_drive.lower():
119+
# Different drives => ignore the first path entirely
120+
result_drive = p_drive
121+
result_path = p_path
122+
continue
123+
# Same drive in different case
124+
result_drive = p_drive
125+
# Second path is relative to the first
126+
if result_path and result_path[-1] not in seps:
127+
result_path = result_path + sep
128+
result_path = result_path + p_path
129+
## add separator between UNC and non-absolute path
130+
if (result_path and result_path[0] not in seps and
131+
result_drive and result_drive[-1:] != colon):
132+
return result_drive + sep + result_path
133+
return result_drive + result_path
180134

181135

182136
# Split a path in a drive specification (a drive letter followed by a

Lib/test/test_ntpath.py

Lines changed: 36 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -126,53 +126,56 @@ def test_join(self):
126126
tester('ntpath.join("/a")', '/a')
127127
tester('ntpath.join("\\a")', '\\a')
128128
tester('ntpath.join("a:")', 'a:')
129-
tester('ntpath.join("a:", "b")', 'a:b')
130-
tester('ntpath.join("a:", "/b")', 'a:/b')
131129
tester('ntpath.join("a:", "\\b")', 'a:\\b')
132-
tester('ntpath.join("a", "/b")', '/b')
133130
tester('ntpath.join("a", "\\b")', '\\b')
134131
tester('ntpath.join("a", "b", "c")', 'a\\b\\c')
135132
tester('ntpath.join("a\\", "b", "c")', 'a\\b\\c')
136133
tester('ntpath.join("a", "b\\", "c")', 'a\\b\\c')
137134
tester('ntpath.join("a", "b", "\\c")', '\\c')
138135
tester('ntpath.join("d:\\", "\\pleep")', 'd:\\pleep')
139136
tester('ntpath.join("d:\\", "a", "b")', 'd:\\a\\b')
140-
tester("ntpath.join('c:', '/a')", 'c:/a')
141-
tester("ntpath.join('c:/', '/a')", 'c:/a')
142-
tester("ntpath.join('c:/a', '/b')", '/b')
143-
tester("ntpath.join('c:', 'd:/')", 'd:/')
144-
tester("ntpath.join('c:/', 'd:/')", 'd:/')
145-
tester("ntpath.join('c:/', 'd:/a/b')", 'd:/a/b')
146-
147-
tester("ntpath.join('')", '')
148-
tester("ntpath.join('', '', '', '', '')", '')
149-
tester("ntpath.join('a')", 'a')
137+
150138
tester("ntpath.join('', 'a')", 'a')
151139
tester("ntpath.join('', '', '', '', 'a')", 'a')
152140
tester("ntpath.join('a', '')", 'a\\')
153141
tester("ntpath.join('a', '', '', '', '')", 'a\\')
154142
tester("ntpath.join('a\\', '')", 'a\\')
155143
tester("ntpath.join('a\\', '', '', '', '')", 'a\\')
156-
157-
# from comment in ntpath.join
158-
tester("ntpath.join('c:', '/a')", 'c:/a')
159-
tester("ntpath.join('//computer/share', '/a')", '//computer/share/a')
160-
tester("ntpath.join('c:/', '/a')", 'c:/a')
161-
tester("ntpath.join('//computer/share/', '/a')", '//computer/share/a')
162-
tester("ntpath.join('c:/a', '/b')", '/b')
163-
tester("ntpath.join('//computer/share/a', '/b')", '/b')
164-
tester("ntpath.join('c:', 'd:/')", 'd:/')
165-
tester("ntpath.join('c:', '//computer/share/')", '//computer/share/')
166-
tester("ntpath.join('//computer/share', 'd:/')", 'd:/')
167-
tester("ntpath.join('//computer/share', '//computer/share/')", '//computer/share/')
168-
tester("ntpath.join('c:/', 'd:/')", 'd:/')
169-
tester("ntpath.join('c:/', '//computer/share/')", '//computer/share/')
170-
tester("ntpath.join('//computer/share/', 'd:/')", 'd:/')
171-
tester("ntpath.join('//computer/share/', '//computer/share/')", '//computer/share/')
172-
173-
tester("ntpath.join('c:', '//computer/share/')", '//computer/share/')
174-
tester("ntpath.join('c:/', '//computer/share/')", '//computer/share/')
175-
tester("ntpath.join('c:/', '//computer/share/a/b')", '//computer/share/a/b')
144+
tester("ntpath.join('a/', '')", 'a/')
145+
146+
tester("ntpath.join('a/b', 'x/y')", 'a/b\\x/y')
147+
tester("ntpath.join('/a/b', 'x/y')", '/a/b\\x/y')
148+
tester("ntpath.join('/a/b/', 'x/y')", '/a/b/x/y')
149+
tester("ntpath.join('c:', 'x/y')", 'c:x/y')
150+
tester("ntpath.join('c:a/b', 'x/y')", 'c:a/b\\x/y')
151+
tester("ntpath.join('c:a/b/', 'x/y')", 'c:a/b/x/y')
152+
tester("ntpath.join('c:/', 'x/y')", 'c:/x/y')
153+
tester("ntpath.join('c:/a/b', 'x/y')", 'c:/a/b\\x/y')
154+
tester("ntpath.join('c:/a/b/', 'x/y')", 'c:/a/b/x/y')
155+
tester("ntpath.join('//computer/share', 'x/y')", '//computer/share\\x/y')
156+
tester("ntpath.join('//computer/share/', 'x/y')", '//computer/share/x/y')
157+
tester("ntpath.join('//computer/share/a/b', 'x/y')", '//computer/share/a/b\\x/y')
158+
159+
tester("ntpath.join('a/b', '/x/y')", '/x/y')
160+
tester("ntpath.join('/a/b', '/x/y')", '/x/y')
161+
tester("ntpath.join('c:', '/x/y')", 'c:/x/y')
162+
tester("ntpath.join('c:a/b', '/x/y')", 'c:/x/y')
163+
tester("ntpath.join('c:/', '/x/y')", 'c:/x/y')
164+
tester("ntpath.join('c:/a/b', '/x/y')", 'c:/x/y')
165+
tester("ntpath.join('//computer/share', '/x/y')", '//computer/share/x/y')
166+
tester("ntpath.join('//computer/share/', '/x/y')", '//computer/share/x/y')
167+
tester("ntpath.join('//computer/share/a', '/x/y')", '//computer/share/x/y')
168+
169+
tester("ntpath.join('c:', 'C:x/y')", 'C:x/y')
170+
tester("ntpath.join('c:a/b', 'C:x/y')", 'C:a/b\\x/y')
171+
tester("ntpath.join('c:/', 'C:x/y')", 'C:/x/y')
172+
tester("ntpath.join('c:/a/b', 'C:x/y')", 'C:/a/b\\x/y')
173+
174+
for x in ('', 'a/b', '/a/b', 'c:', 'c:a/b', 'c:/', 'c:/a/b',
175+
'//computer/share', '//computer/share/', '//computer/share/a/b'):
176+
for y in ('d:', 'd:x/y', 'd:/', 'd:/x/y',
177+
'//machine/common', '//machine/common/', '//machine/common/x/y'):
178+
tester("ntpath.join(%r, %r)" % (x, y), y)
176179

177180
tester("ntpath.join('\\\\computer\\share\\', 'a', 'b')", '\\\\computer\\share\\a\\b')
178181
tester("ntpath.join('\\\\computer\\share', 'a', 'b')", '\\\\computer\\share\\a\\b')

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ Core and Builtins
4848
Library
4949
-------
5050

51+
- Issue #19456: ntpath.join() now joins relative paths correctly when a drive
52+
is present.
53+
5154
- Issue #19077: tempfile.TemporaryDirectory cleanup no longer fails when
5255
called during shutdown. Emitting resource warning in __del__ no longer fails.
5356
Original patch by Antoine Pitrou.

0 commit comments

Comments
 (0)