aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Lib/urllib/request.py
diff options
context:
space:
mode:
authorBarney Gale <barney.gale@gmail.com>2025-04-14 01:49:02 +0100
committerGitHub <noreply@github.com>2025-04-14 01:49:02 +0100
commitccad61e35d240972d14f993507566706fbf419f1 (patch)
tree05a920c319a1a3aca905d95bd57af515ca6e6608 /Lib/urllib/request.py
parent4d3ad0467e5cb145b1f4bde4be5eb776946a4269 (diff)
downloadcpython-ccad61e35d240972d14f993507566706fbf419f1.tar.gz
cpython-ccad61e35d240972d14f993507566706fbf419f1.zip
GH-125866: Support complete "file:" URLs in urllib (#132378)
Add optional *add_scheme* argument to `urllib.request.pathname2url()`; when set to true, a complete URL is returned. Likewise add optional *require_scheme* argument to `url2pathname()`; when set to true, a complete URL is accepted. Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
Diffstat (limited to 'Lib/urllib/request.py')
-rw-r--r--Lib/urllib/request.py32
1 files changed, 21 insertions, 11 deletions
diff --git a/Lib/urllib/request.py b/Lib/urllib/request.py
index 84c075ec8b3..2c9c7b6ca53 100644
--- a/Lib/urllib/request.py
+++ b/Lib/urllib/request.py
@@ -1466,17 +1466,16 @@ class FileHandler(BaseHandler):
def open_local_file(self, req):
import email.utils
import mimetypes
- filename = _splittype(req.full_url)[1]
- localfile = url2pathname(filename)
+ localfile = url2pathname(req.full_url, require_scheme=True)
try:
stats = os.stat(localfile)
size = stats.st_size
modified = email.utils.formatdate(stats.st_mtime, usegmt=True)
- mtype = mimetypes.guess_type(filename)[0]
+ mtype = mimetypes.guess_file_type(localfile)[0]
headers = email.message_from_string(
'Content-type: %s\nContent-length: %d\nLast-modified: %s\n' %
(mtype or 'text/plain', size, modified))
- origurl = f'file:{pathname2url(localfile)}'
+ origurl = pathname2url(localfile, add_scheme=True)
return addinfourl(open(localfile, 'rb'), headers, origurl)
except OSError as exp:
raise URLError(exp, exp.filename)
@@ -1635,9 +1634,16 @@ class DataHandler(BaseHandler):
# Code move from the old urllib module
-def url2pathname(url):
- """OS-specific conversion from a relative URL of the 'file' scheme
- to a file system path; not recommended for general use."""
+def url2pathname(url, *, require_scheme=False):
+ """Convert the given file URL to a local file system path.
+
+ The 'file:' scheme prefix must be omitted unless *require_scheme*
+ is set to true.
+ """
+ if require_scheme:
+ scheme, url = _splittype(url)
+ if scheme != 'file':
+ raise URLError("URL is missing a 'file:' scheme")
authority, url = _splithost(url)
if os.name == 'nt':
if not _is_local_authority(authority):
@@ -1661,13 +1667,17 @@ def url2pathname(url):
return unquote(url, encoding=encoding, errors=errors)
-def pathname2url(pathname):
- """OS-specific conversion from a file system path to a relative URL
- of the 'file' scheme; not recommended for general use."""
+def pathname2url(pathname, *, add_scheme=False):
+ """Convert the given local file system path to a file URL.
+
+ The 'file:' scheme prefix is omitted unless *add_scheme*
+ is set to true.
+ """
if os.name == 'nt':
pathname = pathname.replace('\\', '/')
encoding = sys.getfilesystemencoding()
errors = sys.getfilesystemencodeerrors()
+ scheme = 'file:' if add_scheme else ''
drive, root, tail = os.path.splitroot(pathname)
if drive:
# First, clean up some special forms. We are going to sacrifice the
@@ -1689,7 +1699,7 @@ def pathname2url(pathname):
# avoids interpreting the path as a URL authority.
root = '//' + root
tail = quote(tail, encoding=encoding, errors=errors)
- return drive + root + tail
+ return scheme + drive + root + tail
# Utility functions