From cc813e10ff190af38b8429d0d49fb9249493d504 Mon Sep 17 00:00:00 2001 From: Barney Gale Date: Sat, 23 Nov 2024 10:41:39 +0000 Subject: [PATCH] GH-125866: Preserve Windows drive letter case in file URIs (#127138) Stop converting Windows drive letters to uppercase in `urllib.request.pathname2url()` and `url2pathname()`. This behaviour is unnecessary and inconsistent with pathlib's file URI implementation. --- Doc/library/urllib.request.rst | 7 +++++++ Lib/nturl2path.py | 4 ++-- Lib/test/test_urllib.py | 2 ++ .../Library/2024-11-22-04-49-31.gh-issue-125866.TUtvPK.rst | 2 ++ 4 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-11-22-04-49-31.gh-issue-125866.TUtvPK.rst diff --git a/Doc/library/urllib.request.rst b/Doc/library/urllib.request.rst index e0831bf7e65ad2..a093a5083e037b 100644 --- a/Doc/library/urllib.request.rst +++ b/Doc/library/urllib.request.rst @@ -152,6 +152,9 @@ The :mod:`urllib.request` module defines the following functions: the path component of a URL. This does not produce a complete URL. The return value will already be quoted using the :func:`~urllib.parse.quote` function. + .. versionchanged:: 3.14 + Windows drive letters are no longer converted to uppercase. + .. versionchanged:: 3.14 On Windows, ``:`` characters not following a drive letter are quoted. In previous versions, :exc:`OSError` was raised if a colon character was @@ -164,6 +167,10 @@ The :mod:`urllib.request` module defines the following functions: path. This does not accept a complete URL. This function uses :func:`~urllib.parse.unquote` to decode *path*. + .. versionchanged:: 3.14 + Windows drive letters are no longer converted to uppercase. + + .. function:: getproxies() This helper function returns a dictionary of scheme to proxy server URL diff --git a/Lib/nturl2path.py b/Lib/nturl2path.py index 66092e4821a0ec..01135d1b7683b2 100644 --- a/Lib/nturl2path.py +++ b/Lib/nturl2path.py @@ -35,7 +35,7 @@ def url2pathname(url): if len(comp) != 2 or comp[0][-1] not in string.ascii_letters: error = 'Bad URL: ' + url raise OSError(error) - drive = comp[0][-1].upper() + drive = comp[0][-1] tail = urllib.parse.unquote(comp[1].replace('/', '\\')) return drive + ':' + tail @@ -60,7 +60,7 @@ def pathname2url(p): # DOS drive specified. Add three slashes to the start, producing # an authority section with a zero-length authority, and a path # section starting with a single slash. - drive = f'///{drive.upper()}' + drive = f'///{drive}' drive = urllib.parse.quote(drive, safe='/:') tail = urllib.parse.quote(tail) diff --git a/Lib/test/test_urllib.py b/Lib/test/test_urllib.py index a204ef41c3ce90..22ef3c648e271d 100644 --- a/Lib/test/test_urllib.py +++ b/Lib/test/test_urllib.py @@ -1423,6 +1423,7 @@ def test_pathname2url_win(self): self.assertEqual(fn('\\\\?\\unc\\server\\share\\dir'), '//server/share/dir') self.assertEqual(fn("C:"), '///C:') self.assertEqual(fn("C:\\"), '///C:/') + self.assertEqual(fn('c:\\a\\b.c'), '///c:/a/b.c') self.assertEqual(fn('C:\\a\\b.c'), '///C:/a/b.c') self.assertEqual(fn('C:\\a\\b.c\\'), '///C:/a/b.c/') self.assertEqual(fn('C:\\a\\\\b.c'), '///C:/a//b.c') @@ -1480,6 +1481,7 @@ def test_url2pathname_win(self): self.assertEqual(fn("///C/test/"), '\\C\\test\\') self.assertEqual(fn("////C/test/"), '\\\\C\\test\\') # 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') self.assertEqual(fn('C:/path/to/file/'), 'C:\\path\\to\\file\\') self.assertEqual(fn('C:/path/to//file'), 'C:\\path\\to\\\\file') diff --git a/Misc/NEWS.d/next/Library/2024-11-22-04-49-31.gh-issue-125866.TUtvPK.rst b/Misc/NEWS.d/next/Library/2024-11-22-04-49-31.gh-issue-125866.TUtvPK.rst new file mode 100644 index 00000000000000..682e061747689b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-11-22-04-49-31.gh-issue-125866.TUtvPK.rst @@ -0,0 +1,2 @@ +:func:`urllib.request.pathname2url` and :func:`~urllib.request.url2pathname` +no longer convert Windows drive letters to uppercase.