Repository: SiftScience/sift-python

Issue:
Warnings cause TypeError
I'm seeing some TypeError exceptions, it seems that the new warnings / exception handling code has some issues?

```
  ...
  File "tasks.py", line 296, in track_sift_event
    response = sift_client.track(event_type, attrs)
  File "/home/ubuntu/closeio/venv/local/lib/python2.7/site-packages/sift/client.py", line 108, in track
    warnings.warn(traceback.format_exception_only(type(e), e))
TypeError: expected string or buffer
```

Not sure exactly how/when/why this happens but it seems that the Sift responses aren't always handled properly.

/cc @JohnMcSpedon @fredsadaghiani 

Discussion:

Patch:
diff --git a/README.rst b/README.rst
index 30404c0..4d3f726 100644
--- a/README.rst
+++ b/README.rst
@@ -68,9 +68,9 @@ Here's an example:
         "$payment_gateway" : "$braintree",
         "$card_bin"        : "542486",
         "$card_last4"      : "4444"             
-    }, 
-    "$currency_code" : "USD",
-    "$amount" : 15230000,
+      }, 
+      "$currency_code" : "USD",
+      "$amount" : 15230000,
     }
 
     response = client.track(event, properties)
diff --git a/setup.py b/setup.py
index dd04072..e60cbaf 100644
--- a/setup.py
+++ b/setup.py
@@ -19,7 +19,7 @@ except:
 setup(
     name='Sift',
     description='Python bindings for Sift Science\'s API',
-    version='1.1.2.2', # NB: must be kept in sync with sift/version.py
+    version='1.1.2.3', # NB: must be kept in sync with sift/version.py
     url='https://siftscience.com',
 
     author='Sift Science',
diff --git a/sift/client.py b/sift/client.py
index 68e9311..72fbe38 100644
--- a/sift/client.py
+++ b/sift/client.py
@@ -105,8 +105,7 @@ class Client(object):
             return Response(response)
         except requests.exceptions.RequestException as e:
             warnings.warn('Failed to track event: %s' % properties)
-            warnings.warn(traceback.format_exception_only(type(e), e))
-
+            warnings.warn(traceback.format_exc())
             return e
 
     def score(self, user_id, timeout = None):
@@ -137,8 +136,7 @@ class Client(object):
             return Response(response)
         except requests.exceptions.RequestException as e:
             warnings.warn('Failed to get score for user %s' % user_id)
-            warnings.warn(traceback.format_exception_only(type(e), e))
-
+            warnings.warn(traceback.format_exc())
             return e
 
     def label(self, user_id, properties, timeout = None):
@@ -192,8 +190,7 @@ class Client(object):
 
         except requests.exceptions.RequestException as e:
             warnings.warn('Failed to unlabel user %s' % user_id)
-            warnings.warn(traceback.format_exception_only(type(e), e))
-
+            warnings.warn(traceback.format_exc())
             return e
 
 
diff --git a/sift/version.py b/sift/version.py
index 3b2ddd3..5a6aa7f 100644
--- a/sift/version.py
+++ b/sift/version.py
@@ -1,3 +1,3 @@
 # NB: Be sure to keep in sync w/ setup.py
-VERSION = '1.1.2.2'
+VERSION = '1.1.2.3'
 API_VERSION = '203'


Test patch:
diff --git a/tests/client_test.py b/tests/client_test.py
index e2d57c0..1c0b43e 100644
--- a/tests/client_test.py
+++ b/tests/client_test.py
@@ -1,4 +1,5 @@
 import datetime
+import warnings
 import json
 import mock
 import sift
@@ -303,6 +304,41 @@ class TestSiftPythonClient(unittest.TestCase):
             assert(response.is_ok())
             assert(response.api_error_message == "OK")
             assert(response.body['score'] == 0.55)
+
+    def test_exception_during_track_call(self):
+        with warnings.catch_warnings(record=True) as w:
+            warnings.simplefilter("always")
+            with mock.patch('requests.post') as mock_post:
+                mock_post.side_effect = mock.Mock(side_effect = requests.exceptions.RequestException("Failed"))
+                response = self.sift_client.track('$transaction', valid_transaction_properties())
+            assert(len(w) == 2)
+            assert('Failed to track event:' in str(w[0].message))
+            assert('RequestException: Failed' in str(w[1].message))
+            assert('Traceback' in str(w[1].message))
+
+    def test_exception_during_score_call(self):
+        with warnings.catch_warnings(record=True) as w:
+            warnings.simplefilter("always")
+            with mock.patch('requests.get') as mock_get:
+                mock_get.side_effect = mock.Mock(side_effect = requests.exceptions.RequestException("Failed"))
+                response = self.sift_client.score('Fred')
+            assert(len(w) == 2)
+            assert('Failed to get score for user Fred' in str(w[0].message))
+            assert('RequestException: Failed' in str(w[1].message))
+            assert('Traceback' in str(w[1].message))
+
+    def test_exception_during_unlabel_call(self):
+        with warnings.catch_warnings(record=True) as w:
+            warnings.simplefilter("always")
+            with mock.patch('requests.delete') as mock_delete:
+                mock_delete.side_effect = mock.Mock(side_effect = requests.exceptions.RequestException("Failed"))
+                response = self.sift_client.unlabel('Fred')
+
+            assert(len(w) == 2)
+            assert('Failed to unlabel user Fred' in str(w[0].message))
+            assert('RequestException: Failed' in str(w[1].message))
+            assert('Traceback' in str(w[1].message))
+
             
 def main():
     unittest.main()