Skip to content
Open
25 changes: 24 additions & 1 deletion Lib/test/test_urllib2.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ def getheaders(self, name):
class MockResponse(io.StringIO):
def __init__(self, code, msg, headers, data, url=None):
io.StringIO.__init__(self, data)
self.code, self.msg, self.headers, self.url = code, msg, headers, url
self.status, self.msg, self.headers, self.url = code, msg, headers, url

def info(self):
return self.headers
Expand All @@ -309,6 +309,21 @@ def geturl(self):
return self.url


class MockLegacyResponse(io.StringIO):
# A response object written before the status attribute and the headers
# attribute were added in 3.9 (gh-123503).
def __init__(self, code, msg, headers, data, url=None):
io.StringIO.__init__(self, data)
self.code, self.msg, self.url = code, msg, url
self._headers = headers

def info(self):
return self._headers

def geturl(self):
return self.url


class MockCookieJar:
def add_cookie_header(self, request):
self.ach_req = request
Expand Down Expand Up @@ -1230,6 +1245,14 @@ def test_fixpath_in_weirdurls(self):
self.assertEqual(newreq.selector, '')

def test_errors(self):
self._test_errors(MockResponse)

def test_errors_legacy_response(self):
# A response object providing only the attributes deprecated in 3.9
# is still accepted (gh-123503).
self._test_errors(MockLegacyResponse)

def _test_errors(self, MockResponse):
h = urllib.request.HTTPErrorProcessor()
o = h.parent = MockOpener()

Expand Down
29 changes: 24 additions & 5 deletions Lib/urllib/request.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,24 @@ def install_opener(opener):
global _opener
_opener = opener

def _get_status(response):
# Handlers can return any response object, and one written before the
# status attribute was added in 3.9 only provides the deprecated code
# attribute (gh-123503).
try:
return response.status
except AttributeError:
return response.code


def _get_headers(response):
# Likewise, such a response only provides the deprecated info() method.
try:
return response.headers
except AttributeError:
return response.info()


_url_tempfiles = []
def urlretrieve(url, filename=None, reporthook=None, data=None):
"""
Expand All @@ -210,7 +228,7 @@ def urlretrieve(url, filename=None, reporthook=None, data=None):
url_type, path = _splittype(url)

with contextlib.closing(urlopen(url, data)) as fp:
headers = fp.info()
headers = _get_headers(fp)

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

def http_response(self, request, response):
code, msg, hdrs = response.code, response.msg, response.info()
code = _get_status(response)
msg = response.msg
hdrs = _get_headers(response)

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

def http_response(self, req, response):
if hasattr(self.passwd, 'is_authenticated'):
if 200 <= response.code < 300:
if 200 <= _get_status(response) < 300:
self.passwd.update_authenticated(req.full_url, True)
else:
self.passwd.update_authenticated(req.full_url, False)
Expand Down Expand Up @@ -1339,8 +1359,7 @@ def do_open(self, http_class, req, **http_conn_args):
# This line replaces the .msg attribute of the HTTPResponse
# with .headers, because urllib clients expect the response to
# have the reason in .msg. It would be good to mark this
# attribute is deprecated and get then to use info() or
# .headers.
# attribute is deprecated and get then to use .headers.
r.msg = r.reason
return r

Expand Down
1 change: 1 addition & 0 deletions Misc/ACKS
Original file line number Diff line number Diff line change
Expand Up @@ -1301,6 +1301,7 @@ Dustin J. Mitchell
Gideon Mitchell
Tim Mitchell
Zubin Mithra
Alexandr Mitin
Florian Mladitsch
Kevin Modzelewski
Doug Moen
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Replace usages of deprecated attributes :attr:`addinfourl.code
<urllib.response.addinfourl.code>` and :meth:`HTTPResponse.info
<http.client.HTTPResponse.info>` with their recommended equivalents
(:attr:`addinfourl.status <urllib.response.addinfourl.status>` and
:attr:`HTTPResponse.headers <http.client.HTTPResponse.headers>`),
while retaining backwards compatibility.
Loading