mirror of
https://github.com/kennethreitz/requests.git
synced 2026-06-05 22:50:18 +00:00
Merge pull request #746 from jkbr/develop
Fixed encoding of fields with the same name, multipart/form-data fixes.
This commit is contained in:
@@ -110,3 +110,4 @@ Patches and Suggestions
|
||||
- Victoria Mo
|
||||
- Leila Muhtasib
|
||||
- Matthias Rahlf <matthias@webding.de>
|
||||
- Jakub Roztocil <jakub@roztocil.name>
|
||||
|
||||
+36
-16
@@ -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
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user