Response and Request objects are pickleable.
authorEric Larson <eric@ionrock.org>
Thu, 7 Nov 2013 02:01:27 +0000 (20:01 -0600)
committerEric Larson <eric@ionrock.org>
Thu, 7 Nov 2013 02:01:27 +0000 (20:01 -0600)
Includes a basic test. More could be add to confirm known attributes
that could cause problems.

requests/models.py
test_requests.py

index f82f56a3bb1d7c7fe2d41b2593b8b34391dc7cfd..81eb10923a293c527dfe5c3401fe95486b45b521 100644 (file)
@@ -489,6 +489,19 @@ class Response(object):
     server's response to an HTTP request.
     """
 
+    __attrs__ = [
+        '_content',
+        'status_code',
+        'headers',
+        'url',
+        'history',
+        'encoding',
+        'reason',
+        'cookies',
+        'elapsed',
+        'request',
+    ]
+
     def __init__(self):
         super(Response, self).__init__()
 
@@ -528,6 +541,24 @@ class Response(object):
         #: and the arrival of the response (as a timedelta)
         self.elapsed = datetime.timedelta(0)
 
+    def __getstate__(self):
+        # Consume everything; accessing the content attribute makes
+        # sure the content has been fully read.
+        if not self._content_consumed:
+            self.content
+
+        return dict(
+            (attr, getattr(self, attr, None))
+            for attr in self.__attrs__
+        )
+
+    def __setstate__(self, state):
+        for name, value in state.items():
+            setattr(self, name, value)
+
+        # pickled objects do not have .raw
+        setattr(self, '_content_consumed', True)
+
     def __repr__(self):
         return '<Response [%s]>' % (self.status_code)
 
index 754581e1a965d61915d8d633c26560292ec83dc9..777137b960b33032a2eccdbf6ef18c72c5a7475a 100755 (executable)
@@ -433,7 +433,7 @@ class RequestsTestCase(unittest.TestCase):
         prep = r.prepare()
         assert b'name="stuff"' in prep.body
         assert b'name="b\'stuff\'"' not in prep.body
-    
+
     def test_unicode_method_name(self):
         files = {'file': open('test_requests.py', 'rb')}
         r = requests.request(method=u'POST', url=httpbin('post'), files=files)
@@ -547,6 +547,18 @@ class RequestsTestCase(unittest.TestCase):
         assert next(iter(r))
         io.close()
 
+    def test_request_and_response_are_pickleable(self):
+        r = requests.get(httpbin('get'))
+
+        # verify we can pickle the original request
+        assert pickle.loads(pickle.dumps(r.request))
+
+        # verify we can pickle the response and that we have access to
+        # the original request.
+        pr = pickle.loads(pickle.dumps(r))
+        assert r.request.url == pr.request.url
+        assert r.request.headers == pr.request.headers
+
     def test_get_auth_from_url(self):
         url = 'http://user:pass@complex.url.com/path?query=yes'
         assert ('user', 'pass') == requests.utils.get_auth_from_url(url)