garylee1: @ehrmann b2278bb21492ddbdec39459f962951fe5e0bd0c6 LGTM
diff --git a/sift/client.py b/sift/client.py
index e2bfe50..2337e53 100644
--- a/sift/client.py
+++ b/sift/client.py
@@ -27,7 +27,8 @@ class Client(object):
             api_url=API_URL,
             timeout=2.0,
             account_id=None,
-            version=sift.version.API_VERSION):
+            version=sift.version.API_VERSION,
+            session=None):
         """Initialize the client.
 
         Args:
@@ -57,6 +58,7 @@ class Client(object):
         if not isinstance(api_key, str) or len(api_key.strip()) == 0:
             raise ApiException("valid api_key is required")
 
+        self.session = session or requests.Session()
         self.api_key = api_key
         self.url = api_url
         self.timeout = timeout
@@ -102,7 +104,7 @@ class Client(object):
             return_workflow_status: Whether the API response should
                  include the status of any workflow run as a result of
                  the tracked event.
-            
+
             force_workflow_run: TODO:(rlong) Add after Rishabh adds documentation.
 
             abuse_types(optional): List of abuse types, specifying for which abuse types a score
@@ -156,7 +158,7 @@ class Client(object):
             params['force_workflow_run'] = 'true'
 
         try:
-            response = requests.post(
+            response = self.session.post(
                 path,
                 data=json.dumps(properties),
                 headers=headers,
@@ -203,7 +205,7 @@ class Client(object):
             params['abuse_types'] = ','.join(abuse_types)
 
         try:
-            response = requests.get(
+            response = self.session.get(
                 self._score_url(user_id, version),
                 headers=headers,
                 timeout=timeout,
@@ -246,7 +248,7 @@ class Client(object):
             params['abuse_types'] = ','.join(abuse_types)
 
         try:
-            response = requests.get(
+            response = self.session.get(
                 self._user_score_url(user_id, self.version),
                 headers=headers,
                 timeout=timeout,
@@ -286,7 +288,7 @@ class Client(object):
             params['abuse_types'] = ','.join(abuse_types)
 
         try:
-            response = requests.post(
+            response = self.session.post(
                 self._user_score_url(user_id, self.version),
                 headers=headers,
                 timeout=timeout,
@@ -365,7 +367,7 @@ class Client(object):
 
         try:
 
-            response = requests.delete(
+            response = self.session.delete(
                 self._label_url(user_id, version),
                 headers=headers,
                 timeout=timeout,
@@ -394,7 +396,7 @@ class Client(object):
             timeout = self.timeout
 
         try:
-            return Response(requests.get(
+            return Response(self.session.get(
                 self._workflow_status_url(self.account_id, run_id),
                 auth=requests.auth.HTTPBasicAuth(self.api_key, ''),
                 headers={'User-Agent': self._user_agent()},
@@ -438,9 +440,9 @@ class Client(object):
             params['abuse_types'] = abuse_types
 
         try:
-            return Response(requests.get(self._get_decisions_url(self.account_id), params=params,
-                                         auth=requests.auth.HTTPBasicAuth(self.api_key, ''),
-                                         headers={'User-Agent': self._user_agent()}, timeout=timeout))
+            return Response(self.session.get(self._get_decisions_url(self.account_id), params=params,
+                                             auth=requests.auth.HTTPBasicAuth(self.api_key, ''),
+                                             headers={'User-Agent': self._user_agent()}, timeout=timeout))
 
         except requests.exceptions.RequestException as e:
             raise ApiException(str(e))
@@ -465,7 +467,7 @@ class Client(object):
         self._validate_apply_decision_request(properties, user_id)
 
         try:
-            return Response(requests.post(
+            return Response(self.session.post(
                 self._user_decisions_url(self.account_id, user_id),
                 data=json.dumps(properties),
                 auth=requests.auth.HTTPBasicAuth(self.api_key, ''),
@@ -504,7 +506,7 @@ class Client(object):
         self._validate_apply_decision_request(properties, user_id)
 
         try:
-            return Response(requests.post(
+            return Response(self.session.post(
                 self._order_apply_decisions_url(self.account_id, user_id, order_id),
                 data=json.dumps(properties),
                 auth=requests.auth.HTTPBasicAuth(self.api_key, ''),
@@ -553,7 +555,7 @@ class Client(object):
             timeout = self.timeout
 
         try:
-            return Response(requests.get(
+            return Response(self.session.get(
                 self._user_decisions_url(self.account_id, user_id),
                 auth=requests.auth.HTTPBasicAuth(self.api_key, ''),
                 headers={'User-Agent': self._user_agent()},
@@ -581,7 +583,7 @@ class Client(object):
             timeout = self.timeout
 
         try:
-            return Response(requests.get(
+            return Response(self.session.get(
                 self._order_decisions_url(self.account_id, order_id),
                 auth=requests.auth.HTTPBasicAuth(self.api_key, ''),
                 headers={'User-Agent': self._user_agent()},
@@ -613,7 +615,7 @@ class Client(object):
             timeout = self.timeout
 
         try:
-            return Response(requests.get(
+            return Response(self.session.get(
                 self._content_decisions_url(self.account_id, user_id, content_id),
                 auth=requests.auth.HTTPBasicAuth(self.api_key, ''),
                 headers={'User-Agent': self._user_agent()},
@@ -643,7 +645,7 @@ class Client(object):
             timeout = self.timeout
 
         try:
-            return Response(requests.get(
+            return Response(self.session.get(
                 self._session_decisions_url(self.account_id, user_id, session_id),
                 auth=requests.auth.HTTPBasicAuth(self.api_key, ''),
                 headers={'User-Agent': self._user_agent()},
@@ -679,7 +681,7 @@ class Client(object):
         self._validate_apply_decision_request(properties, user_id)
 
         try:
-            return Response(requests.post(
+            return Response(self.session.post(
                 self._session_apply_decisions_url(self.account_id, user_id, session_id),
                 data=json.dumps(properties),
                 auth=requests.auth.HTTPBasicAuth(self.api_key, ''),
@@ -719,7 +721,7 @@ class Client(object):
         self._validate_apply_decision_request(properties, user_id)
 
         try:
-            return Response(requests.post(
+            return Response(self.session.post(
                 self._content_apply_decisions_url(self.account_id, user_id, content_id),
                 data=json.dumps(properties),
                 auth=requests.auth.HTTPBasicAuth(self.api_key, ''),

Provide a way for users to use requests.Session
We're trying to use [connection pooling](http://docs.python-requests.org/en/master/user/advanced/#session-objects) which reduces our latency from 800ms to 200ms but we're finding it very difficult to "swap out" the `requests.get` for a `session.get` due to the design of the class.

Could you please add a method hook that we could override in a subclass that returns the requests/session object? Something like this would be perfect:

```
def get_requester(self):
    return requests
```

Then in a subclass, we could override that method like so:

```

def __init__(self, *args, **kwargs):
    self.session = requests.Session( .. )
    super(Class, self).__init__(*args, **kwargs)

def get_requester(self):
    return self.session
```

And then each of the methods that needs to fire off a web request would use get_requester rather than using `requests` directly.
diff --git a/tests/test_client.py b/tests/test_client.py
index 9f3b81e..412281d 100644
--- a/tests/test_client.py
+++ b/tests/test_client.py
@@ -221,7 +221,7 @@ class TestSiftPythonClient(unittest.TestCase):
         mock_response.json.return_value = json.loads(mock_response.content)
         mock_response.status_code = 200
         mock_response.headers = response_with_data_header()
-        with mock.patch('requests.post') as mock_post:
+        with mock.patch.object(self.sift_client.session, 'post') as mock_post:
             mock_post.return_value = mock_response
             response = self.sift_client.track(event, valid_transaction_properties())
             mock_post.assert_called_with(
@@ -243,7 +243,7 @@ class TestSiftPythonClient(unittest.TestCase):
         mock_response.json.return_value = json.loads(mock_response.content)
         mock_response.status_code = 200
         mock_response.headers = response_with_data_header()
-        with mock.patch('requests.post') as mock_post:
+        with mock.patch.object(self.sift_client.session, 'post') as mock_post:
             mock_post.return_value = mock_response
             response = self.sift_client.track(
                 event, valid_transaction_properties(), timeout=test_timeout)
@@ -264,10 +264,10 @@ class TestSiftPythonClient(unittest.TestCase):
         mock_response.json.return_value = json.loads(mock_response.content)
         mock_response.status_code = 200
         mock_response.headers = response_with_data_header()
-        with mock.patch('requests.get') as mock_post:
-            mock_post.return_value = mock_response
+        with mock.patch.object(self.sift_client.session, 'get') as mock_get:
+            mock_get.return_value = mock_response
             response = self.sift_client.score('12345')
-            mock_post.assert_called_with(
+            mock_get.assert_called_with(
                 'https://api.siftscience.com/v205/score/12345',
                 params={'api_key': self.test_key},
                 headers=mock.ANY,
@@ -286,10 +286,10 @@ class TestSiftPythonClient(unittest.TestCase):
         mock_response.json.return_value = json.loads(mock_response.content)
         mock_response.status_code = 200
         mock_response.headers = response_with_data_header()
-        with mock.patch('requests.get') as mock_post:
-            mock_post.return_value = mock_response
+        with mock.patch.object(self.sift_client.session, 'get') as mock_get:
+            mock_get.return_value = mock_response
             response = self.sift_client.score('12345', test_timeout)
-            mock_post.assert_called_with(
+            mock_get.assert_called_with(
                 'https://api.siftscience.com/v205/score/12345',
                 params={'api_key': self.test_key},
                 headers=mock.ANY,
@@ -310,7 +310,7 @@ class TestSiftPythonClient(unittest.TestCase):
         mock_response.json.return_value = json.loads(mock_response.content)
         mock_response.status_code = 200
         mock_response.headers = response_with_data_header()
-        with mock.patch('requests.get') as mock_get:
+        with mock.patch.object(self.sift_client.session, 'get') as mock_get:
             mock_get.return_value = mock_response
             response = self.sift_client.get_user_score('12345', test_timeout)
             mock_get.assert_called_with(
@@ -336,7 +336,7 @@ class TestSiftPythonClient(unittest.TestCase):
         mock_response.json.return_value = json.loads(mock_response.content)
         mock_response.status_code = 200
         mock_response.headers = response_with_data_header()
-        with mock.patch('requests.get') as mock_get:
+        with mock.patch.object(self.sift_client.session, 'get') as mock_get:
             mock_get.return_value = mock_response
             response = self.sift_client.get_user_score('12345',
                                                        abuse_types=['payment_abuse', 'content_abuse'],
@@ -364,7 +364,7 @@ class TestSiftPythonClient(unittest.TestCase):
         mock_response.json.return_value = json.loads(mock_response.content)
         mock_response.status_code = 200
         mock_response.headers = response_with_data_header()
-        with mock.patch('requests.post') as mock_post:
+        with mock.patch.object(self.sift_client.session, 'post') as mock_post:
             mock_post.return_value = mock_response
             response = self.sift_client.rescore_user('12345', test_timeout)
             mock_post.assert_called_with(
@@ -390,7 +390,7 @@ class TestSiftPythonClient(unittest.TestCase):
         mock_response.json.return_value = json.loads(mock_response.content)
         mock_response.status_code = 200
         mock_response.headers = response_with_data_header()
-        with mock.patch('requests.post') as mock_post:
+        with mock.patch.object(self.sift_client.session, 'post') as mock_post:
             mock_post.return_value = mock_response
             response = self.sift_client.rescore_user('12345',
                                                      abuse_types=['payment_abuse', 'content_abuse'],
@@ -417,7 +417,7 @@ class TestSiftPythonClient(unittest.TestCase):
         mock_response.json.return_value = json.loads(mock_response.content)
         mock_response.status_code = 200
         mock_response.headers = response_with_data_header()
-        with mock.patch('requests.post') as mock_post:
+        with mock.patch.object(self.sift_client.session, 'post') as mock_post:
             mock_post.return_value = mock_response
             response = self.sift_client.track(
                 event,
@@ -471,7 +471,7 @@ class TestSiftPythonClient(unittest.TestCase):
         mock_response.json.return_value = json.loads(mock_response.content)
         mock_response.status_code = 200
         mock_response.headers = response_with_data_header()
-        with mock.patch('requests.get') as mock_get:
+        with mock.patch.object(self.sift_client.session, 'get') as mock_get:
             mock_get.return_value = mock_response
 
             response = self.sift_client.get_decisions(entity_type="user",
@@ -517,7 +517,7 @@ class TestSiftPythonClient(unittest.TestCase):
         mock_response.json.return_value = json.loads(mock_response.content)
         mock_response.status_code = 200
         mock_response.headers = response_with_data_header()
-        with mock.patch('requests.get') as mock_get:
+        with mock.patch.object(self.sift_client.session, 'get') as mock_get:
             mock_get.return_value = mock_response
 
             response = self.sift_client.get_decisions(entity_type="session",
@@ -559,7 +559,7 @@ class TestSiftPythonClient(unittest.TestCase):
         mock_response.json.return_value = json.loads(mock_response.content)
         mock_response.status_code = 200
         mock_response.headers = response_with_data_header()
-        with mock.patch('requests.post') as mock_post:
+        with mock.patch.object(self.sift_client.session, 'post') as mock_post:
             mock_post.return_value = mock_response
             response = self.sift_client.apply_user_decision(user_id, apply_decision_request)
             data = json.dumps(apply_decision_request)
@@ -646,7 +646,7 @@ class TestSiftPythonClient(unittest.TestCase):
             'source': 'MANUAL_REVIEW',
             'time': 1481569575
         }
-        with mock.patch('requests.post') as mock_post:
+        with mock.patch.object(self.sift_client.session, 'post') as mock_post:
             mock_post.return_value = mock_response
         try:
             response = self.sift_client.apply_user_decision(user_id, apply_decision_request)
@@ -664,7 +664,7 @@ class TestSiftPythonClient(unittest.TestCase):
             'decision_id': 'user_looks_ok_legacy',
             'time': 1481569575
         }
-        with mock.patch('requests.post') as mock_post:
+        with mock.patch.object(self.sift_client.session, 'post') as mock_post:
             mock_post.return_value = mock_response
         try:
             response = self.sift_client.apply_user_decision(user_id, apply_decision_request)
@@ -683,7 +683,7 @@ class TestSiftPythonClient(unittest.TestCase):
             'source': 'INVALID_SOURCE',
             'time': 1481569575
         }
-        with mock.patch('requests.post') as mock_post:
+        with mock.patch.object(self.sift_client.session, 'post') as mock_post:
             mock_post.return_value = mock_response
         try:
             response = self.sift_client.apply_user_decision(user_id, apply_decision_request)
@@ -718,7 +718,7 @@ class TestSiftPythonClient(unittest.TestCase):
         mock_response.json.return_value = json.loads(mock_response.content)
         mock_response.status_code = 200
         mock_response.headers = response_with_data_header()
-        with mock.patch('requests.post') as mock_post:
+        with mock.patch.object(self.sift_client.session, 'post') as mock_post:
             mock_post.return_value = mock_response
             response = self.sift_client.apply_order_decision(user_id, order_id, apply_decision_request)
             data = json.dumps(apply_decision_request)
@@ -754,7 +754,7 @@ class TestSiftPythonClient(unittest.TestCase):
         mock_response.json.return_value = json.loads(mock_response.content)
         mock_response.status_code = 200
         mock_response.headers = response_with_data_header()
-        with mock.patch('requests.post') as mock_post:
+        with mock.patch.object(self.sift_client.session, 'post') as mock_post:
             mock_post.return_value = mock_response
             response = self.sift_client.apply_session_decision(user_id, session_id, apply_decision_request)
             data = json.dumps(apply_decision_request)
@@ -790,7 +790,7 @@ class TestSiftPythonClient(unittest.TestCase):
         mock_response.json.return_value = json.loads(mock_response.content)
         mock_response.status_code = 200
         mock_response.headers = response_with_data_header()
-        with mock.patch('requests.post') as mock_post:
+        with mock.patch.object(self.sift_client.session, 'post') as mock_post:
             mock_post.return_value = mock_response
             response = self.sift_client.apply_content_decision(user_id, content_id, apply_decision_request)
             data = json.dumps(apply_decision_request)
@@ -809,7 +809,7 @@ class TestSiftPythonClient(unittest.TestCase):
         mock_response.json.return_value = json.loads(mock_response.content)
         mock_response.status_code = 200
         mock_response.headers = response_with_data_header()
-        with mock.patch('requests.post') as mock_post:
+        with mock.patch.object(self.sift_client.session, 'post') as mock_post:
             mock_post.return_value = mock_response
             response = self.sift_client.label(user_id, valid_label_properties())
             properties = {
@@ -837,7 +837,7 @@ class TestSiftPythonClient(unittest.TestCase):
         mock_response.json.return_value = json.loads(mock_response.content)
         mock_response.status_code = 200
         mock_response.headers = response_with_data_header()
-        with mock.patch('requests.post') as mock_post:
+        with mock.patch.object(self.sift_client.session, 'post') as mock_post:
             mock_post.return_value = mock_response
             response = self.sift_client.label(
                 user_id, valid_label_properties(), test_timeout)
@@ -862,7 +862,7 @@ class TestSiftPythonClient(unittest.TestCase):
         user_id = '54321'
         mock_response = mock.Mock()
         mock_response.status_code = 204
-        with mock.patch('requests.delete') as mock_delete:
+        with mock.patch.object(self.sift_client.session, 'delete') as mock_delete:
             mock_delete.return_value = mock_response
             response = self.sift_client.unlabel(user_id, abuse_type='account_abuse')
             mock_delete.assert_called_with(
@@ -885,7 +885,7 @@ class TestSiftPythonClient(unittest.TestCase):
 
             user_id = u'23056'
 
-            with mock.patch('requests.post') as mock_post:
+            with mock.patch.object(self.sift_client.session, 'post') as mock_post:
                 mock_post.return_value = mock_response
                 assert(self.sift_client.track(
                     u'$transaction',
@@ -893,8 +893,8 @@ class TestSiftPythonClient(unittest.TestCase):
                 assert(self.sift_client.label(
                     user_id,
                     valid_label_properties()))
-            with mock.patch('requests.get') as mock_post:
-                mock_post.return_value = mock_response
+            with mock.patch.object(self.sift_client.session, 'get') as mock_get:
+                mock_get.return_value = mock_response
                 assert(self.sift_client.score(
                     user_id, abuse_types=[u'payment_abuse', 'content_abuse']))
 
@@ -902,7 +902,7 @@ class TestSiftPythonClient(unittest.TestCase):
         user_id = "54321=.-_+@:&^%!$"
         mock_response = mock.Mock()
         mock_response.status_code = 204
-        with mock.patch('requests.delete') as mock_delete:
+        with mock.patch.object(self.sift_client.session, 'delete') as mock_delete:
             mock_delete.return_value = mock_response
             response = self.sift_client.unlabel(user_id)
             mock_delete.assert_called_with(
@@ -920,7 +920,7 @@ class TestSiftPythonClient(unittest.TestCase):
         mock_response.json.return_value = json.loads(mock_response.content)
         mock_response.status_code = 200
         mock_response.headers = response_with_data_header()
-        with mock.patch('requests.post') as mock_post:
+        with mock.patch.object(self.sift_client.session, 'post') as mock_post:
             mock_post.return_value = mock_response
             response = self.sift_client.label(
                 user_id, valid_label_properties())
@@ -951,10 +951,10 @@ class TestSiftPythonClient(unittest.TestCase):
         mock_response.json.return_value = json.loads(mock_response.content)
         mock_response.status_code = 200
         mock_response.headers = response_with_data_header()
-        with mock.patch('requests.get') as mock_post:
-            mock_post.return_value = mock_response
+        with mock.patch.object(self.sift_client.session, 'get') as mock_get:
+            mock_get.return_value = mock_response
             response = self.sift_client.score(user_id, abuse_types=['legacy'])
-            mock_post.assert_called_with(
+            mock_get.assert_called_with(
                 'https://api.siftscience.com/v205/score/%s' % urllib.quote(user_id),
                 params={'api_key': self.test_key, 'abuse_types': 'legacy'},
                 headers=mock.ANY,
@@ -968,7 +968,7 @@ class TestSiftPythonClient(unittest.TestCase):
 
     def test_exception_during_track_call(self):
         warnings.simplefilter("always")
-        with mock.patch('requests.post') as mock_post:
+        with mock.patch.object(self.sift_client.session, 'post') as mock_post:
             mock_post.side_effect = mock.Mock(
                 side_effect=requests.exceptions.RequestException("Failed"))
             try:
@@ -979,7 +979,7 @@ class TestSiftPythonClient(unittest.TestCase):
 
     def test_exception_during_score_call(self):
         warnings.simplefilter("always")
-        with mock.patch('requests.get') as mock_get:
+        with mock.patch.object(self.sift_client.session, 'get') as mock_get:
             mock_get.side_effect = mock.Mock(
                 side_effect=requests.exceptions.RequestException("Failed"))
             try:
@@ -989,7 +989,7 @@ class TestSiftPythonClient(unittest.TestCase):
 
     def test_exception_during_unlabel_call(self):
         warnings.simplefilter("always")
-        with mock.patch('requests.delete') as mock_delete:
+        with mock.patch.object(self.sift_client.session, 'delete') as mock_delete:
             mock_delete.side_effect = mock.Mock(
                 side_effect=requests.exceptions.RequestException("Failed"))
             try:
@@ -1006,7 +1006,7 @@ class TestSiftPythonClient(unittest.TestCase):
         mock_response.status_code = 200
         mock_response.headers = response_with_data_header()
 
-        with mock.patch('requests.post') as mock_post:
+        with mock.patch.object(self.sift_client.session, 'post') as mock_post:
             mock_post.return_value = mock_response
 
             response = self.sift_client.track(
@@ -1036,7 +1036,7 @@ class TestSiftPythonClient(unittest.TestCase):
         mock_response.status_code = 200
         mock_response.headers = response_with_data_header()
 
-        with mock.patch('requests.get') as mock_get:
+        with mock.patch.object(self.sift_client.session, 'get') as mock_get:
             mock_get.return_value = mock_response
 
             response = self.sift_client.get_workflow_status('4zxwibludiaaa', timeout=3)
@@ -1055,7 +1055,7 @@ class TestSiftPythonClient(unittest.TestCase):
         mock_response.status_code = 200
         mock_response.headers = response_with_data_header()
 
-        with mock.patch('requests.get') as mock_get:
+        with mock.patch.object(self.sift_client.session, 'get') as mock_get:
             mock_get.return_value = mock_response
 
             response = self.sift_client.get_user_decisions('example_user')
@@ -1074,7 +1074,7 @@ class TestSiftPythonClient(unittest.TestCase):
         mock_response.status_code = 200
         mock_response.headers = response_with_data_header()
 
-        with mock.patch('requests.get') as mock_get:
+        with mock.patch.object(self.sift_client.session, 'get') as mock_get:
             mock_get.return_value = mock_response
 
             response = self.sift_client.get_order_decisions('example_order')
@@ -1094,7 +1094,7 @@ class TestSiftPythonClient(unittest.TestCase):
         mock_response.status_code = 200
         mock_response.headers = response_with_data_header()
 
-        with mock.patch('requests.get') as mock_get:
+        with mock.patch.object(self.sift_client.session, 'get') as mock_get:
             mock_get.return_value = mock_response
 
             response = self.sift_client.get_session_decisions('example_user','example_session')
@@ -1113,7 +1113,7 @@ class TestSiftPythonClient(unittest.TestCase):
         mock_response.status_code = 200
         mock_response.headers = response_with_data_header()
 
-        with mock.patch('requests.get') as mock_get:
+        with mock.patch.object(self.sift_client.session, 'get') as mock_get:
             mock_get.return_value = mock_response
 
             response = self.sift_client.get_content_decisions('example_user', 'example_content')
@@ -1125,6 +1125,22 @@ class TestSiftPythonClient(unittest.TestCase):
             assert(response.is_ok())
             assert(response.body['decisions']['content_abuse']['decision']['id'] == 'content_looks_bad_content_abuse')
 
+    def test_provided_session(self):
+        session = mock.Mock()
+        client = sift.Client(api_key=self.test_key, account_id=self.account_id, session=session)
+
+        mock_response = mock.Mock()
+        mock_response.content = '{"status": 0, "error_message": "OK"}'
+        mock_response.json.return_value = json.loads(mock_response.content)
+        mock_response.status_code = 200
+        mock_response.headers = response_with_data_header()
+        session.post.return_value = mock_response
+
+        event = '$transaction'
+        client.track(event, valid_transaction_properties())
+        session.post.assert_called_once()
+
+
 def main():
     unittest.main()
 
diff --git a/tests/test_client_v203.py b/tests/test_client_v203.py
index be7205b..073710c 100644
--- a/tests/test_client_v203.py
+++ b/tests/test_client_v203.py
@@ -117,7 +117,7 @@ class TestSiftPythonClient(unittest.TestCase):
         mock_response.json.return_value = json.loads(mock_response.content)
         mock_response.status_code = 200
         mock_response.headers = response_with_data_header()
-        with mock.patch('requests.post') as mock_post:
+        with mock.patch.object(self.sift_client.session, 'post') as mock_post:
             mock_post.return_value = mock_response
             response = self.sift_client.track(event, valid_transaction_properties())
             mock_post.assert_called_with(
@@ -139,7 +139,7 @@ class TestSiftPythonClient(unittest.TestCase):
         mock_response.json.return_value = json.loads(mock_response.content)
         mock_response.status_code = 200
         mock_response.headers = response_with_data_header()
-        with mock.patch('requests.post') as mock_post:
+        with mock.patch.object(self.sift_client_v204.session, 'post') as mock_post:
             mock_post.return_value = mock_response
             response = self.sift_client_v204.track(
                 event, valid_transaction_properties(), timeout=test_timeout, version='203')
@@ -160,10 +160,10 @@ class TestSiftPythonClient(unittest.TestCase):
         mock_response.json.return_value = json.loads(mock_response.content)
         mock_response.status_code = 200
         mock_response.headers = response_with_data_header()
-        with mock.patch('requests.get') as mock_post:
-            mock_post.return_value = mock_response
+        with mock.patch.object(self.sift_client_v204.session, 'get') as mock_get:
+            mock_get.return_value = mock_response
             response = self.sift_client_v204.score('12345', version='203')
-            mock_post.assert_called_with(
+            mock_get.assert_called_with(
                 'https://api.siftscience.com/v203/score/12345',
                 params={'api_key': self.test_key},
                 headers=mock.ANY,
@@ -180,10 +180,10 @@ class TestSiftPythonClient(unittest.TestCase):
         mock_response.json.return_value = json.loads(mock_response.content)
         mock_response.status_code = 200
         mock_response.headers = response_with_data_header()
-        with mock.patch('requests.get') as mock_post:
-            mock_post.return_value = mock_response
+        with mock.patch.object(self.sift_client.session, 'get') as mock_get:
+            mock_get.return_value = mock_response
             response = self.sift_client.score('12345', test_timeout)
-            mock_post.assert_called_with(
+            mock_get.assert_called_with(
                 'https://api.siftscience.com/v203/score/12345',
                 params={'api_key': self.test_key},
                 headers=mock.ANY,
@@ -201,7 +201,7 @@ class TestSiftPythonClient(unittest.TestCase):
         mock_response.json.return_value = json.loads(mock_response.content)
         mock_response.status_code = 200
         mock_response.headers = response_with_data_header()
-        with mock.patch('requests.post') as mock_post:
+        with mock.patch.object(self.sift_client.session, 'post') as mock_post:
             mock_post.return_value = mock_response
             response = self.sift_client.track(
                 event, valid_transaction_properties(), return_score=True)
@@ -224,7 +224,7 @@ class TestSiftPythonClient(unittest.TestCase):
         mock_response.json.return_value = json.loads(mock_response.content)
         mock_response.status_code = 200
         mock_response.headers = response_with_data_header()
-        with mock.patch('requests.post') as mock_post:
+        with mock.patch.object(self.sift_client.session, 'post') as mock_post:
             mock_post.return_value = mock_response
             response = self.sift_client.label(user_id, valid_label_properties())
             properties = {
@@ -252,7 +252,7 @@ class TestSiftPythonClient(unittest.TestCase):
         mock_response.json.return_value = json.loads(mock_response.content)
         mock_response.status_code = 200
         mock_response.headers = response_with_data_header()
-        with mock.patch('requests.post') as mock_post:
+        with mock.patch.object(self.sift_client_v204.session, 'post') as mock_post:
             mock_post.return_value = mock_response
             response = self.sift_client_v204.label(
                 user_id, valid_label_properties(), test_timeout, version='203')
@@ -277,7 +277,7 @@ class TestSiftPythonClient(unittest.TestCase):
         user_id = '54321'
         mock_response = mock.Mock()
         mock_response.status_code = 204
-        with mock.patch('requests.delete') as mock_delete:
+        with mock.patch.object(self.sift_client.session, 'delete') as mock_delete:
             mock_delete.return_value = mock_response
             response = self.sift_client.unlabel(user_id)
             mock_delete.assert_called_with(
@@ -300,7 +300,7 @@ class TestSiftPythonClient(unittest.TestCase):
 
             user_id = u'23056'
 
-            with mock.patch('requests.post') as mock_post:
+            with mock.patch.object(self.sift_client.session, 'post') as mock_post:
                 mock_post.return_value = mock_response
                 assert(
                     self.sift_client.track(
@@ -310,15 +310,15 @@ class TestSiftPythonClient(unittest.TestCase):
                     self.sift_client.label(
                         user_id,
                         valid_label_properties()))
-            with mock.patch('requests.get') as mock_post:
-                mock_post.return_value = mock_response
+            with mock.patch.object(self.sift_client.session, 'get') as mock_get:
+                mock_get.return_value = mock_response
                 assert(self.sift_client.score(user_id))
 
     def test_unlabel_user_with_special_chars_ok(self):
         user_id = "54321=.-_+@:&^%!$"
         mock_response = mock.Mock()
         mock_response.status_code = 204
-        with mock.patch('requests.delete') as mock_delete:
+        with mock.patch.object(self.sift_client_v204.session, 'delete') as mock_delete:
             mock_delete.return_value = mock_response
             response = self.sift_client_v204.unlabel(user_id, version='203')
             mock_delete.assert_called_with(
@@ -336,7 +336,7 @@ class TestSiftPythonClient(unittest.TestCase):
         mock_response.json.return_value = json.loads(mock_response.content)
         mock_response.status_code = 200
         mock_response.headers = response_with_data_header()
-        with mock.patch('requests.post') as mock_post:
+        with mock.patch.object(self.sift_client.session, 'post') as mock_post:
             mock_post.return_value = mock_response
             response = self.sift_client.label(
                 user_id, valid_label_properties())
@@ -367,10 +367,10 @@ class TestSiftPythonClient(unittest.TestCase):
         mock_response.json.return_value = json.loads(mock_response.content)
         mock_response.status_code = 200
         mock_response.headers = response_with_data_header()
-        with mock.patch('requests.get') as mock_post:
-            mock_post.return_value = mock_response
+        with mock.patch.object(self.sift_client.session, 'get') as mock_get:
+            mock_get.return_value = mock_response
             response = self.sift_client.score(user_id)
-            mock_post.assert_called_with(
+            mock_get.assert_called_with(
                 'https://api.siftscience.com/v203/score/%s' % urllib.quote(user_id),
                 params={'api_key': self.test_key},
                 headers=mock.ANY,
@@ -382,7 +382,7 @@ class TestSiftPythonClient(unittest.TestCase):
 
     def test_exception_during_track_call(self):
         warnings.simplefilter("always")
-        with mock.patch('requests.post') as mock_post:
+        with mock.patch.object(self.sift_client.session, 'post') as mock_post:
             mock_post.side_effect = mock.Mock(
                 side_effect=requests.exceptions.RequestException("Failed"))
             try:
@@ -393,7 +393,7 @@ class TestSiftPythonClient(unittest.TestCase):
 
     def test_exception_during_score_call(self):
         warnings.simplefilter("always")
-        with mock.patch('requests.get') as mock_get:
+        with mock.patch.object(self.sift_client.session, 'get') as mock_get:
             mock_get.side_effect = mock.Mock(
                 side_effect=requests.exceptions.RequestException("Failed"))
             try:
@@ -403,7 +403,7 @@ class TestSiftPythonClient(unittest.TestCase):
 
     def test_exception_during_unlabel_call(self):
         warnings.simplefilter("always")
-        with mock.patch('requests.delete') as mock_delete:
+        with mock.patch.object(self.sift_client.session, 'delete') as mock_delete:
             mock_delete.side_effect = mock.Mock(
                 side_effect=requests.exceptions.RequestException("Failed"))
             try:
@@ -420,7 +420,7 @@ class TestSiftPythonClient(unittest.TestCase):
         mock_response.status_code = 200
         mock_response.headers = response_with_data_header()
 
-        with mock.patch('requests.post') as mock_post:
+        with mock.patch.object(self.sift_client.session, 'post') as mock_post:
             mock_post.return_value = mock_response
 
             response = self.sift_client.track(

pip install -e .[test]
parse_log_pytest
requirements.txt
pytest
3.9
requirements/base.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
certifi==2025.1.31
charset-normalizer==3.4.1
exceptiongroup==1.2.2
idna==3.10
iniconfig==2.1.0
linecache2==1.0.0
mock==5.2.0
packaging==24.2
pluggy==1.5.0
pytest==8.3.5
requests==2.32.3
-e git+https://github.com/SiftScience/sift-python.git@09b6a55cbadebcb5289c548a346f0ade3fd22685#egg=Sift
six==1.17.0
tomli==2.2.1
traceback2==1.4.0
unittest2==1.1.0
urllib3==2.3.0

name: sift-python
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - pip=25.0=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - argparse==1.4.0
      - certifi==2025.1.31
      - charset-normalizer==3.4.1
      - exceptiongroup==1.2.2
      - idna==3.10
      - iniconfig==2.1.0
      - linecache2==1.0.0
      - mock==5.2.0
      - packaging==24.2
      - pluggy==1.5.0
      - pytest==8.3.5
      - requests==2.32.3
      - six==1.17.0
      - tomli==2.2.1
      - traceback2==1.4.0
      - unittest2==1.1.0
      - urllib3==2.3.0
prefix: /opt/conda/envs/sift-python

tests/test_client.py::TestSiftPythonClient::test_apply_decision_invalid_source_fails tests/test_client.py::TestSiftPythonClient::test_apply_decision_manual_review_no_analyst_fails tests/test_client.py::TestSiftPythonClient::test_apply_decision_no_source_fails tests/test_client.py::TestSiftPythonClient::test_apply_decision_to_content_ok tests/test_client.py::TestSiftPythonClient::test_apply_decision_to_order_ok tests/test_client.py::TestSiftPythonClient::test_apply_decision_to_session_ok tests/test_client.py::TestSiftPythonClient::test_apply_decision_to_user_ok tests/test_client.py::TestSiftPythonClient::test_event_ok tests/test_client.py::TestSiftPythonClient::test_event_with_timeout_param_ok tests/test_client.py::TestSiftPythonClient::test_exception_during_score_call tests/test_client.py::TestSiftPythonClient::test_exception_during_track_call tests/test_client.py::TestSiftPythonClient::test_exception_during_unlabel_call tests/test_client.py::TestSiftPythonClient::test_get_content_decisions tests/test_client.py::TestSiftPythonClient::test_get_decisions tests/test_client.py::TestSiftPythonClient::test_get_decisions_entity_session tests/test_client.py::TestSiftPythonClient::test_get_order_decisions tests/test_client.py::TestSiftPythonClient::test_get_session_decisions tests/test_client.py::TestSiftPythonClient::test_get_user_decisions tests/test_client.py::TestSiftPythonClient::test_get_user_score_ok tests/test_client.py::TestSiftPythonClient::test_get_user_score_with_abuse_types_ok tests/test_client.py::TestSiftPythonClient::test_get_workflow_status tests/test_client.py::TestSiftPythonClient::test_label_user__with_special_chars_ok tests/test_client.py::TestSiftPythonClient::test_label_user_ok tests/test_client.py::TestSiftPythonClient::test_label_user_with_timeout_param_ok tests/test_client.py::TestSiftPythonClient::test_provided_session tests/test_client.py::TestSiftPythonClient::test_rescore_user_ok tests/test_client.py::TestSiftPythonClient::test_rescore_user_with_abuse_types_ok tests/test_client.py::TestSiftPythonClient::test_return_actions_on_track tests/test_client.py::TestSiftPythonClient::test_score__with_special_user_id_chars_ok tests/test_client.py::TestSiftPythonClient::test_score_ok tests/test_client.py::TestSiftPythonClient::test_score_with_timeout_param_ok tests/test_client.py::TestSiftPythonClient::test_sync_score_ok tests/test_client.py::TestSiftPythonClient::test_unlabel_user_ok tests/test_client.py::TestSiftPythonClient::test_unlabel_user_with_special_chars_ok tests/test_client_v203.py::TestSiftPythonClient::test_event_ok tests/test_client_v203.py::TestSiftPythonClient::test_event_with_timeout_param_ok tests/test_client_v203.py::TestSiftPythonClient::test_exception_during_score_call tests/test_client_v203.py::TestSiftPythonClient::test_exception_during_track_call tests/test_client_v203.py::TestSiftPythonClient::test_exception_during_unlabel_call tests/test_client_v203.py::TestSiftPythonClient::test_label_user__with_special_chars_ok tests/test_client_v203.py::TestSiftPythonClient::test_label_user_ok tests/test_client_v203.py::TestSiftPythonClient::test_label_user_with_timeout_param_ok tests/test_client_v203.py::TestSiftPythonClient::test_return_actions_on_track tests/test_client_v203.py::TestSiftPythonClient::test_score__with_special_user_id_chars_ok tests/test_client_v203.py::TestSiftPythonClient::test_score_ok tests/test_client_v203.py::TestSiftPythonClient::test_score_with_timeout_param_ok tests/test_client_v203.py::TestSiftPythonClient::test_sync_score_ok tests/test_client_v203.py::TestSiftPythonClient::test_unlabel_user_ok tests/test_client_v203.py::TestSiftPythonClient::test_unlabel_user_with_special_chars_ok
tests/test_client.py::TestSiftPythonClient::test_apply_decision_to_content_fails_with_no_content_id tests/test_client.py::TestSiftPythonClient::test_apply_decision_to_order_fails_with_no_order_id tests/test_client.py::TestSiftPythonClient::test_apply_decision_to_session_fails_with_no_session_id tests/test_client.py::TestSiftPythonClient::test_constructor_api_key tests/test_client.py::TestSiftPythonClient::test_constructor_invalid_api_url tests/test_client.py::TestSiftPythonClient::test_constructor_requires_valid_api_key tests/test_client.py::TestSiftPythonClient::test_get_decisions_fails tests/test_client.py::TestSiftPythonClient::test_get_session_decisions_fails_with_no_session_id tests/test_client.py::TestSiftPythonClient::test_global_api_key tests/test_client.py::TestSiftPythonClient::test_score_requires_user_id tests/test_client.py::TestSiftPythonClient::test_track_requires_properties tests/test_client.py::TestSiftPythonClient::test_track_requires_valid_event tests/test_client.py::TestSiftPythonClient::test_unicode_string_parameter_support tests/test_client.py::TestSiftPythonClient::test_validate_apply_decision_request_no_analyst_fails tests/test_client.py::TestSiftPythonClient::test_validate_apply_decision_request_no_source_fails tests/test_client.py::TestSiftPythonClient::test_validate_empty_apply_decision_request_fails tests/test_client.py::TestSiftPythonClient::test_validate_no_user_id_string_fails tests/test_client_v203.py::TestSiftPythonClient::test_score_requires_user_id tests/test_client_v203.py::TestSiftPythonClient::test_track_requires_properties tests/test_client_v203.py::TestSiftPythonClient::test_track_requires_valid_event tests/test_client_v203.py::TestSiftPythonClient::test_unicode_string_parameter_support
sift/client.py
sift/client.py