Skip to content

Commit a210f6d

Browse files
barneygaleebonnal
authored andcommitted
pythonGH-126766: url2pathname(): handle 'localhost' authority (python#127129)
Discard any 'localhost' authority from the beginning of a `file:` URI. As a result, file URIs like `//localhost/etc/hosts` are correctly decoded as `/etc/hosts`.
1 parent 520027b commit a210f6d

File tree

4 files changed

+15
-5
lines changed

4 files changed

+15
-5
lines changed

Lib/nturl2path.py

+7-4
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,17 @@ def url2pathname(url):
1515
# become
1616
# C:\foo\bar\spam.foo
1717
import string, urllib.parse
18+
if url[:3] == '///':
19+
# URL has an empty authority section, so the path begins on the third
20+
# character.
21+
url = url[2:]
22+
elif url[:12] == '//localhost/':
23+
# Skip past 'localhost' authority.
24+
url = url[11:]
1825
# Windows itself uses ":" even in URLs.
1926
url = url.replace(':', '|')
2027
if not '|' in url:
2128
# No drive specifier, just convert slashes
22-
if url[:3] == '///':
23-
# URL has an empty authority section, so the path begins on the
24-
# third character.
25-
url = url[2:]
2629
# make sure not to convert quoted slashes :-)
2730
return urllib.parse.unquote(url.replace('/', '\\'))
2831
comp = url.split('|')

Lib/test/test_urllib.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -1496,6 +1496,8 @@ def test_url2pathname_win(self):
14961496
# Localhost paths
14971497
self.assertEqual(fn('//localhost/C:/path/to/file'), 'C:\\path\\to\\file')
14981498
self.assertEqual(fn('//localhost/C|/path/to/file'), 'C:\\path\\to\\file')
1499+
self.assertEqual(fn('//localhost/path/to/file'), '\\path\\to\\file')
1500+
self.assertEqual(fn('//localhost//server/path/to/file'), '\\\\server\\path\\to\\file')
14991501
# Percent-encoded forward slashes are preserved for backwards compatibility
15001502
self.assertEqual(fn('C:/foo%2fbar'), 'C:\\foo/bar')
15011503
self.assertEqual(fn('//server/share/foo%2fbar'), '\\\\server\\share\\foo/bar')
@@ -1514,7 +1516,7 @@ def test_url2pathname_posix(self):
15141516
self.assertEqual(fn('//foo/bar'), '//foo/bar')
15151517
self.assertEqual(fn('///foo/bar'), '/foo/bar')
15161518
self.assertEqual(fn('////foo/bar'), '//foo/bar')
1517-
self.assertEqual(fn('//localhost/foo/bar'), '//localhost/foo/bar')
1519+
self.assertEqual(fn('//localhost/foo/bar'), '/foo/bar')
15181520

15191521
@unittest.skipUnless(os_helper.FS_NONASCII, 'need os_helper.FS_NONASCII')
15201522
def test_url2pathname_nonascii(self):

Lib/urllib/request.py

+3
Original file line numberDiff line numberDiff line change
@@ -1657,6 +1657,9 @@ def url2pathname(pathname):
16571657
# URL has an empty authority section, so the path begins on the
16581658
# third character.
16591659
pathname = pathname[2:]
1660+
elif pathname[:12] == '//localhost/':
1661+
# Skip past 'localhost' authority.
1662+
pathname = pathname[11:]
16601663
encoding = sys.getfilesystemencoding()
16611664
errors = sys.getfilesystemencodeerrors()
16621665
return unquote(pathname, encoding=encoding, errors=errors)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix issue where :func:`urllib.request.url2pathname` failed to discard any
2+
'localhost' authority present in the URL.

0 commit comments

Comments
 (0)