Skip to content

Commit 54da020

Browse files
author
Thomas Vanhaniemi
committed
feat: Add support for uploading files as a list
Uploading a list of files with the same key is supported by the Requests library. This allows the receiving server to accept a list of files for the same key, i.e. accepting any number of files. Issue: #401
1 parent 233ff7c commit 54da020

File tree

5 files changed

+43
-16
lines changed

5 files changed

+43
-16
lines changed

atests/http_server/helpers.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -110,15 +110,16 @@ def get_files():
110110

111111
files = dict()
112112

113-
for k, v in request.files.items():
114-
content_type = request.files[k].content_type or 'application/octet-stream'
115-
val = json_safe(v.read(), content_type)
116-
if files.get(k):
117-
if not isinstance(files[k], list):
118-
files[k] = [files[k]]
119-
files[k].append(val)
120-
else:
121-
files[k] = val
113+
for k in request.files.keys():
114+
for v in request.files.getlist(k):
115+
content_type = v.content_type or 'application/octet-stream'
116+
val = json_safe(v.read(), content_type)
117+
if files.get(k):
118+
if not isinstance(files[k], list):
119+
files[k] = [files[k]]
120+
files[k].append(val)
121+
else:
122+
files[k] = val
122123

123124
return files
124125

atests/test_post_multipart.robot

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ Library RequestsLibrary
33

44

55
*** Test Cases ***
6-
Test Post On Session Multipart
6+
Test Post Dictionary On Session Multipart
77
${file_1}= Get File For Streaming Upload atests/randombytes.bin
88
${file_2}= Get File For Streaming Upload atests/randombytes.bin
99
${files}= Create Dictionary randombytes1 ${file_1} randombytes2 ${file_2}
@@ -14,3 +14,19 @@ Test Post On Session Multipart
1414
Should Contain ${resp.json()}[headers][Content-Length] 480
1515
Should Contain ${resp.json()}[files] randombytes1
1616
Should Contain ${resp.json()}[files] randombytes2
17+
18+
Test Post List On Session Multipart
19+
${file_1}= Get File For Streaming Upload atests/randombytes.bin
20+
${file_2}= Get File For Streaming Upload atests/randombytes.bin
21+
${file_1_tuple}= Create List file1.bin ${file_1}
22+
${file_2_tuple}= Create List file2.bin ${file_2}
23+
${file_1_upload}= Create List randombytes ${file_1_tuple}
24+
${file_2_upload}= Create List randombytes ${file_2_tuple}
25+
${files}= Create List ${file_1_upload} ${file_2_upload}
26+
27+
${resp}= POST On Session ${GLOBAL_SESSION} /anything files=${files}
28+
29+
Should Contain ${resp.json()}[headers][Content-Type] multipart/form-data; boundary=
30+
Should Contain ${resp.json()}[headers][Content-Length] 466
31+
Should Contain ${resp.json()}[files] randombytes
32+
Length Should Be ${resp.json()}[files][randombytes] 2

doc/RequestsLibrary.html

Lines changed: 2 additions & 2 deletions
Large diffs are not rendered by default.

src/RequestsLibrary/RequestsKeywords.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from RequestsLibrary import log
77
from RequestsLibrary.compat import urljoin
88
from RequestsLibrary.utils import (
9+
is_list_or_tuple,
910
is_file_descriptor,
1011
warn_if_equal_symbol_in_url_session_less,
1112
)
@@ -48,9 +49,16 @@ def _common_request(self, method, session, uri, **kwargs):
4849

4950
files = kwargs.get("files", {}) or {}
5051
data = kwargs.get("data", []) or []
51-
files_descriptor_to_close = filter(
52-
is_file_descriptor, list(files.values()) + [data]
53-
)
52+
53+
if is_list_or_tuple(files):
54+
files_descriptor_to_close = filter(
55+
is_file_descriptor, [file[1][1] for file in files] + [data]
56+
)
57+
else:
58+
files_descriptor_to_close = filter(
59+
is_file_descriptor, list(files.values()) + [data]
60+
)
61+
5462
for file_descriptor in files_descriptor_to_close:
5563
file_descriptor.close()
5664

@@ -176,7 +184,7 @@ def session_less_get(
176184
| ``json`` | A JSON serializable Python object to send in the body of the request. |
177185
| ``headers`` | Dictionary of HTTP Headers to send with the request. |
178186
| ``cookies`` | Dict or CookieJar object to send with the request. |
179-
| ``files`` | Dictionary of file-like-objects (or ``{'name': file-tuple}``) for multipart encoding upload. ``file-tuple`` can be a 2-tuple ``('filename', fileobj)``, 3-tuple ``('filename', fileobj, 'content_type')`` or a 4-tuple ``('filename', fileobj, 'content_type', custom_headers)``, where ``'content-type'`` is a string defining the content type of the given file and ``custom_headers`` a dict-like object containing additional headers to add for the file. |
187+
| ``files`` | Dictionary of file-like-objects (or ``{'name': file-tuple}``) for multipart encoding upload. ``file-tuple`` can be a 2-tuple ``('filename', fileobj)``, 3-tuple ``('filename', fileobj, 'content_type')`` or a 4-tuple ``('filename', fileobj, 'content_type', custom_headers)``, where ``'content-type'`` is a string defining the content type of the given file and ``custom_headers`` a dict-like object containing additional headers to add for the file. List or tuple of ``('key': file-tuple)`` allows uploading multiple files with the same key, resulting in a list of files on the receiving end. |
180188
| ``auth`` | Auth tuple to enable Basic/Digest/Custom HTTP Auth. |
181189
| ``timeout`` | How many seconds to wait for the server to send data before giving up, as a float, or a ``(connect timeout, read timeout)`` tuple. |
182190
| ``allow_redirects`` | Boolean. Enable/disable (values ``${True}`` or ``${False}``). Only for HEAD method keywords allow_redirection defaults to ``${False}``, all others ``${True}``. |

src/RequestsLibrary/utils.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ def is_string_type(data):
7474
def is_file_descriptor(fd):
7575
return isinstance(fd, io.IOBase)
7676

77+
def is_list_or_tuple(data):
78+
return isinstance(data, (list, tuple))
7779

7880
def utf8_urlencode(data):
7981
if is_string_type(data):

0 commit comments

Comments
 (0)