Skip to content

Commit 04f38bb

Browse files
[3.12] GH-126766: url2pathname(): handle empty authority section. (GH-126767) (#126837)
GH-126766: `url2pathname()`: handle empty authority section. (GH-126767) Discard two leading slashes from the beginning of a `file:` URI if they introduce an empty authority section. As a result, file URIs like `///etc/hosts` are correctly parsed as `/etc/hosts`. (cherry picked from commit cae9d9d) Co-authored-by: Barney Gale <[email protected]>
1 parent 306db14 commit 04f38bb

File tree

4 files changed

+14
-9
lines changed

4 files changed

+14
-9
lines changed

Lib/nturl2path.py

+3-4
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,9 @@ def url2pathname(url):
1919
url = url.replace(':', '|')
2020
if not '|' in url:
2121
# No drive specifier, just convert slashes
22-
if url[:4] == '////':
23-
# path is something like ////host/path/on/remote/host
24-
# convert this to \\host\path\on\remote\host
25-
# (notice halving of slashes at the start of the path)
22+
if url[:3] == '///':
23+
# URL has an empty authority section, so the path begins on the
24+
# third character.
2625
url = url[2:]
2726
# make sure not to convert quoted slashes :-)
2827
return urllib.parse.unquote(url.replace('/', '\\'))

Lib/test/test_urllib.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -1558,7 +1558,7 @@ def test_pathname2url_win(self):
15581558
self.assertEqual(fn('//?/unc/server/share/dir'), '//server/share/dir')
15591559
# Round-tripping
15601560
urls = ['///C:',
1561-
'///folder/test/',
1561+
'/folder/test/',
15621562
'///C:/foo/bar/spam.foo']
15631563
for url in urls:
15641564
self.assertEqual(fn(urllib.request.url2pathname(url)), url)
@@ -1582,7 +1582,7 @@ def test_url2pathname_win(self):
15821582
self.assertEqual(fn('/C|//'), 'C:\\\\')
15831583
self.assertEqual(fn('///C|/path'), 'C:\\path')
15841584
# No DOS drive
1585-
self.assertEqual(fn("///C/test/"), '\\\\\\C\\test\\')
1585+
self.assertEqual(fn("///C/test/"), '\\C\\test\\')
15861586
self.assertEqual(fn("////C/test/"), '\\\\C\\test\\')
15871587
# DOS drive paths
15881588
self.assertEqual(fn('C:/path/to/file'), 'C:\\path\\to\\file')
@@ -1606,7 +1606,7 @@ def test_url2pathname_win(self):
16061606
self.assertEqual(fn('//server/share/foo%2fbar'), '\\\\server\\share\\foo/bar')
16071607
# Round-tripping
16081608
paths = ['C:',
1609-
r'\\\C\test\\',
1609+
r'\C\test\\',
16101610
r'C:\foo\bar\spam.foo']
16111611
for path in paths:
16121612
self.assertEqual(fn(urllib.request.pathname2url(path)), path)
@@ -1617,8 +1617,8 @@ def test_url2pathname_posix(self):
16171617
fn = urllib.request.url2pathname
16181618
self.assertEqual(fn('/foo/bar'), '/foo/bar')
16191619
self.assertEqual(fn('//foo/bar'), '//foo/bar')
1620-
self.assertEqual(fn('///foo/bar'), '///foo/bar')
1621-
self.assertEqual(fn('////foo/bar'), '////foo/bar')
1620+
self.assertEqual(fn('///foo/bar'), '/foo/bar')
1621+
self.assertEqual(fn('////foo/bar'), '//foo/bar')
16221622
self.assertEqual(fn('//localhost/foo/bar'), '//localhost/foo/bar')
16231623

16241624
class Utility_Tests(unittest.TestCase):

Lib/urllib/request.py

+4
Original file line numberDiff line numberDiff line change
@@ -1681,6 +1681,10 @@ def data_open(self, req):
16811681
def url2pathname(pathname):
16821682
"""OS-specific conversion from a relative URL of the 'file' scheme
16831683
to a file system path; not recommended for general use."""
1684+
if pathname[:3] == '///':
1685+
# URL has an empty authority section, so the path begins on the
1686+
# third character.
1687+
pathname = pathname[2:]
16841688
return unquote(pathname)
16851689

16861690
def pathname2url(pathname):
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix issue where :func:`urllib.request.url2pathname` failed to discard two
2+
leading slashes introducing an empty authority section.

0 commit comments

Comments
 (0)