Merge pull request #746 from jkbr/develop

Fixed encoding of fields with the same name, multipart/form-data fixes.
This commit is contained in:
Kenneth Reitz
2012-08-10 23:30:21 -07:00
3 changed files with 75 additions and 19 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>
+36 -16
View File
@@ -10,6 +10,7 @@ This module contains the primary objects that power Requests.
import os
import socket
from datetime import datetime
from io import BytesIO
from .hooks import dispatch_hook, HOOKS
from .structures import CaseInsensitiveDict
@@ -344,36 +345,55 @@ 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
else:
fn = guess_filename(v) or k
fp = v
if isinstance(fp, (bytes, str)):
if isinstance(fp, str):
fp = StringIO(fp)
fields.update({k: (fn, fp.read())})
if isinstance(fp, bytes):
fp = BytesIO(fp)
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):
+38 -3
View File
@@ -7,7 +7,6 @@
import sys
import os
sys.path.insert(0, os.path.abspath('..'))
import json
import os
import unittest
@@ -981,10 +980,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 +1027,41 @@ 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.
https://github.com/kennethreitz/requests/pull/746
"""
fields = [
('__field__', '__value__'),
('__field__', '__value__'),
]
r = post(httpbin('post'), data=fields, files=fields)
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 = (b'Content-Disposition: form-data;'
b' name="__field__"; filename="__field__"')
self.assertEqual(body.count(b'__value__'), 4)
self.assertEqual(body.count(file_field), 2)
def test_bytes_files(self):
"""Test that `bytes` can be used as the values of `files`."""
post(httpbin('post'), files={'test': b'test'})
if __name__ == '__main__':
unittest.main()