Allow passing a file or file-like object as data.
authorTorsten Landschoff <torsten.landschoff@dynamore.de>
Thu, 7 Jun 2012 08:13:40 +0000 (10:13 +0200)
committerTorsten Landschoff <torsten.landschoff@dynamore.de>
Thu, 7 Jun 2012 08:13:40 +0000 (10:13 +0200)
The underlying httplib already allows passing an open file object as body
to its HTTPConnection.request method. I think requests should allow that
as well.

requests/models.py
tests/test_requests.py

index 3a0818d..71cb6ad 100644 (file)
@@ -90,7 +90,7 @@ class Request(object):
         #: HTTP Method to use.
         self.method = method
 
-        #: Dictionary or byte of request body data to attach to the
+        #: Dictionary, bytes or file stream of request body data to attach to the
         #: :class:`Request <Request>`.
         self.data = None
 
@@ -323,6 +323,8 @@ class Request(object):
             return data
         if isinstance(data, str):
             return data
+        elif hasattr(data, 'read'):
+            return data
         elif hasattr(data, '__iter__'):
             try:
                 dict(data)
@@ -507,7 +509,7 @@ class Request(object):
             if self.data:
 
                 body = self._encode_params(self.data)
-                if isinstance(self.data, str):
+                if isinstance(self.data, str) or hasattr(self.data, 'read'):
                     content_type = None
                 else:
                     content_type = 'application/x-www-form-urlencoded'
index de34cfa..817fd63 100755 (executable)
@@ -12,6 +12,7 @@ import json
 import os
 import unittest
 import pickle
+import tempfile
 
 import requests
 from requests.compat import str, StringIO
@@ -463,6 +464,26 @@ class RequestsTestSuite(TestSetup, TestBaseMixin, unittest.TestCase):
             assert rbody.get('form') in (None, {})
             self.assertEqual(rbody.get('data'), 'fooaowpeuf')
 
+    def test_file_post_data(self):
+
+        filecontent = "fooaowpeufbarasjhf"
+        testfile = tempfile.NamedTemporaryFile()
+        testfile.write(filecontent)
+        testfile.flush()
+
+        for service in SERVICES:
+
+            r = post(service('post'), data=open(testfile.name, "rb"),
+                    headers={"content-type": "application/octet-stream"})
+
+            self.assertEqual(r.status_code, 200)
+            self.assertEqual(r.headers['content-type'], 'application/json')
+            self.assertEqual(r.url, service('post'))
+
+            rbody = json.loads(r.text)
+            assert rbody.get('form') in (None, {})
+            self.assertEqual(rbody.get('data'), filecontent)
+
     def test_urlencoded_post_querystring(self):
 
         for service in SERVICES: