Properly serialize RecentlyUsedContainers for cache
authorIan Cordasco <graffatcolmingov@gmail.com>
Tue, 18 Nov 2014 04:13:35 +0000 (22:13 -0600)
committerIan Cordasco <graffatcolmingov@gmail.com>
Tue, 18 Nov 2014 04:16:32 +0000 (22:16 -0600)
RecentlyUsedContainers are threadsafe so they require a lock and as such
cannot be serialized with pickle directly. To handle it, we need to
convert it to a dictionary first and then back when deserializing.

Fixes #2345

requests/sessions.py

index c2f42b1403c99a8bee47be4e8565d62f9fc249b5..ba8eaa3da26aab0285d2c48ba584f901a4b26c75 100644 (file)
@@ -36,6 +36,8 @@ from .status_codes import codes
 # formerly defined here, reexposed here for backward compatibility
 from .models import REDIRECT_STATI
 
+REDIRECT_CACHE_SIZE = 1000
+
 
 def merge_setting(request_setting, session_setting, dict_class=OrderedDict):
     """
@@ -274,7 +276,7 @@ class Session(SessionRedirectMixin):
     __attrs__ = [
         'headers', 'cookies', 'auth', 'proxies', 'hooks', 'params', 'verify',
         'cert', 'prefetch', 'adapters', 'stream', 'trust_env',
-        'max_redirects', 'redirect_cache'
+        'max_redirects',
     ]
 
     def __init__(self):
@@ -329,7 +331,7 @@ class Session(SessionRedirectMixin):
         self.mount('http://', HTTPAdapter())
 
         # Only store 1000 redirects to prevent using infinite memory
-        self.redirect_cache = RecentlyUsedContainer(1000)
+        self.redirect_cache = RecentlyUsedContainer(REDIRECT_CACHE_SIZE)
 
     def __enter__(self):
         return self
@@ -660,12 +662,19 @@ class Session(SessionRedirectMixin):
             self.adapters[key] = self.adapters.pop(key)
 
     def __getstate__(self):
-        return dict((attr, getattr(self, attr, None)) for attr in self.__attrs__)
+        state = dict((attr, getattr(self, attr, None)) for attr in self.__attrs__)
+        state['redirect_cache'] = dict(self.redirect_cache)
+        return state
 
     def __setstate__(self, state):
+        redirect_cache = state.pop('redirect_cache', {})
         for attr, value in state.items():
             setattr(self, attr, value)
 
+        self.redirect_cache = RecentlyUsedContainer(REDIRECT_CACHE_SIZE)
+        for redirect, to in redirect_cache.items():
+            self.redirect_cache[redirect] = to
+
 
 def session():
     """Returns a :class:`Session` for context-management."""