diff --git a/Doc/library/urllib.request.rst b/Doc/library/urllib.request.rst index 18df078..dac4176 100644 --- a/Doc/library/urllib.request.rst +++ b/Doc/library/urllib.request.rst @@ -175,11 +175,15 @@ The following classes are provided: *url* should be a string containing a valid URL. - *data* must be a bytes object 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. *data* should be a buffer in the - standard :mimetype:`application/x-www-form-urlencoded` format. + *data* must be a :term:`bytes-like object ` specifying + additional data to send to the server, or ``None`` if no such data is needed. + data may also be an iterable of :term:`bytes-like object `, + including a dict with key of this type (this is not supported for HTTPS), and + in that case Content-Length value must be specified in the headers. 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. *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 of 2-tuples and returns a string in this format. It should be encoded to bytes diff --git a/Lib/test/test_urllib.py b/Lib/test/test_urllib.py index 58ca2a5..7f3bcd2 100644 --- a/Lib/test/test_urllib.py +++ b/Lib/test/test_urllib.py @@ -1407,8 +1407,12 @@ class RequestTests(unittest.TestCase): Request = urllib.request.Request request = Request("http://www.python.org") self.assertEqual(request.get_method(), 'GET') + request = Request("http://www.python.org", None) + self.assertEqual(request.get_method(), 'GET') request = Request("http://www.python.org", {}) self.assertEqual(request.get_method(), 'POST') + request = Request("http://www.python.org", b'') + self.assertEqual(request.get_method(), 'POST') def test_with_method_arg(self): Request = urllib.request.Request @@ -1424,6 +1428,15 @@ class RequestTests(unittest.TestCase): self.assertEqual(request.get_method(), 'HEAD') + def test_with_invalid_param_type(self): + Request = urllib.request.Request + with self.assertRaisesRegex(TypeError, "url should be of type str"): + request = Request(b"http://www.python.org") + with self.assertRaisesRegex(TypeError, "POST data cannot be of type str"): + request = Request("http://www.python.org", '') + with self.assertRaisesRegex(TypeError,"Key should be type of bytes in POST data"): + request = Request("http://www.python.org", {'abc': 1}) + class URL2PathNameTests(unittest.TestCase): def test_converting_drive_letter(self): diff --git a/Lib/urllib/request.py b/Lib/urllib/request.py index e6abf34..06c0e2d 100644 --- a/Lib/urllib/request.py +++ b/Lib/urllib/request.py @@ -353,6 +353,8 @@ class Request: @full_url.setter def full_url(self, url): + if not isinstance(url, str): + raise TypeError("url should be of type str") # unwrap('') --> 'type://host/path' self._full_url = unwrap(url) self._full_url, self.fragment = splittag(self._full_url) @@ -370,6 +372,13 @@ class Request: @data.setter def data(self, data): + if isinstance(data, str): + raise TypeError("POST data cannot be of type str") + + if type(data) is dict: + if any(not isinstance(k, bytes) for k in data): + raise TypeError("Key should be type of bytes in POST data") + if data != self._data: self._data = data # issue 16464 @@ -1229,10 +1238,6 @@ class AbstractHTTPHandler(BaseHandler): if request.data is not None: # POST data = request.data - if isinstance(data, str): - msg = "POST data should be bytes or an iterable of bytes. " \ - "It cannot be of type str." - raise TypeError(msg) if not request.has_header('Content-type'): request.add_unredirected_header( 'Content-type',