Fixed encoding of fields with the same name.

* Properly handle repeated data fields for multipart/form-data requests (#737)
* Allow a list of 2-tuples as the `files` agument.
* Consistently serialize lists a of parameters (#729).
This commit is contained in:
Jakub Roztocil
2012-07-27 17:08:16 +02:00
parent 64646182b2
commit cfa627ae62
3 changed files with 69 additions and 17 deletions
+1
View File
@@ -110,3 +110,4 @@ Patches and Suggestions
- Victoria Mo
- Leila Muhtasib
- Matthias Rahlf <matthias@webding.de>
- Jakub Roztocil <jakub@roztocil.name>
+32 -15
View File
@@ -344,16 +344,33 @@ class Request(object):
return data
def _encode_files(self, files):
"""Build the body for a multipart/form-data request.
Will successfully encode files when passed as a dict or a list of
2-tuples. Order is retained if data is a list of 2-tuples but abritrary
if parameters are supplied as a dict.
"""
if (not files) or isinstance(self.data, str):
return None
try:
fields = self.data.copy()
except AttributeError:
fields = dict(self.data)
def tuples(obj):
"""Ensure 2-tuples. A dict or a 2-tuples list can be supplied."""
if isinstance(obj, dict):
return list(obj.items())
elif hasattr(obj, '__iter__'):
try:
dict(obj)
except ValueError:
pass
else:
return obj
raise ValueError('A dict or a list of 2-tuples required.')
for (k, v) in list(files.items()):
# 2-tuples containing both file and data fields.
fields = []
for k, v in tuples(files):
# support for explicit filename
if isinstance(v, (tuple, list)):
fn, fp = v
@@ -362,18 +379,18 @@ class Request(object):
fp = v
if isinstance(fp, (bytes, str)):
fp = StringIO(fp)
fields.update({k: (fn, fp.read())})
fields.append((k, (fn, fp.read())))
for field in fields:
if isinstance(fields[field], numeric_types):
fields[field] = str(fields[field])
if isinstance(fields[field], list):
newvalue = ', '.join(fields[field])
fields[field] = newvalue
(body, content_type) = encode_multipart_formdata(fields)
for k, vs in tuples(self.data):
if isinstance(vs, list):
for v in vs:
fields.append((k, str(v)))
else:
fields.append((k, str(vs)))
return (body, content_type)
body, content_type = encode_multipart_formdata(fields)
return body, content_type
@property
def full_url(self):
+36 -2
View File
@@ -981,10 +981,10 @@ class RequestsTestSuite(TestSetup, TestBaseMixin, unittest.TestCase):
list for a value in the data argument."""
data = {'field': ['a', 'b']}
files = {'file': 'Garbled data'}
files = {'field': 'Garbled data'}
r = post(httpbin('post'), data=data, files=files)
t = json.loads(r.text)
self.assertEqual(t.get('form'), {'field': 'a, b'})
self.assertEqual(t.get('form'), {'field': ['a', 'b']})
self.assertEqual(t.get('files'), files)
def test_str_data_content_type(self):
@@ -1028,5 +1028,39 @@ class RequestsTestSuite(TestSetup, TestBaseMixin, unittest.TestCase):
r = get(URL())
self.assertEqual(r.status_code, 200)
def test_post_fields_with_multiple_values_and_files_as_tuples(self):
"""Test that it is possible to POST multiple data and file fields
with the same name."""
data = [
('__field__', '__value__'),
('__field__', '__value__'),
]
files = [
('__field__', '__value__'),
('__field__', '__value__'),
]
r = post(httpbin('post'), data=data, files=files)
t = json.loads(r.text)
self.assertEqual(t.get('form'), {
'__field__': [
'__value__',
'__value__',
]
})
# It's not currently possible to test for multiple file fields with
# the same name against httpbin so we need to inspect the encoded
# body manually.
request = r.request
body, content_type = request._encode_files(request.files)
file_field = ('Content-Disposition: form-data;'
' name="__field__"; filename="__field__"')
self.assertEqual(body.count('__value__'), 4)
self.assertEqual(body.count(file_field), 2)
if __name__ == '__main__':
unittest.main()