Repository: ross/requests-futures

Issue:
Allow FuturesSession to take a session object to defer to
I'd like to use requests-futures with my own session object (in my case an instance of requests-oauthlib's `OAuth2Session`). The way grequests handles this is to add an __init__ kwarg which will take a session object and, if provided, defer requests calls to it. It seems like this should be possible to do with `FuturesSession` as well.

Discussion:

Patch:
diff --git a/README.rst b/README.rst
index 5a25b22..2cc5149 100644
--- a/README.rst
+++ b/README.rst
@@ -68,6 +68,15 @@ As a shortcut in case of just increasing workers number you can pass
     from requests_futures.sessions import FuturesSession
     session = FuturesSession(max_workers=10)
 
+FutureSession will use an existing session object if supplied:
+
+.. code-block:: python
+
+    from requests import session
+    from requests_futures.sessions import FuturesSession
+    my_session = session()
+    future_session = FuturesSession(session=my_session)
+
 That's it. The api of requests.Session is preserved without any modifications
 beyond returning a Future rather than Response. As with all futures exceptions
 are shifted (thrown) to the future.result() call so try/except blocks should be
diff --git a/requests_futures/sessions.py b/requests_futures/sessions.py
index 3c52de7..905196f 100644
--- a/requests_futures/sessions.py
+++ b/requests_futures/sessions.py
@@ -27,7 +27,8 @@ from requests.adapters import DEFAULT_POOLSIZE, HTTPAdapter
 
 class FuturesSession(Session):
 
-    def __init__(self, executor=None, max_workers=2, *args, **kwargs):
+    def __init__(self, executor=None, max_workers=2, session=None, *args,
+                 **kwargs):
         """Creates a FuturesSession
 
         Notes
@@ -50,6 +51,7 @@ class FuturesSession(Session):
                 self.mount('http://', HTTPAdapter(**adapter_kwargs))
 
         self.executor = executor
+        self.session = session
 
     def request(self, *args, **kwargs):
         """Maintains the existing api for Session.request.
@@ -60,7 +62,10 @@ class FuturesSession(Session):
         response in the background, e.g. call resp.json() so that json parsing
         happens in the background thread.
         """
-        func = sup = super(FuturesSession, self).request
+        if self.session:
+            func = sup = self.session.request
+        else:
+            func = sup = super(FuturesSession, self).request
 
         background_callback = kwargs.pop('background_callback', None)
         if background_callback:


Test patch:
diff --git a/test_requests_futures.py b/test_requests_futures.py
index f9f6844..876ef6f 100644
--- a/test_requests_futures.py
+++ b/test_requests_futures.py
@@ -4,7 +4,7 @@
 """Tests for Requests."""
 
 from concurrent.futures import Future
-from requests import Response
+from requests import Response, session
 from os import environ
 from requests_futures.sessions import FuturesSession
 from unittest import TestCase, main
@@ -54,6 +54,18 @@ class RequestsTestCase(TestCase):
             resp = future.result()
         self.assertEqual('boom', cm.exception.args[0])
 
+    def test_supplied_session(self):
+        """ Tests the `session` keyword argument. """
+        requests_session = session()
+        requests_session.headers['Foo'] = 'bar'
+        sess = FuturesSession(session=requests_session)
+        future = sess.get(httpbin('headers'))
+        self.assertIsInstance(future, Future)
+        resp = future.result()
+        self.assertIsInstance(resp, Response)
+        self.assertEqual(200, resp.status_code)
+        self.assertEqual(resp.json()['headers']['Foo'], 'bar')
+
     def test_max_workers(self):
         """ Tests the `max_workers` shortcut. """
         from concurrent.futures import ThreadPoolExecutor