0
0
mirror of https://github.com/python/cpython.git synced 2024-11-21 21:09:37 +01:00

GH-126766: url2pathname(): handle empty authority section. (#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`.
This commit is contained in:
Barney Gale 2024-11-14 20:22:14 +00:00 committed by GitHub
parent 47cbf03885
commit cae9d9d20f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 14 additions and 9 deletions

View File

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

View File

@ -1549,7 +1549,7 @@ class Pathname_Tests(unittest.TestCase):
self.assertEqual(fn('//?/unc/server/share/dir'), '//server/share/dir') self.assertEqual(fn('//?/unc/server/share/dir'), '//server/share/dir')
# Round-tripping # Round-tripping
urls = ['///C:', urls = ['///C:',
'///folder/test/', '/folder/test/',
'///C:/foo/bar/spam.foo'] '///C:/foo/bar/spam.foo']
for url in urls: for url in urls:
self.assertEqual(fn(urllib.request.url2pathname(url)), url) self.assertEqual(fn(urllib.request.url2pathname(url)), url)
@ -1573,7 +1573,7 @@ class Pathname_Tests(unittest.TestCase):
self.assertEqual(fn('/C|//'), 'C:\\\\') self.assertEqual(fn('/C|//'), 'C:\\\\')
self.assertEqual(fn('///C|/path'), 'C:\\path') self.assertEqual(fn('///C|/path'), 'C:\\path')
# No DOS drive # No DOS drive
self.assertEqual(fn("///C/test/"), '\\\\\\C\\test\\') self.assertEqual(fn("///C/test/"), '\\C\\test\\')
self.assertEqual(fn("////C/test/"), '\\\\C\\test\\') self.assertEqual(fn("////C/test/"), '\\\\C\\test\\')
# DOS drive paths # DOS drive paths
self.assertEqual(fn('C:/path/to/file'), 'C:\\path\\to\\file') self.assertEqual(fn('C:/path/to/file'), 'C:\\path\\to\\file')
@ -1597,7 +1597,7 @@ class Pathname_Tests(unittest.TestCase):
self.assertEqual(fn('//server/share/foo%2fbar'), '\\\\server\\share\\foo/bar') self.assertEqual(fn('//server/share/foo%2fbar'), '\\\\server\\share\\foo/bar')
# Round-tripping # Round-tripping
paths = ['C:', paths = ['C:',
r'\\\C\test\\', r'\C\test\\',
r'C:\foo\bar\spam.foo'] r'C:\foo\bar\spam.foo']
for path in paths: for path in paths:
self.assertEqual(fn(urllib.request.pathname2url(path)), path) self.assertEqual(fn(urllib.request.pathname2url(path)), path)
@ -1608,8 +1608,8 @@ class Pathname_Tests(unittest.TestCase):
fn = urllib.request.url2pathname fn = urllib.request.url2pathname
self.assertEqual(fn('/foo/bar'), '/foo/bar') self.assertEqual(fn('/foo/bar'), '/foo/bar')
self.assertEqual(fn('//foo/bar'), '//foo/bar') self.assertEqual(fn('//foo/bar'), '//foo/bar')
self.assertEqual(fn('///foo/bar'), '///foo/bar') self.assertEqual(fn('///foo/bar'), '/foo/bar')
self.assertEqual(fn('////foo/bar'), '////foo/bar') self.assertEqual(fn('////foo/bar'), '//foo/bar')
self.assertEqual(fn('//localhost/foo/bar'), '//localhost/foo/bar') self.assertEqual(fn('//localhost/foo/bar'), '//localhost/foo/bar')
class Utility_Tests(unittest.TestCase): class Utility_Tests(unittest.TestCase):

View File

@ -1656,6 +1656,10 @@ else:
def url2pathname(pathname): def url2pathname(pathname):
"""OS-specific conversion from a relative URL of the 'file' scheme """OS-specific conversion from a relative URL of the 'file' scheme
to a file system path; not recommended for general use.""" to a file system path; not recommended for general use."""
if pathname[:3] == '///':
# URL has an empty authority section, so the path begins on the
# third character.
pathname = pathname[2:]
return unquote(pathname) return unquote(pathname)
def pathname2url(pathname): def pathname2url(pathname):

View File

@ -0,0 +1,2 @@
Fix issue where :func:`urllib.request.url2pathname` failed to discard two
leading slashes introducing an empty authority section.