Increasing super_len compatibilty to include BytesIO and cStringIO objects.
authorMatt Spitz <mattspitz@gmail.com>
Thu, 10 Oct 2013 18:54:47 +0000 (14:54 -0400)
committerMatt Spitz <mattspitz@gmail.com>
Thu, 10 Oct 2013 18:54:47 +0000 (14:54 -0400)
Added a check for 'getvalue' attr, calling it to retrieve the length if we can.

We also try/except the fileno() call, which can throw
io.UnsupportedOperation for BytesIO because, well, they're not files.

requests/utils.py
test_requests.py

index 3ec61312aa263eb42eff4eb683b74261638cddd3..5000105a65e9aaa2f2892db7f3ce9e7a79b6e4f4 100644 (file)
@@ -12,6 +12,7 @@ that are also useful for external consumption.
 import cgi
 import codecs
 import collections
+import io
 import os
 import platform
 import re
@@ -46,11 +47,21 @@ def dict_to_sequence(d):
 def super_len(o):
     if hasattr(o, '__len__'):
         return len(o)
+
     if hasattr(o, 'len'):
         return o.len
+
     if hasattr(o, 'fileno'):
-        return os.fstat(o.fileno()).st_size
+        try:
+            fileno = o.fileno()
+        except io.UnsupportedOperation:
+            pass
+        else:
+            return os.fstat(fileno).st_size
 
+    if hasattr(o, 'getvalue'):
+        # e.g. BytesIO, cStringIO.StringI
+        return len(o.getvalue())
 
 def get_netrc_auth(url):
     """Returns the Requests tuple auth for a given url from netrc."""
index 3d6665bad0a61860862414b507a3dcc442664b0a..18fb10ed8c674f1c54d5541a210f7fa4c3d5e15b 100755 (executable)
@@ -873,5 +873,26 @@ class TestCaseInsensitiveDict(unittest.TestCase):
         self.assertEqual(frozenset(cid), keyset)
 
 
+class UtilsTestCase(unittest.TestCase):
+
+    def test_super_len_io_streams(self):
+        """ Ensures that we properly deal with different kinds of IO streams. """
+        # uses StringIO or io.StringIO (see import above)
+        from io import BytesIO
+        from requests.utils import super_len
+
+        self.assertEqual(super_len(StringIO.StringIO()), 0)
+        self.assertEqual(super_len(StringIO.StringIO('with so much drama in the LBC')), 29)
+
+        self.assertEqual(super_len(BytesIO()), 0)
+        self.assertEqual(super_len(BytesIO(b"it's kinda hard bein' snoop d-o-double-g")), 40)
+
+        try:
+            import cStringIO
+        except ImportError:
+            pass
+        else:
+            self.assertEqual(super_len(cStringIO.StringIO('but some how, some way...')), 25)
+
 if __name__ == '__main__':
     unittest.main()