Skip to content

Commit e3a81cb

Browse files
committed
GH-126212: Fix removal of slashes in file URIs on Windows
Adjust `urllib.request.pathname2url()` and `url2pathname()` so that they don't remove slashes from Windows DOS drive paths and URLs. There was no basis for this behaviour, and it conflicts with how UNC and POSIX paths are handled.
1 parent 951cb2c commit e3a81cb

File tree

3 files changed

+15
-20
lines changed

3 files changed

+15
-20
lines changed

Lib/nturl2path.py

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -24,22 +24,14 @@ def url2pathname(url):
2424
# convert this to \\host\path\on\remote\host
2525
# (notice halving of slashes at the start of the path)
2626
url = url[2:]
27-
components = url.split('/')
28-
# make sure not to convert quoted slashes :-)
29-
return urllib.parse.unquote('\\'.join(components))
27+
return urllib.parse.unquote(url.replace('/', '\\'))
3028
comp = url.split('|')
3129
if len(comp) != 2 or comp[0][-1] not in string.ascii_letters:
3230
error = 'Bad URL: ' + url
3331
raise OSError(error)
3432
drive = comp[0][-1].upper()
35-
components = comp[1].split('/')
36-
path = drive + ':'
37-
for comp in components:
38-
if comp:
39-
path = path + '\\' + urllib.parse.unquote(comp)
40-
# Issue #11474 - handing url such as |c/|
41-
if path.endswith(':') and url.endswith('/'):
42-
path += '\\'
33+
tail = comp[1].replace('/', '\\')
34+
path = drive + ':' + urllib.parse.unquote(tail)
4335
return path
4436

4537
def pathname2url(p):
@@ -60,17 +52,13 @@ def pathname2url(p):
6052
raise OSError('Bad path: ' + p)
6153
if not ':' in p:
6254
# No drive specifier, just convert slashes and quote the name
63-
components = p.split('\\')
64-
return urllib.parse.quote('/'.join(components))
55+
return urllib.parse.quote(p.replace('\\', '/'))
6556
comp = p.split(':', maxsplit=2)
6657
if len(comp) != 2 or len(comp[0]) > 1:
6758
error = 'Bad path: ' + p
6859
raise OSError(error)
6960

7061
drive = urllib.parse.quote(comp[0].upper())
71-
components = comp[1].split('\\')
72-
path = '///' + drive + ':'
73-
for comp in components:
74-
if comp:
75-
path = path + '/' + urllib.parse.quote(comp)
62+
tail = comp[1].replace('\\', '/')
63+
path = '///' + drive + ':' + urllib.parse.quote(tail)
7664
return path

Lib/test/test_urllib.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1526,8 +1526,10 @@ def test_pathname2url_win(self):
15261526
self.assertEqual(fn('\\\\?\\C:\\dir'), '///C:/dir')
15271527
self.assertEqual(fn('\\\\?\\unc\\server\\share\\dir'), '//server/share/dir')
15281528
self.assertEqual(fn("C:"), '///C:')
1529-
self.assertEqual(fn("C:\\"), '///C:')
1529+
self.assertEqual(fn("C:\\"), '///C:/')
15301530
self.assertEqual(fn('C:\\a\\b.c'), '///C:/a/b.c')
1531+
self.assertEqual(fn('C:\\a\\b.c\\'), '///C:/a/b.c/')
1532+
self.assertEqual(fn('C:\\a\\\\b.c'), '///C:/a//b.c')
15311533
self.assertEqual(fn('C:\\a\\b%#c'), '///C:/a/b%25%23c')
15321534
self.assertEqual(fn('C:\\a\\b\xe9'), '///C:/a/b%C3%A9')
15331535
self.assertEqual(fn('C:\\foo\\bar\\spam.foo'), "///C:/foo/bar/spam.foo")
@@ -1563,13 +1565,15 @@ def test_url2pathname_win(self):
15631565
self.assertEqual(fn("///C|"), 'C:')
15641566
self.assertEqual(fn("///C:"), 'C:')
15651567
self.assertEqual(fn('///C:/'), 'C:\\')
1566-
self.assertEqual(fn('/C|//'), 'C:\\')
1568+
self.assertEqual(fn('/C|//'), 'C:\\\\')
15671569
self.assertEqual(fn('///C|/path'), 'C:\\path')
15681570
# No DOS drive
15691571
self.assertEqual(fn("///C/test/"), '\\\\\\C\\test\\')
15701572
self.assertEqual(fn("////C/test/"), '\\\\C\\test\\')
15711573
# DOS drive paths
15721574
self.assertEqual(fn('C:/path/to/file'), 'C:\\path\\to\\file')
1575+
self.assertEqual(fn('C:/path/to/file/'), 'C:\\path\\to\\file\\')
1576+
self.assertEqual(fn('C:/path/to//file'), 'C:\\path\\to\\\\file')
15731577
self.assertEqual(fn('C|/path/to/file'), 'C:\\path\\to\\file')
15741578
self.assertEqual(fn('/C|/path/to/file'), 'C:\\path\\to\\file')
15751579
self.assertEqual(fn('///C|/path/to/file'), 'C:\\path\\to\\file')
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix issue where :func:`urllib.request.pathname2url` and
2+
:func:`~urllib.request.url2pathname` removed slashes from Windows DOS drive
3+
paths and URLs.

0 commit comments

Comments
 (0)