# HG changeset patch # User Denver Coneybeare # Date 1301254486 14400 # Node ID c1787fa6a3d390200a9c3a20d113d1c2b846f82f # Parent 34adc0b917d035205c05ee4679d48b9ec0c419cd Issue 1673007: urllib.Request should take a "method" parameter to __init__() to override return value of get_method() diff --git a/Doc/library/urllib.request.rst b/Doc/library/urllib.request.rst --- a/Doc/library/urllib.request.rst +++ b/Doc/library/urllib.request.rst @@ -132,7 +132,7 @@ 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. @@ -141,7 +141,8 @@ *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. + be a POST instead of a GET when the *data* parameter is provided + (unless *method* is 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 +158,8 @@ :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 +176,10 @@ document, and the user had no option to approve the automatic fetching of the image, this should be true. + *method* should be ``None`` (the default) or a string to use a the + HTTP request method. The value given for this parameter is simply + set in the :attr:`Request.method` attribute and is used by + :meth:`Request.get_method()` when computing its return value. .. class:: OpenerDirector() @@ -354,6 +359,14 @@ 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 to the value specified to the constructor for the + *method* parameter. + .. method:: Request.add_data(data) Set the :class:`Request` data to *data*. This is ignored by all handlers except @@ -364,7 +377,9 @@ .. 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'``. + HTTP requests, and currently always returns ``'GET'`` or ``'POST'``, + unless the :attr:`Request.method` attribute is set to a value other + than ``None``, in which case that value is returned. .. method:: Request.has_data() diff --git a/Lib/test/test_urllib.py b/Lib/test/test_urllib.py --- a/Lib/test/test_urllib.py +++ b/Lib/test/test_urllib.py @@ -1105,7 +1105,57 @@ # self.assertEqual(ftp.ftp.sock.gettimeout(), 30) # ftp.close() +class RequestMethodTests(unittest.TestCase): + """Unit tests for urllib.Request and how it works with HTTP request + methods""" + def test_method_attribute_default(self): + """Ensure that Request.method=None if not specified to __init__()""" + request = urllib.request.Request("http://www.python.org") + self.assertIsNone(request.method) + + def test_method_attribute_specified(self): + """Ensure that Request.method is set to the value specified to + __init__()""" + method = object() + request = urllib.request.Request("http://www.python.org", method=method) + self.assertIs(request.method, method) + + def test_get_method_data_None_method_None(self): + """Ensure that Request.get_method() returns "GET" if both data=None and + method=None""" + request = urllib.request.Request("http://www.python.org") + self.assertEquals(request.get_method(), "GET") + + def test_get_method_data_notNone_method_None(self): + """Ensure that Request.get_method() returns "POST" if data=[not None] + and method=None""" + request = urllib.request.Request("http://www.python.org", data=object()) + self.assertEquals(request.get_method(), "POST") + + def test_get_method_data_None_method_notNone(self): + """Ensure that Request.get_method() returns self.method if data=None and + method=[not None]""" + method = object() + request = urllib.request.Request("http://www.python.org", method=method) + self.assertIs(request.get_method(), method) + + def test_get_method_data_notNone_method_notNone(self): + """Ensure that Request.get_method() returns self.method if + data=[not None] and method=[not None]""" + method = object() + request = urllib.request.Request("http://www.python.org", data=object(), + method=method) + self.assertIs(request.get_method(), method) + + def test_get_method_uses_method_attribute(self): + """Ensure that Request.get_method() returns self.method even if it was + changed after construction of the Request object""" + method1 = object() + method2 = object() + request = urllib.request.Request("http://www.python.org", method=method1) + request.method = method2 + self.assertIs(request.get_method(), method2) def test_main(): support.run_unittest( @@ -1120,6 +1170,7 @@ Utility_Tests, URLopener_Tests, #FTPWrapperTests, + RequestMethodTests, ) diff --git a/Lib/urllib/request.py b/Lib/urllib/request.py --- a/Lib/urllib/request.py +++ b/Lib/urllib/request.py @@ -177,7 +177,8 @@ 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, fragment = splittag(self.full_url) @@ -191,6 +192,7 @@ 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,9 @@ self.host = unquote(self.host) def get_method(self): - if self.data is not None: + if self.method is not None: + return self.method + elif self.data is not None: return "POST" else: return "GET"