Skip to content

Commit 992437d

Browse files
Accept response objects which only provide the deprecated attributes
1 parent 140aa22 commit 992437d

2 files changed

Lines changed: 46 additions & 3 deletions

File tree

Lib/test/test_urllib2.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,21 @@ def geturl(self):
309309
return self.url
310310

311311

312+
class MockLegacyResponse(io.StringIO):
313+
# A response object written before the status attribute and the headers
314+
# attribute were added in 3.9 (gh-123503).
315+
def __init__(self, code, msg, headers, data, url=None):
316+
io.StringIO.__init__(self, data)
317+
self.code, self.msg, self.url = code, msg, url
318+
self._headers = headers
319+
320+
def info(self):
321+
return self._headers
322+
323+
def geturl(self):
324+
return self.url
325+
326+
312327
class MockCookieJar:
313328
def add_cookie_header(self, request):
314329
self.ach_req = request
@@ -1230,6 +1245,14 @@ def test_fixpath_in_weirdurls(self):
12301245
self.assertEqual(newreq.selector, '')
12311246

12321247
def test_errors(self):
1248+
self._test_errors(MockResponse)
1249+
1250+
def test_errors_legacy_response(self):
1251+
# A response object providing only the attributes deprecated in 3.9
1252+
# is still accepted (gh-123503).
1253+
self._test_errors(MockLegacyResponse)
1254+
1255+
def _test_errors(self, MockResponse):
12331256
h = urllib.request.HTTPErrorProcessor()
12341257
o = h.parent = MockOpener()
12351258

Lib/urllib/request.py

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,24 @@ def install_opener(opener):
190190
global _opener
191191
_opener = opener
192192

193+
def _get_status(response):
194+
# Handlers can return any response object, and one written before the
195+
# status attribute was added in 3.9 only provides the deprecated code
196+
# attribute (gh-123503).
197+
try:
198+
return response.status
199+
except AttributeError:
200+
return response.code
201+
202+
203+
def _get_headers(response):
204+
# Likewise, such a response only provides the deprecated info() method.
205+
try:
206+
return response.headers
207+
except AttributeError:
208+
return response.info()
209+
210+
193211
_url_tempfiles = []
194212
def urlretrieve(url, filename=None, reporthook=None, data=None):
195213
"""
@@ -210,7 +228,7 @@ def urlretrieve(url, filename=None, reporthook=None, data=None):
210228
url_type, path = _splittype(url)
211229

212230
with contextlib.closing(urlopen(url, data)) as fp:
213-
headers = fp.headers
231+
headers = _get_headers(fp)
214232

215233
# Just return the local path and the "headers" for file://
216234
# URLs. No sense in performing a copy unless requested.
@@ -596,7 +614,9 @@ class HTTPErrorProcessor(BaseHandler):
596614
handler_order = 1000 # after all other processing
597615

598616
def http_response(self, request, response):
599-
code, msg, hdrs = response.status, response.msg, response.headers
617+
code = _get_status(response)
618+
msg = response.msg
619+
hdrs = _get_headers(response)
600620

601621
# According to RFC 2616, "2xx" code indicates that the client's
602622
# request was successfully received, understood, and accepted.
@@ -1008,7 +1028,7 @@ def http_request(self, req):
10081028

10091029
def http_response(self, req, response):
10101030
if hasattr(self.passwd, 'is_authenticated'):
1011-
if 200 <= response.status < 300:
1031+
if 200 <= _get_status(response) < 300:
10121032
self.passwd.update_authenticated(req.full_url, True)
10131033
else:
10141034
self.passwd.update_authenticated(req.full_url, False)

0 commit comments

Comments
 (0)