Skip to content

Commit ab800ab

Browse files
committed
Updatez
1 parent f64ca0d commit ab800ab

File tree

3 files changed

+34
-27
lines changed

3 files changed

+34
-27
lines changed

Doc/library/urllib.request.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,12 @@ The :mod:`urllib.request` module defines the following functions:
181181
>>> url2pathname(url.removeprefix('file:'))
182182
'C:\\Program Files'
183183

184+
.. versionchanged:: 3.14
185+
On non-Windows platforms, if a URL authority (e.g. a hostname) is
186+
present, then it is discarded if it resolves to ``localhost``, otherwise
187+
:exc:`~urllib.error.URLError` is raised. In previous versions the
188+
authority is included in the returned path.
189+
184190
.. versionchanged:: 3.14
185191
Windows drive letters are no longer converted to uppercase, and ``:``
186192
characters not following a drive letter no longer cause an

Lib/urllib/request.py

Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1450,29 +1450,17 @@ def parse_http_list(s):
14501450
return [part.strip() for part in res]
14511451

14521452
class FileHandler(BaseHandler):
1453-
# names for the localhost
1454-
names = None
1455-
def get_names(self):
1456-
if FileHandler.names is None:
1457-
try:
1458-
FileHandler.names = tuple(
1459-
socket.gethostbyname_ex('localhost')[2] +
1460-
socket.gethostbyname_ex(socket.gethostname())[2])
1461-
except socket.gaierror:
1462-
FileHandler.names = (socket.gethostbyname('localhost'),)
1463-
return FileHandler.names
1464-
14651453
# not entirely sure what the rules are here
14661454
def open_local_file(self, req):
14671455
import email.utils
14681456
import mimetypes
1469-
filename = req.full_url
1470-
localfile = url2pathname(filename.removeprefix('file:'))
1457+
filename = _splittype(req.full_url)[1]
1458+
localfile = url2pathname(filename)
14711459
try:
14721460
stats = os.stat(localfile)
14731461
size = stats.st_size
14741462
modified = email.utils.formatdate(stats.st_mtime, usegmt=True)
1475-
mtype = mimetypes.guess_type(filename)[0]
1463+
mtype = mimetypes.guess_file_type(localfile)[0]
14761464
headers = email.message_from_string(
14771465
'Content-type: %s\nContent-length: %d\nLast-modified: %s\n' %
14781466
(mtype or 'text/plain', size, modified))
@@ -1483,14 +1471,25 @@ def open_local_file(self, req):
14831471

14841472
file_open = open_local_file
14851473

1486-
def _is_local_host(host):
1487-
if not host or host == 'localhost':
1474+
_local_addresses = None
1475+
1476+
def _is_local_authority(authority):
1477+
global _local_addresses
1478+
1479+
if not authority or authority == 'localhost':
14881480
return True
14891481
try:
1490-
name = socket.gethostbyname(host)
1482+
address = socket.gethostbyname(authority)
14911483
except socket.gaierror:
14921484
return False
1493-
return name in FileHandler().get_names()
1485+
if _local_addresses is None:
1486+
try:
1487+
_local_addresses = tuple(
1488+
socket.gethostbyname_ex('localhost')[2] +
1489+
socket.gethostbyname_ex(socket.gethostname())[2])
1490+
except socket.gaierror:
1491+
_local_addresses = (socket.gethostbyname('localhost'),)
1492+
return address in _local_addresses
14941493

14951494
class FTPHandler(BaseHandler):
14961495
def ftp_open(self, req):
@@ -1639,12 +1638,12 @@ def url2pathname(url):
16391638
"""OS-specific conversion from a relative URL of the 'file' scheme
16401639
to a file system path; not recommended for general use."""
16411640
authority, url = _splithost(url)
1642-
16431641
if os.name == 'nt':
16441642
if authority and authority != 'localhost':
1643+
# e.g. file://server/share/file.txt
16451644
url = '//' + authority + url
16461645
elif url[:3] == '///':
1647-
# Skip past extra slash before UNC drive in URL path.
1646+
# e.g. file://///server/share/file.txt
16481647
url = url[1:]
16491648
else:
16501649
if url[:1] == '/' and url[2:3] in (':', '|'):
@@ -1654,8 +1653,8 @@ def url2pathname(url):
16541653
# Older URLs use a pipe after a drive letter
16551654
url = url[:1] + ':' + url[2:]
16561655
url = url.replace('/', '\\')
1657-
elif not _is_local_host(authority):
1658-
raise URLError(f'URL {url!r} uses non-local authority {authority!r}')
1656+
elif not _is_local_authority(authority):
1657+
raise URLError("file:// scheme is supported only on localhost")
16591658
encoding = sys.getfilesystemencoding()
16601659
errors = sys.getfilesystemencodeerrors()
16611660
return unquote(url, encoding=encoding, errors=errors)
Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
Fix issue where :func:`urllib.request.url2pathname` included any URL
2-
authority in the resulting path, except on Windows. It now discards a local
3-
authority, and raises :exc:`urllib.error.URLError` for a non-local
4-
authority.
1+
Fix issue where :func:`urllib.request.url2pathname` mishandled file URLs with
2+
non-empty, non-``localhost`` authorities on non-Windows systems. Authorities
3+
that resolve to ``localhost`` are now discarded; other authorities now cause
4+
a :exc:`urllib.error.URLError` to be raised. Previously these authorities
5+
were incorrectly included in the returned path. This change does not affect
6+
Windows, where UNC paths are returned for non-local URLs.

0 commit comments

Comments
 (0)