Kaydet (Commit) c17686f0 authored tarafından R David Murray's avatar R David Murray

Issue #13866: add *quote_via* argument to urlencode.

Patch by samwyse, completed by Arnon Yaari, and reviewed by
Martin Panter.
üst 1dbee946
...@@ -519,7 +519,8 @@ task isn't already covered by the URL parsing functions above. ...@@ -519,7 +519,8 @@ task isn't already covered by the URL parsing functions above.
Example: ``unquote_to_bytes('a%26%EF')`` yields ``b'a&\xef'``. Example: ``unquote_to_bytes('a%26%EF')`` yields ``b'a&\xef'``.
.. function:: urlencode(query, doseq=False, safe='', encoding=None, errors=None) .. function:: urlencode(query, doseq=False, safe='', encoding=None, \
errors=None, quote_via=quote_plus)
Convert a mapping object or a sequence of two-element tuples, which may Convert a mapping object or a sequence of two-element tuples, which may
contain :class:`str` or :class:`bytes` objects, to a "percent-encoded" contain :class:`str` or :class:`bytes` objects, to a "percent-encoded"
...@@ -528,8 +529,16 @@ task isn't already covered by the URL parsing functions above. ...@@ -528,8 +529,16 @@ task isn't already covered by the URL parsing functions above.
properly encoded to bytes, otherwise it would result in a :exc:`TypeError`. properly encoded to bytes, otherwise it would result in a :exc:`TypeError`.
The resulting string is a series of ``key=value`` pairs separated by ``'&'`` The resulting string is a series of ``key=value`` pairs separated by ``'&'``
characters, where both *key* and *value* are quoted using :func:`quote_plus` characters, where both *key* and *value* are quoted using the *quote_via*
above. When a sequence of two-element tuples is used as the *query* function. By default, :func:`quote_plus` is used to quote the values, which
means spaces are quoted as a ``'+'`` character and '/' characters are
encoded as ``%2F``, which follows the standard for GET requests
(``application/x-www-form-urlencoded``). An alternate function that can be
passed as *quote_via* is :func:`quote`, which will encode spaces as ``%20``
and not encode '/' characters. For maximum control of what is quoted, use
``quote`` and specify a value for *safe*.
When a sequence of two-element tuples is used as the *query*
argument, the first element of each tuple is a key and the second is a argument, the first element of each tuple is a key and the second is a
value. The value element in itself can be a sequence and in that case, if value. The value element in itself can be a sequence and in that case, if
the optional parameter *doseq* is evaluates to *True*, individual the optional parameter *doseq* is evaluates to *True*, individual
...@@ -538,7 +547,7 @@ task isn't already covered by the URL parsing functions above. ...@@ -538,7 +547,7 @@ task isn't already covered by the URL parsing functions above.
string will match the order of parameter tuples in the sequence. string will match the order of parameter tuples in the sequence.
The *safe*, *encoding*, and *errors* parameters are passed down to The *safe*, *encoding*, and *errors* parameters are passed down to
:func:`quote_plus` (the *encoding* and *errors* parameters are only passed *quote_via* (the *encoding* and *errors* parameters are only passed
when a query element is a :class:`str`). when a query element is a :class:`str`).
To reverse this encoding process, :func:`parse_qs` and :func:`parse_qsl` are To reverse this encoding process, :func:`parse_qs` and :func:`parse_qsl` are
...@@ -550,6 +559,9 @@ task isn't already covered by the URL parsing functions above. ...@@ -550,6 +559,9 @@ task isn't already covered by the URL parsing functions above.
.. versionchanged:: 3.2 .. versionchanged:: 3.2
Query parameter supports bytes and string objects. Query parameter supports bytes and string objects.
.. versionadded:: 3.5
*quote_via* parameter.
.. seealso:: .. seealso::
......
...@@ -622,6 +622,10 @@ urllib ...@@ -622,6 +622,10 @@ urllib
sent. (Contributed by Matej Cepl in :issue:`19494` and Akshit Khurana in sent. (Contributed by Matej Cepl in :issue:`19494` and Akshit Khurana in
:issue:`7159`.) :issue:`7159`.)
* A new :func:`~urllib.parse.urlencode` parameter *quote_via* provides a way to
control the encoding of query parts if needed. (Contributed by Samwyse and
Arnon Yaari in :issue:`13866`.)
wsgiref wsgiref
------- -------
......
...@@ -785,6 +785,16 @@ class UrlParseTestCase(unittest.TestCase): ...@@ -785,6 +785,16 @@ class UrlParseTestCase(unittest.TestCase):
result = urllib.parse.urlencode({'a': Trivial()}, True) result = urllib.parse.urlencode({'a': Trivial()}, True)
self.assertEqual(result, 'a=trivial') self.assertEqual(result, 'a=trivial')
def test_urlencode_quote_via(self):
result = urllib.parse.urlencode({'a': 'some value'})
self.assertEqual(result, "a=some+value")
result = urllib.parse.urlencode({'a': 'some value/another'},
quote_via=urllib.parse.quote)
self.assertEqual(result, "a=some%20value%2Fanother")
result = urllib.parse.urlencode({'a': 'some value/another'},
safe='/', quote_via=urllib.parse.quote)
self.assertEqual(result, "a=some%20value/another")
def test_quote_from_bytes(self): def test_quote_from_bytes(self):
self.assertRaises(TypeError, urllib.parse.quote_from_bytes, 'foo') self.assertRaises(TypeError, urllib.parse.quote_from_bytes, 'foo')
result = urllib.parse.quote_from_bytes(b'archaeological arcana') result = urllib.parse.quote_from_bytes(b'archaeological arcana')
......
...@@ -750,7 +750,8 @@ def quote_from_bytes(bs, safe='/'): ...@@ -750,7 +750,8 @@ def quote_from_bytes(bs, safe='/'):
_safe_quoters[safe] = quoter = Quoter(safe).__getitem__ _safe_quoters[safe] = quoter = Quoter(safe).__getitem__
return ''.join([quoter(char) for char in bs]) return ''.join([quoter(char) for char in bs])
def urlencode(query, doseq=False, safe='', encoding=None, errors=None): def urlencode(query, doseq=False, safe='', encoding=None, errors=None,
quote_via=quote_plus):
"""Encode a dict or sequence of two-element tuples into a URL query string. """Encode a dict or sequence of two-element tuples into a URL query string.
If any values in the query arg are sequences and doseq is true, each If any values in the query arg are sequences and doseq is true, each
...@@ -762,8 +763,8 @@ def urlencode(query, doseq=False, safe='', encoding=None, errors=None): ...@@ -762,8 +763,8 @@ def urlencode(query, doseq=False, safe='', encoding=None, errors=None):
The components of a query arg may each be either a string or a bytes type. The components of a query arg may each be either a string or a bytes type.
The safe, encoding, and errors parameters are passed down to quote_plus() The safe, encoding, and errors parameters are passed down to the function
(encoding and errors only if a component is a str). specified by quote_via (encoding and errors only if a component is a str).
""" """
if hasattr(query, "items"): if hasattr(query, "items"):
...@@ -789,27 +790,27 @@ def urlencode(query, doseq=False, safe='', encoding=None, errors=None): ...@@ -789,27 +790,27 @@ def urlencode(query, doseq=False, safe='', encoding=None, errors=None):
if not doseq: if not doseq:
for k, v in query: for k, v in query:
if isinstance(k, bytes): if isinstance(k, bytes):
k = quote_plus(k, safe) k = quote_via(k, safe)
else: else:
k = quote_plus(str(k), safe, encoding, errors) k = quote_via(str(k), safe, encoding, errors)
if isinstance(v, bytes): if isinstance(v, bytes):
v = quote_plus(v, safe) v = quote_via(v, safe)
else: else:
v = quote_plus(str(v), safe, encoding, errors) v = quote_via(str(v), safe, encoding, errors)
l.append(k + '=' + v) l.append(k + '=' + v)
else: else:
for k, v in query: for k, v in query:
if isinstance(k, bytes): if isinstance(k, bytes):
k = quote_plus(k, safe) k = quote_via(k, safe)
else: else:
k = quote_plus(str(k), safe, encoding, errors) k = quote_via(str(k), safe, encoding, errors)
if isinstance(v, bytes): if isinstance(v, bytes):
v = quote_plus(v, safe) v = quote_via(v, safe)
l.append(k + '=' + v) l.append(k + '=' + v)
elif isinstance(v, str): elif isinstance(v, str):
v = quote_plus(v, safe, encoding, errors) v = quote_via(v, safe, encoding, errors)
l.append(k + '=' + v) l.append(k + '=' + v)
else: else:
try: try:
...@@ -817,15 +818,15 @@ def urlencode(query, doseq=False, safe='', encoding=None, errors=None): ...@@ -817,15 +818,15 @@ def urlencode(query, doseq=False, safe='', encoding=None, errors=None):
x = len(v) x = len(v)
except TypeError: except TypeError:
# not a sequence # not a sequence
v = quote_plus(str(v), safe, encoding, errors) v = quote_via(str(v), safe, encoding, errors)
l.append(k + '=' + v) l.append(k + '=' + v)
else: else:
# loop over the sequence # loop over the sequence
for elt in v: for elt in v:
if isinstance(elt, bytes): if isinstance(elt, bytes):
elt = quote_plus(elt, safe) elt = quote_via(elt, safe)
else: else:
elt = quote_plus(str(elt), safe, encoding, errors) elt = quote_via(str(elt), safe, encoding, errors)
l.append(k + '=' + elt) l.append(k + '=' + elt)
return '&'.join(l) return '&'.join(l)
......
...@@ -47,6 +47,8 @@ Core and Builtins ...@@ -47,6 +47,8 @@ Core and Builtins
Library Library
------- -------
- Issue #13866: *quote_via* argument added to urllib.parse.urlencode.
- Issue #20098: New mangle_from_ policy option for email, default True - Issue #20098: New mangle_from_ policy option for email, default True
for compat32, but False for all other policies. for compat32, but False for all other policies.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment