Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 2fa5d70

Browse filesBrowse files
[3.10] gh-67693: Fix urlunparse() and urlunsplit() for URIs with path starting with multiple slashes and no authority (GH-113563) (#119026)
(cherry picked from commit e237b25)
1 parent 06f28dc commit 2fa5d70
Copy full SHA for 2fa5d70

File tree

3 files changed

+70
-4
lines changed
Filter options

3 files changed

+70
-4
lines changed

‎Lib/test/test_urlparse.py

Copy file name to clipboardExpand all lines: Lib/test/test_urlparse.py
+67-3Lines changed: 67 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,15 +70,17 @@
7070

7171
class UrlParseTestCase(unittest.TestCase):
7272

73-
def checkRoundtrips(self, url, parsed, split):
73+
def checkRoundtrips(self, url, parsed, split, url2=None):
74+
if url2 is None:
75+
url2 = url
7476
result = urllib.parse.urlparse(url)
7577
self.assertEqual(result, parsed)
7678
t = (result.scheme, result.netloc, result.path,
7779
result.params, result.query, result.fragment)
7880
self.assertEqual(t, parsed)
7981
# put it back together and it should be the same
8082
result2 = urllib.parse.urlunparse(result)
81-
self.assertEqual(result2, url)
83+
self.assertEqual(result2, url2)
8284
self.assertEqual(result2, result.geturl())
8385

8486
# the result of geturl() is a fixpoint; we can always parse it
@@ -104,7 +106,7 @@ def checkRoundtrips(self, url, parsed, split):
104106
result.query, result.fragment)
105107
self.assertEqual(t, split)
106108
result2 = urllib.parse.urlunsplit(result)
107-
self.assertEqual(result2, url)
109+
self.assertEqual(result2, url2)
108110
self.assertEqual(result2, result.geturl())
109111

110112
# check the fixpoint property of re-parsing the result of geturl()
@@ -142,9 +144,39 @@ def test_qs(self):
142144

143145
def test_roundtrips(self):
144146
str_cases = [
147+
('path/to/file',
148+
('', '', 'path/to/file', '', '', ''),
149+
('', '', 'path/to/file', '', '')),
150+
('/path/to/file',
151+
('', '', '/path/to/file', '', '', ''),
152+
('', '', '/path/to/file', '', '')),
153+
('//path/to/file',
154+
('', 'path', '/to/file', '', '', ''),
155+
('', 'path', '/to/file', '', '')),
156+
('////path/to/file',
157+
('', '', '//path/to/file', '', '', ''),
158+
('', '', '//path/to/file', '', '')),
159+
('scheme:path/to/file',
160+
('scheme', '', 'path/to/file', '', '', ''),
161+
('scheme', '', 'path/to/file', '', '')),
162+
('scheme:/path/to/file',
163+
('scheme', '', '/path/to/file', '', '', ''),
164+
('scheme', '', '/path/to/file', '', '')),
165+
('scheme://path/to/file',
166+
('scheme', 'path', '/to/file', '', '', ''),
167+
('scheme', 'path', '/to/file', '', '')),
168+
('scheme:////path/to/file',
169+
('scheme', '', '//path/to/file', '', '', ''),
170+
('scheme', '', '//path/to/file', '', '')),
145171
('file:///tmp/junk.txt',
146172
('file', '', '/tmp/junk.txt', '', '', ''),
147173
('file', '', '/tmp/junk.txt', '', '')),
174+
('file:////tmp/junk.txt',
175+
('file', '', '//tmp/junk.txt', '', '', ''),
176+
('file', '', '//tmp/junk.txt', '', '')),
177+
('file://///tmp/junk.txt',
178+
('file', '', '///tmp/junk.txt', '', '', ''),
179+
('file', '', '///tmp/junk.txt', '', '')),
148180
('imap://mail.python.org/mbox1',
149181
('imap', 'mail.python.org', '/mbox1', '', '', ''),
150182
('imap', 'mail.python.org', '/mbox1', '', '')),
@@ -175,6 +207,38 @@ def _encode(t):
175207
for url, parsed, split in str_cases + bytes_cases:
176208
self.checkRoundtrips(url, parsed, split)
177209

210+
def test_roundtrips_normalization(self):
211+
str_cases = [
212+
('///path/to/file',
213+
'/path/to/file',
214+
('', '', '/path/to/file', '', '', ''),
215+
('', '', '/path/to/file', '', '')),
216+
('scheme:///path/to/file',
217+
'scheme:/path/to/file',
218+
('scheme', '', '/path/to/file', '', '', ''),
219+
('scheme', '', '/path/to/file', '', '')),
220+
('file:/tmp/junk.txt',
221+
'file:///tmp/junk.txt',
222+
('file', '', '/tmp/junk.txt', '', '', ''),
223+
('file', '', '/tmp/junk.txt', '', '')),
224+
('http:/tmp/junk.txt',
225+
'http:///tmp/junk.txt',
226+
('http', '', '/tmp/junk.txt', '', '', ''),
227+
('http', '', '/tmp/junk.txt', '', '')),
228+
('https:/tmp/junk.txt',
229+
'https:///tmp/junk.txt',
230+
('https', '', '/tmp/junk.txt', '', '', ''),
231+
('https', '', '/tmp/junk.txt', '', '')),
232+
]
233+
def _encode(t):
234+
return (t[0].encode('ascii'),
235+
t[1].encode('ascii'),
236+
tuple(x.encode('ascii') for x in t[2]),
237+
tuple(x.encode('ascii') for x in t[3]))
238+
bytes_cases = [_encode(x) for x in str_cases]
239+
for url, url2, parsed, split in str_cases + bytes_cases:
240+
self.checkRoundtrips(url, parsed, split, url2)
241+
178242
def test_http_roundtrips(self):
179243
# urllib.parse.urlsplit treats 'http:' as an optimized special case,
180244
# so we test both 'http:' and 'https:' in all the following.

‎Lib/urllib/parse.py

Copy file name to clipboardExpand all lines: Lib/urllib/parse.py
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -521,7 +521,7 @@ def urlunsplit(components):
521521
empty query; the RFC states that these are equivalent)."""
522522
scheme, netloc, url, query, fragment, _coerce_result = (
523523
_coerce_args(*components))
524-
if netloc or (scheme and scheme in uses_netloc and url[:2] != '//'):
524+
if netloc or (scheme and scheme in uses_netloc) or url[:2] == '//':
525525
if url and url[:1] != '/': url = '/' + url
526526
url = '//' + (netloc or '') + url
527527
if scheme:
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix :func:`urllib.parse.urlunparse` and :func:`urllib.parse.urlunsplit` for URIs with path starting with multiple slashes and no authority.
2+
Based on patch by Ashwin Ramaswami.

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.