diff --git a/Doc/library/urllib.rst b/Doc/library/urllib.rst index 1856ccac468470..3ec6e881df8e01 100644 --- a/Doc/library/urllib.rst +++ b/Doc/library/urllib.rst @@ -27,16 +27,17 @@ High-level interface a server somewhere on the network. If the connection cannot be made the :exc:`IOError` exception is raised. If all went well, a file-like object is returned. This supports the following methods: :meth:`read`, :meth:`readline`, - :meth:`readlines`, :meth:`fileno`, :meth:`close`, :meth:`info` and + :meth:`readlines`, :meth:`fileno`, :meth:`close`, :meth:`info`, :meth:`getcode` and :meth:`geturl`. It also has proper support for the :term:`iterator` protocol. One caveat: the :meth:`read` method, if the size argument is omitted or negative, may not read until the end of the data stream; there is no good way to determine that the entire stream from a socket has been read in the general case. - Except for the :meth:`info` and :meth:`geturl` methods, these methods have the - same interface as for file objects --- see section :ref:`bltin-file-objects` in - this manual. (It is not a built-in file object, however, so it can't be used at - those few places where a true built-in file object is required.) + Except for the :meth:`info`, :meth:`getcode` and :meth:`geturl` methods, + these methods have the same interface as for file objects --- see section + :ref:`bltin-file-objects` in this manual. (It is not a built-in file object, + however, so it can't be used at those few places where a true built-in file + object is required.) .. index:: module: mimetools @@ -58,6 +59,9 @@ High-level interface the client was redirected to. The :meth:`geturl` method can be used to get at this redirected URL. + The :meth:`getcode` method returns the HTTP status code that was sent with the + response, or ``None`` if the URL is no HTTP URL. + If the *url* uses the :file:`http:` scheme identifier, the optional *data* argument may be given to specify a ``POST`` request (normally the request type is ``GET``). The *data* argument must be in standard diff --git a/Lib/test/test_urllib.py b/Lib/test/test_urllib.py index ed7f6ceb4de885..455e7fc7fe539f 100644 --- a/Lib/test/test_urllib.py +++ b/Lib/test/test_urllib.py @@ -47,7 +47,7 @@ def tearDown(self): def test_interface(self): # Make sure object returned by urlopen() has the specified methods for attr in ("read", "readline", "readlines", "fileno", - "close", "info", "geturl", "__iter__"): + "close", "info", "geturl", "getcode", "__iter__"): self.assert_(hasattr(self.returned_obj, attr), "object returned by urlopen() lacks %s attribute" % attr) @@ -87,6 +87,9 @@ def test_info(self): def test_geturl(self): self.assertEqual(self.returned_obj.geturl(), self.pathname) + def test_getcode(self): + self.assertEqual(self.returned_obj.getcode(), None) + def test_iter(self): # Test iterator # Don't need to count number of iterations since test would fail the @@ -123,6 +126,8 @@ def test_read(self): fp = urllib.urlopen("http://python.org/") self.assertEqual(fp.readline(), 'Hello!') self.assertEqual(fp.readline(), '') + self.assertEqual(fp.geturl(), 'http://python.org/') + self.assertEqual(fp.getcode(), 200) finally: self.unfakehttp() diff --git a/Lib/test/test_urllibnet.py b/Lib/test/test_urllibnet.py index 9105afe42af96f..a28c081569a6b4 100644 --- a/Lib/test/test_urllibnet.py +++ b/Lib/test/test_urllibnet.py @@ -83,6 +83,16 @@ def test_geturl(self): open_url.close() self.assertEqual(gotten_url, URL) + def test_getcode(self): + # test getcode() with the fancy opener to get 404 error codes + URL = "http://www.python.org/XXXinvalidXXX" + open_url = urllib.FancyURLopener().open(URL) + try: + code = open_url.getcode() + finally: + open_url.close() + self.assertEqual(code, 404) + def test_fileno(self): if (sys.platform in ('win32',) or not hasattr(os, 'fdopen')): diff --git a/Lib/urllib.py b/Lib/urllib.py index c6751b8f23d982..4a49fc026e5f3f 100644 --- a/Lib/urllib.py +++ b/Lib/urllib.py @@ -343,7 +343,7 @@ def open_http(self, url, data=None): # According to RFC 2616, "2xx" code indicates that the client's # request was successfully received, understood, and accepted. if (200 <= errcode < 300): - return addinfourl(fp, headers, "http:" + url) + return addinfourl(fp, headers, "http:" + url, errcode) else: if data is None: return self.http_error(url, fp, errcode, errmsg, headers) @@ -438,7 +438,7 @@ def open_https(self, url, data=None): # According to RFC 2616, "2xx" code indicates that the client's # request was successfully received, understood, and accepted. if (200 <= errcode < 300): - return addinfourl(fp, headers, "https:" + url) + return addinfourl(fp, headers, "https:" + url, errcode) else: if data is None: return self.http_error(url, fp, errcode, errmsg, headers) @@ -610,7 +610,7 @@ def __init__(self, *args, **kwargs): def http_error_default(self, url, fp, errcode, errmsg, headers): """Default error handling -- don't raise an exception.""" - return addinfourl(fp, headers, "http:" + url) + return addinfourl(fp, headers, "http:" + url, errcode) def http_error_302(self, url, fp, errcode, errmsg, headers, data=None): """Error 302 -- relocated (temporarily).""" @@ -953,14 +953,18 @@ def info(self): class addinfourl(addbase): """class to add info() and geturl() methods to an open file.""" - def __init__(self, fp, headers, url): + def __init__(self, fp, headers, url, code=None): addbase.__init__(self, fp) self.headers = headers self.url = url + self.code = code def info(self): return self.headers + def getcode(self): + return self.code + def geturl(self): return self.url diff --git a/Misc/NEWS b/Misc/NEWS index 3d70408f0837c6..3ff4f3ece6c695 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -369,6 +369,9 @@ Core and builtins Library ------- +- #1178141: add a getcode() method to the addinfourls that urllib.open() + returns so that you can retrieve the HTTP status code. + - Issue #1003: Fix zipfile decryption check, it would fail zip files with extended local headers.