Fixed #30024 -- Made urlencode() and Client raise TypeError when None is passed as data.
This commit is contained in:
parent
293db9eb36
commit
6fe9c45b72
|
@ -192,7 +192,12 @@ def encode_multipart(boundary, data):
|
|||
# file, or a *list* of form values and/or files. Remember that HTTP field
|
||||
# names can be duplicated!
|
||||
for (key, value) in data.items():
|
||||
if is_file(value):
|
||||
if value is None:
|
||||
raise TypeError(
|
||||
'Cannot encode None as POST data. Did you mean to pass an '
|
||||
'empty string or omit the value?'
|
||||
)
|
||||
elif is_file(value):
|
||||
lines.extend(encode_file(boundary, key, value))
|
||||
elif not isinstance(value, str) and is_iterable(value):
|
||||
for item in value:
|
||||
|
|
|
@ -91,20 +91,31 @@ def urlencode(query, doseq=False):
|
|||
query = query.items()
|
||||
query_params = []
|
||||
for key, value in query:
|
||||
if isinstance(value, (str, bytes)):
|
||||
if value is None:
|
||||
raise TypeError(
|
||||
'Cannot encode None in a query string. Did you mean to pass '
|
||||
'an empty string or omit the value?'
|
||||
)
|
||||
elif isinstance(value, (str, bytes)):
|
||||
query_val = value
|
||||
else:
|
||||
try:
|
||||
iter(value)
|
||||
itr = iter(value)
|
||||
except TypeError:
|
||||
query_val = value
|
||||
else:
|
||||
# Consume generators and iterators, even when doseq=True, to
|
||||
# work around https://bugs.python.org/issue31706.
|
||||
query_val = [
|
||||
item if isinstance(item, bytes) else str(item)
|
||||
for item in value
|
||||
]
|
||||
query_val = []
|
||||
for item in itr:
|
||||
if item is None:
|
||||
raise TypeError(
|
||||
'Cannot encode None in a query string. Did you '
|
||||
'mean to pass an empty string or omit the value?'
|
||||
)
|
||||
elif not isinstance(item, bytes):
|
||||
item = str(item)
|
||||
query_val.append(item)
|
||||
query_params.append((key, query_val))
|
||||
return original_urlencode(query_params, doseq)
|
||||
|
||||
|
|
|
@ -457,6 +457,11 @@ Miscellaneous
|
|||
* Tests that violate deferrable database constraints now error when run on
|
||||
SQLite 3.20+, just like on other backends that support such constraints.
|
||||
|
||||
* To catch usage mistakes, the test :class:`~django.test.Client` and
|
||||
:func:`django.utils.http.urlencode` now raise ``TypeError`` if ``None`` is
|
||||
passed as a value to encode because ``None`` can't be encoded in GET and POST
|
||||
data. Either pass an empty string or omit the value.
|
||||
|
||||
.. _deprecated-features-2.2:
|
||||
|
||||
Features deprecated in 2.2
|
||||
|
|
|
@ -59,6 +59,14 @@ class ClientTest(TestCase):
|
|||
response = self.client.get('/get_view/?var=1\ufffd')
|
||||
self.assertEqual(response.context['var'], '1\ufffd')
|
||||
|
||||
def test_get_data_none(self):
|
||||
msg = (
|
||||
'Cannot encode None in a query string. Did you mean to pass an '
|
||||
'empty string or omit the value?'
|
||||
)
|
||||
with self.assertRaisesMessage(TypeError, msg):
|
||||
self.client.get('/get_view/', {'value': None})
|
||||
|
||||
def test_get_post_view(self):
|
||||
"GET a view that normally expects POSTs"
|
||||
response = self.client.get('/post_view/', {})
|
||||
|
@ -92,6 +100,14 @@ class ClientTest(TestCase):
|
|||
self.assertEqual(response.templates[0].name, 'POST Template')
|
||||
self.assertContains(response, 'Data received')
|
||||
|
||||
def test_post_data_none(self):
|
||||
msg = (
|
||||
'Cannot encode None as POST data. Did you mean to pass an empty '
|
||||
'string or omit the value?'
|
||||
)
|
||||
with self.assertRaisesMessage(TypeError, msg):
|
||||
self.client.post('/post_view/', {'value': None})
|
||||
|
||||
def test_json_serialization(self):
|
||||
"""The test client serializes JSON data."""
|
||||
methods = ('post', 'put', 'patch', 'delete')
|
||||
|
|
|
@ -12,7 +12,12 @@ from django.utils.http import (
|
|||
)
|
||||
|
||||
|
||||
class URLEncodeTests(unittest.TestCase):
|
||||
class URLEncodeTests(SimpleTestCase):
|
||||
cannot_encode_none_msg = (
|
||||
'Cannot encode None in a query string. Did you mean to pass an '
|
||||
'empty string or omit the value?'
|
||||
)
|
||||
|
||||
def test_tuples(self):
|
||||
self.assertEqual(urlencode((('a', 1), ('b', 2), ('c', 3))), 'a=1&b=2&c=3')
|
||||
|
||||
|
@ -65,6 +70,20 @@ class URLEncodeTests(unittest.TestCase):
|
|||
self.assertEqual(urlencode({'a': gen()}, doseq=True), 'a=0&a=1')
|
||||
self.assertEqual(urlencode({'a': gen()}, doseq=False), 'a=%5B%270%27%2C+%271%27%5D')
|
||||
|
||||
def test_none(self):
|
||||
with self.assertRaisesMessage(TypeError, self.cannot_encode_none_msg):
|
||||
urlencode({'a': None})
|
||||
|
||||
def test_none_in_sequence(self):
|
||||
with self.assertRaisesMessage(TypeError, self.cannot_encode_none_msg):
|
||||
urlencode({'a': [None]}, doseq=True)
|
||||
|
||||
def test_none_in_generator(self):
|
||||
def gen():
|
||||
yield None
|
||||
with self.assertRaisesMessage(TypeError, self.cannot_encode_none_msg):
|
||||
urlencode({'a': gen()}, doseq=True)
|
||||
|
||||
|
||||
class Base36IntTests(SimpleTestCase):
|
||||
def test_roundtrip(self):
|
||||
|
|
Loading…
Reference in New Issue