Skip to content

Commit 4ea9c5b

Browse files
[3.13] gh-85110: Preserve relative path in URL without netloc in urllib.parse.urlunsplit() (GH-123179) (#123187)
gh-85110: Preserve relative path in URL without netloc in urllib.parse.urlunsplit() (GH-123179) (cherry picked from commit 90c892e) Co-authored-by: Serhiy Storchaka <[email protected]>
1 parent 2b4c31d commit 4ea9c5b

File tree

3 files changed

+38
-9
lines changed

3 files changed

+38
-9
lines changed

Lib/test/test_urlparse.py

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,9 @@ def test_roundtrips(self):
207207
('scheme://///path/to/file',
208208
('scheme', '', '///path/to/file', '', '', ''),
209209
('scheme', '', '///path/to/file', '', '')),
210+
('file:tmp/junk.txt',
211+
('file', '', 'tmp/junk.txt', '', '', ''),
212+
('file', '', 'tmp/junk.txt', '', '')),
210213
('file:///tmp/junk.txt',
211214
('file', '', '/tmp/junk.txt', '', '', ''),
212215
('file', '', '/tmp/junk.txt', '', '')),
@@ -216,6 +219,18 @@ def test_roundtrips(self):
216219
('file://///tmp/junk.txt',
217220
('file', '', '///tmp/junk.txt', '', '', ''),
218221
('file', '', '///tmp/junk.txt', '', '')),
222+
('http:tmp/junk.txt',
223+
('http', '', 'tmp/junk.txt', '', '', ''),
224+
('http', '', 'tmp/junk.txt', '', '')),
225+
('http://example.com/tmp/junk.txt',
226+
('http', 'example.com', '/tmp/junk.txt', '', '', ''),
227+
('http', 'example.com', '/tmp/junk.txt', '', '')),
228+
('http:///example.com/tmp/junk.txt',
229+
('http', '', '/example.com/tmp/junk.txt', '', '', ''),
230+
('http', '', '/example.com/tmp/junk.txt', '', '')),
231+
('http:////example.com/tmp/junk.txt',
232+
('http', '', '//example.com/tmp/junk.txt', '', '', ''),
233+
('http', '', '//example.com/tmp/junk.txt', '', '')),
219234
('imap://mail.python.org/mbox1',
220235
('imap', 'mail.python.org', '/mbox1', '', '', ''),
221236
('imap', 'mail.python.org', '/mbox1', '', '')),
@@ -260,7 +275,8 @@ def _encode(t):
260275
('', '', 'schème:path/to/file', '', '')),
261276
]
262277
for url, parsed, split in str_cases + bytes_cases:
263-
self.checkRoundtrips(url, parsed, split)
278+
with self.subTest(url):
279+
self.checkRoundtrips(url, parsed, split)
264280

265281
def test_roundtrips_normalization(self):
266282
str_cases = [
@@ -292,7 +308,8 @@ def _encode(t):
292308
tuple(x.encode('ascii') for x in t[3]))
293309
bytes_cases = [_encode(x) for x in str_cases]
294310
for url, url2, parsed, split in str_cases + bytes_cases:
295-
self.checkRoundtrips(url, parsed, split, url2)
311+
with self.subTest(url):
312+
self.checkRoundtrips(url, parsed, split, url2)
296313

297314
def test_http_roundtrips(self):
298315
# urllib.parse.urlsplit treats 'http:' as an optimized special case,
@@ -333,11 +350,17 @@ def _encode(t):
333350
self.checkRoundtrips(url, parsed, split)
334351

335352
def checkJoin(self, base, relurl, expected):
336-
str_components = (base, relurl, expected)
337-
self.assertEqual(urllib.parse.urljoin(base, relurl), expected)
338-
bytes_components = baseb, relurlb, expectedb = [
339-
x.encode('ascii') for x in str_components]
340-
self.assertEqual(urllib.parse.urljoin(baseb, relurlb), expectedb)
353+
with self.subTest(base=base, relurl=relurl):
354+
self.assertEqual(urllib.parse.urljoin(base, relurl), expected)
355+
baseb = base.encode('ascii')
356+
relurlb = relurl.encode('ascii')
357+
expectedb = expected.encode('ascii')
358+
self.assertEqual(urllib.parse.urljoin(baseb, relurlb), expectedb)
359+
360+
relurl = urllib.parse.urlunsplit(urllib.parse.urlsplit(relurl))
361+
self.assertEqual(urllib.parse.urljoin(base, relurl), expected)
362+
relurlb = urllib.parse.urlunsplit(urllib.parse.urlsplit(relurlb))
363+
self.assertEqual(urllib.parse.urljoin(baseb, relurlb), expectedb)
341364

342365
def test_unparse_parse(self):
343366
str_cases = ['Python', './Python','x-newscheme://foo.com/stuff','x://y','x:/y','x:/','/',]

Lib/urllib/parse.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -525,9 +525,13 @@ def urlunsplit(components):
525525
empty query; the RFC states that these are equivalent)."""
526526
scheme, netloc, url, query, fragment, _coerce_result = (
527527
_coerce_args(*components))
528-
if netloc or (scheme and scheme in uses_netloc) or url[:2] == '//':
528+
if netloc:
529529
if url and url[:1] != '/': url = '/' + url
530-
url = '//' + (netloc or '') + url
530+
url = '//' + netloc + url
531+
elif url[:2] == '//':
532+
url = '//' + url
533+
elif scheme and scheme in uses_netloc and (not url or url[:1] == '/'):
534+
url = '//' + url
531535
if scheme:
532536
url = scheme + ':' + url
533537
if query:
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Preserve relative path in URL without netloc in
2+
:func:`urllib.parse.urlunsplit` and :func:`urllib.parse.urlunparse`.

0 commit comments

Comments
 (0)