From de49d64dbcfb108e79a0e42f983ebc2df07219a0 Mon Sep 17 00:00:00 2001 From: Senthil Kumaran Date: Sun, 16 Oct 2011 23:54:44 +0800 Subject: Fix closes issue 1673007 urllib.request to support HEAD requests with a new method arg. --- Doc/library/urllib.request.rst | 35 ++++++++++++++++++++++++++++------- Doc/whatsnew/3.3.rst | 10 ++++++++++ Lib/test/test_urllib.py | 23 +++++++++++++++++++++++ Lib/urllib/request.py | 9 +++++++-- Misc/NEWS | 4 ++++ 5 files changed, 72 insertions(+), 9 deletions(-) diff --git a/Doc/library/urllib.request.rst b/Doc/library/urllib.request.rst index c08cbc6..ecb357e 100644 --- a/Doc/library/urllib.request.rst +++ b/Doc/library/urllib.request.rst @@ -132,7 +132,7 @@ The :mod:`urllib.request` module defines the following functions: The following classes are provided: -.. class:: Request(url, data=None, headers={}, origin_req_host=None, unverifiable=False) +.. class:: Request(url, data=None, headers={}, origin_req_host=None, unverifiable=False, method=None) This class is an abstraction of a URL request. @@ -140,8 +140,8 @@ The following classes are provided: *data* may be a string specifying additional data to send to the server, or ``None`` if no such data is needed. Currently HTTP - requests are the only ones that use *data*; the HTTP request will - be a POST instead of a GET when the *data* parameter is provided. + requests are the only ones that use *data*, in order to choose between + ``'GET'`` and ``'POST'`` when *method* is not specified. *data* should be a buffer in the standard :mimetype:`application/x-www-form-urlencoded` format. The :func:`urllib.parse.urlencode` function takes a mapping or sequence @@ -157,8 +157,8 @@ The following classes are provided: :mod:`urllib`'s default user agent string is ``"Python-urllib/2.6"`` (on Python 2.6). - The final two arguments are only of interest for correct handling - of third-party HTTP cookies: + The following two arguments, *origin_req_host* and *unverifiable*, + are only of interest for correct handling of third-party HTTP cookies: *origin_req_host* should be the request-host of the origin transaction, as defined by :rfc:`2965`. It defaults to @@ -175,6 +175,13 @@ The following classes are provided: document, and the user had no option to approve the automatic fetching of the image, this should be true. + *method* should be a string that indicates the HTTP request method that + will be used (e.g. ``'HEAD'``). Its value is stored in the + :attr:`Request.method` attribute and is used by :meth:`Request.get_method()`. + + .. versionchanged:: 3.3 + :attr:`Request.method` argument is added to the Request class. + .. class:: OpenerDirector() @@ -369,6 +376,15 @@ request. boolean, indicates whether the request is unverifiable as defined by RFC 2965. +.. attribute:: Request.method + + The HTTP request method to use. This value is used by + :meth:`Request.get_method` to override the computed HTTP request + method that would otherwise be returned. This attribute is + initialized with the value of the *method* argument passed to the constructor. + + ..versionadded:: 3.3 + .. method:: Request.add_data(data) Set the :class:`Request` data to *data*. This is ignored by all handlers except @@ -378,8 +394,13 @@ request. .. method:: Request.get_method() - Return a string indicating the HTTP request method. This is only meaningful for - HTTP requests, and currently always returns ``'GET'`` or ``'POST'``. + Return a string indicating the HTTP request method. If + :attr:`Request.method` is not ``None``, return its value, otherwise return + ``'GET'`` if :attr:`Request.data` is ``None``, or ``'POST'`` if it's not. + This is only meaningful for HTTP requests. + + .. versionchanged:: 3.3 + get_method now looks at the value of :attr:`Request.method` first. .. method:: Request.has_data() diff --git a/Doc/whatsnew/3.3.rst b/Doc/whatsnew/3.3.rst index 6c57d72..945aa97 100644 --- a/Doc/whatsnew/3.3.rst +++ b/Doc/whatsnew/3.3.rst @@ -474,6 +474,16 @@ shutil path also specifying the user/group names and not only their numeric ids. (Contributed by Sandro Tosi in :issue:`12191`) +urllib +------ + +The :class:`~urllib.request.Request` class, now accepts a *method* argument +used by :meth:`~urllib.request.Request.get_method` to determine what HTTP method +should be used. For example, this will send an ``'HEAD'`` request:: + + >>> urlopen(Request('http://www.python.org', method='HEAD')) + +(:issue:`1673007`) Optimizations ============= diff --git a/Lib/test/test_urllib.py b/Lib/test/test_urllib.py index 77637a6..4e34ae5 100644 --- a/Lib/test/test_urllib.py +++ b/Lib/test/test_urllib.py @@ -1157,6 +1157,28 @@ class URLopener_Tests(unittest.TestCase): # self.assertEqual(ftp.ftp.sock.gettimeout(), 30) # ftp.close() +class RequestTests(unittest.TestCase): + """Unit tests for urllib.request.Request.""" + + def test_default_values(self): + Request = urllib.request.Request + request = Request("http://www.python.org") + self.assertEqual(request.get_method(), 'GET') + request = Request("http://www.python.org", {}) + self.assertEqual(request.get_method(), 'POST') + + def test_with_method_arg(self): + Request = urllib.request.Request + request = Request("http://www.python.org", method='HEAD') + self.assertEqual(request.method, 'HEAD') + self.assertEqual(request.get_method(), 'HEAD') + request = Request("http://www.python.org", {}, method='HEAD') + self.assertEqual(request.method, 'HEAD') + self.assertEqual(request.get_method(), 'HEAD') + request = Request("http://www.python.org", method='GET') + self.assertEqual(request.get_method(), 'GET') + request.method = 'HEAD' + self.assertEqual(request.get_method(), 'HEAD') def test_main(): @@ -1172,6 +1194,7 @@ def test_main(): Utility_Tests, URLopener_Tests, #FTPWrapperTests, + RequestTests, ) diff --git a/Lib/urllib/request.py b/Lib/urllib/request.py index a947608..65ce287 100644 --- a/Lib/urllib/request.py +++ b/Lib/urllib/request.py @@ -177,7 +177,8 @@ def request_host(request): class Request: def __init__(self, url, data=None, headers={}, - origin_req_host=None, unverifiable=False): + origin_req_host=None, unverifiable=False, + method=None): # unwrap('') --> 'type://host/path' self.full_url = unwrap(url) self.full_url, self.fragment = splittag(self.full_url) @@ -191,6 +192,7 @@ class Request: origin_req_host = request_host(self) self.origin_req_host = origin_req_host self.unverifiable = unverifiable + self.method = method self._parse() def _parse(self): @@ -202,7 +204,10 @@ class Request: self.host = unquote(self.host) def get_method(self): - if self.data is not None: + """Return a string indicating the HTTP request method.""" + if self.method is not None: + return self.method + elif self.data is not None: return "POST" else: return "GET" diff --git a/Misc/NEWS b/Misc/NEWS index f02e878..f490a7a 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -304,6 +304,10 @@ Core and Builtins Library ------- + +- issue #1673007: urllib2 to support HEAD request via new method argument. + Patch contributions by David Stanek, Patrick Westerhoff and Ezio Melotti. + - Issue #12386: packaging does not fail anymore when writing the RESOURCES file. -- cgit v0.12