Skip to content

Commit dca72d3

Browse files
pythongh-156713: Use the filesystem encoding in nturl2path
urllib.request.pathname2url() and url2pathname() use the filesystem encoding and error handler since pythongh-85168, but nturl2path, which implements them on Windows before 3.14, was left unchanged. Paths containing surrogate characters raised UnicodeEncodeError.
1 parent d7f9c64 commit dca72d3

3 files changed

Lines changed: 36 additions & 3 deletions

File tree

Lib/nturl2path.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@ def url2pathname(url):
2222
# ///C:/foo/bar/spam.foo
2323
# become
2424
# C:\foo\bar\spam.foo
25+
import sys
2526
import urllib.parse
27+
encoding = sys.getfilesystemencoding()
28+
errors = sys.getfilesystemencodeerrors()
2629
if url[:3] == '///':
2730
# URL has an empty authority section, so the path begins on the third
2831
# character.
@@ -40,7 +43,8 @@ def url2pathname(url):
4043
if url[1:2] == '|':
4144
# Older URLs use a pipe after a drive letter
4245
url = url[:1] + ':' + url[2:]
43-
return urllib.parse.unquote(url.replace('/', '\\'))
46+
return urllib.parse.unquote(url.replace('/', '\\'),
47+
encoding=encoding, errors=errors)
4448

4549
def pathname2url(p):
4650
"""OS-specific conversion from a file system path to a relative URL
@@ -50,7 +54,10 @@ def pathname2url(p):
5054
# becomes
5155
# ///C:/foo/bar/spam.foo
5256
import ntpath
57+
import sys
5358
import urllib.parse
59+
encoding = sys.getfilesystemencoding()
60+
errors = sys.getfilesystemencodeerrors()
5461
# First, clean up some special forms. We are going to sacrifice
5562
# the additional information anyway
5663
p = p.replace('\\', '/')
@@ -65,10 +72,11 @@ def pathname2url(p):
6572
# an authority section with a zero-length authority, and a path
6673
# section starting with a single slash.
6774
drive = f'///{drive}'
68-
drive = urllib.parse.quote(drive, safe='/:')
75+
drive = urllib.parse.quote(drive, encoding=encoding, errors=errors,
76+
safe='/:')
6977
elif root:
7078
# Add explicitly empty authority to path beginning with one slash.
7179
root = f'//{root}'
7280

73-
tail = urllib.parse.quote(tail)
81+
tail = urllib.parse.quote(tail, encoding=encoding, errors=errors)
7482
return drive + root + tail

Lib/test/test_nturl2path.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import sys
12
import unittest
3+
import urllib.parse
24

35
from test.support import warnings_helper
46

@@ -58,6 +60,15 @@ def test_pathname2url(self):
5860
for url in urls:
5961
self.assertEqual(fn(nturl2path.url2pathname(url)), url)
6062

63+
def test_pathname2url_surrogates(self):
64+
# gh-156713: the filesystem encoding and error handler are used,
65+
# so that paths containing surrogate characters can be converted.
66+
encoding = sys.getfilesystemencoding()
67+
errors = sys.getfilesystemencodeerrors()
68+
tail = urllib.parse.quote('a\udcff', encoding=encoding, errors=errors)
69+
self.assertEqual(nturl2path.pathname2url('C:\\a\udcff'),
70+
'///C:/' + tail)
71+
6172
def test_url2pathname(self):
6273
fn = nturl2path.url2pathname
6374
self.assertEqual(fn('/'), '\\')
@@ -103,5 +114,15 @@ def test_url2pathname(self):
103114
self.assertEqual(fn(nturl2path.pathname2url(path)), path)
104115

105116

117+
def test_url2pathname_surrogates(self):
118+
# gh-156713: the filesystem encoding and error handler are used, so
119+
# that URLs containing percent-encoded surrogates can be converted.
120+
encoding = sys.getfilesystemencoding()
121+
errors = sys.getfilesystemencodeerrors()
122+
url = urllib.parse.quote('a\udcff', encoding=encoding, errors=errors)
123+
self.assertEqual(nturl2path.url2pathname('///C:/' + url),
124+
'C:\\a\udcff')
125+
126+
106127
if __name__ == '__main__':
107128
unittest.main()
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Fix :func:`!nturl2path.pathname2url` and :func:`!nturl2path.url2pathname`:
2+
the filesystem encoding and error handler are now used for percent-encoding
3+
and decoding, as in :mod:`urllib.request`. Previously paths containing
4+
surrogate characters raised :exc:`UnicodeEncodeError`.

0 commit comments

Comments
 (0)