Index: Doc/library/urllib.parse.rst =================================================================== --- Doc/library/urllib.parse.rst (revision 88386) +++ Doc/library/urllib.parse.rst (working copy) @@ -140,6 +140,7 @@ Use the :func:`urllib.parse.urlencode` function to convert such dictionaries into query strings. + .. versionchanged:: 3.2 Add *encoding* and *errors* parameters. @@ -506,9 +507,10 @@ .. function:: urlencode(query, doseq=False, safe='', encoding=None, errors=None) Convert a mapping object or a sequence of two-element tuples, which may - either be a :class:`str` or a :class:`bytes`, to a "percent-encoded" string, - suitable to pass to :func:`urlopen` above as the optional *data* argument. - This is useful to pass a dictionary of form fields to a ``POST`` request. + either be a :class:`str` or a :class:`bytes`, to a "percent-encoded" + string. The resultant string must be converted to bytes using the + user-specified encoding before it is sent to :func:`urlopen` as the optional + *data* argument. The resulting string is a series of ``key=value`` pairs separated by ``'&'`` characters, where both *key* and *value* are quoted using :func:`quote_plus` above. When a sequence of two-element tuples is used as the *query* @@ -525,6 +527,9 @@ To reverse this encoding process, :func:`parse_qs` and :func:`parse_qsl` are provided in this module to parse query strings into Python data structures. + Refer to :ref:`urllib examples ` to find out how urlencode + method can be used for generating query string for a URL or data for POST. + .. versionchanged:: 3.2 Query parameter supports bytes and string objects. Index: Doc/library/urllib.request.rst =================================================================== --- Doc/library/urllib.request.rst (revision 88386) +++ Doc/library/urllib.request.rst (working copy) @@ -967,7 +967,7 @@ >>> import urllib.request >>> req = urllib.request.Request(url='https://localhost/cgi-bin/test.cgi', - ... data='This data is passed to stdin of the CGI') + ... data=b'This data is passed to stdin of the CGI') >>> f = urllib.request.urlopen(req) >>> print(f.read().decode('utf-8')) Got Data: "This data is passed to stdin of the CGI" @@ -1043,11 +1043,13 @@ >>> f = urllib.request.urlopen("http://www.musi-cal.com/cgi-bin/query?%s" % params) >>> print(f.read().decode('utf-8')) -The following example uses the ``POST`` method instead:: +The following example uses the ``POST`` method instead. Note that params output +from urlencode is encoded to bytes before it is sent to urlopen as data:: >>> import urllib.request >>> import urllib.parse >>> params = urllib.parse.urlencode({'spam': 1, 'eggs': 2, 'bacon': 0}) + >>> params = params.encode('utf-8') >>> f = urllib.request.urlopen("http://www.musi-cal.com/cgi-bin/query", params) >>> print(f.read().decode('utf-8')) Index: Lib/urllib/request.py =================================================================== --- Lib/urllib/request.py (revision 88386) +++ Lib/urllib/request.py (working copy) @@ -1048,6 +1048,9 @@ if request.data is not None: # POST data = request.data + if isinstance(data, str): + raise TypeError("POST data should be bytes" + "or an iterable of bytes. It cannot be str.") if not request.has_header('Content-type'): request.add_unredirected_header( 'Content-type', Index: Lib/test/test_urllib2.py =================================================================== --- Lib/test/test_urllib2.py (revision 88386) +++ Lib/test/test_urllib2.py (working copy) @@ -794,6 +794,10 @@ http.raise_on_endheaders = True self.assertRaises(urllib.error.URLError, h.do_open, http, req) + # Check for TypeError on POST data which is str. + req = Request("http://example.com/","badpost") + self.assertRaises(TypeError, h.do_request_, req) + # check adding of standard headers o.addheaders = [("Spam", "eggs")] for data in b"", None: # POST, GET @@ -837,10 +841,11 @@ else: newreq = h.do_request_(req) - # A file object + # A file object. + # Test only Content-Length attribute of request. - file_obj = io.StringIO() - file_obj.write("Something\nSomething\nSomething\n") + file_obj = io.BytesIO() + file_obj.write(b"Something\nSomething\nSomething\n") for headers in {}, {"Content-Length": 30}: req = Request("http://example.com/", file_obj, headers) @@ -863,7 +868,6 @@ newreq = h.do_request_(req) self.assertEqual(int(newreq.get_header('Content-length')),16) - def test_http_doubleslash(self): # Checks the presence of any unnecessary double slash in url does not # break anything. Previously, a double slash directly after the host