Skip to content

Commit 2cf2dd6

Browse files
vasiliykorsenthil
andauthored
gh-50720: Set errno and strerror on URLError wrapping an OSError (#160938)
URLError will set errno and strerror attributes from the OSError Co-authored-by: Senthil Kumaran <senthil@python.org>
1 parent 663e99d commit 2cf2dd6

4 files changed

Lines changed: 33 additions & 0 deletions

File tree

‎Doc/library/urllib.error.rst‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ The following exceptions are raised by :mod:`!urllib.error` as appropriate:
2727
:exc:`URLError` used to be a subtype of :exc:`IOError`, which is now an
2828
alias of :exc:`OSError`.
2929

30+
.. versionchanged:: 3.16
31+
The :attr:`~OSError.errno` and :attr:`~OSError.strerror` attributes are
32+
now set from :attr:`reason` when it is an :exc:`OSError`.
33+
3034

3135
.. exception:: HTTPError(url, code, msg, hdrs, fp)
3236

‎Lib/test/test_urllib2.py‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,27 @@ def test_parse_http_list(self):
8282
def test_URLError_reasonstr(self):
8383
err = urllib.error.URLError('reason')
8484
self.assertIn(err.reason, str(err))
85+
self.assertIsNone(err.errno)
86+
self.assertIsNone(err.strerror)
87+
88+
def test_URLError_OSError_attributes(self):
89+
# gh-50720: errno and strerror are copied from a wrapped OSError.
90+
socket_error = socket.gaierror(-2, 'Name or service not known')
91+
92+
class FailingHTTPConnection(MockHTTPClass):
93+
def request(self, *args, **kwargs):
94+
raise socket_error
95+
96+
h = urllib.request.AbstractHTTPHandler()
97+
req = Request("http://example.com/")
98+
req.timeout = None
99+
100+
with self.assertRaises(urllib.error.URLError) as cm:
101+
h.do_open(FailingHTTPConnection(), req)
102+
err = cm.exception
103+
self.assertIs(err.reason, socket_error)
104+
self.assertEqual(err.errno, -2)
105+
self.assertEqual(err.strerror, 'Name or service not known')
85106

86107

87108
class RequestHdrsTests(unittest.TestCase):

‎Lib/urllib/error.py‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ def __init__(self, reason, filename=None):
2727
self.reason = reason
2828
if filename is not None:
2929
self.filename = filename
30+
if isinstance(reason, OSError):
31+
# OSError.__init__() is not called, so copy the attributes
32+
# it would have set from the wrapped error (gh-50720).
33+
self.errno = reason.errno
34+
self.strerror = reason.strerror
3035

3136
def __str__(self):
3237
return '<urlopen error %s>' % self.reason
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
:exc:`urllib.error.URLError` now sets the :attr:`~OSError.errno` and
2+
:attr:`~OSError.strerror` attributes from
3+
:attr:`~urllib.error.URLError.reason` when it is an :exc:`OSError`.

0 commit comments

Comments
 (0)