Repository: civisanalytics/civis-python

Issue:
cache last response
It might be nice to save the last response in the `APIClient` class so that users could retrieve it if they forget to assign it when using the client in a REPL or notebook.

e.g., 

```
> client = civis.APIClient()
> client.scripts.post_containers(...)
> client.last_response
{"id": ..., }
```

This idea came from a comment by @skomanduri.  I can't think of drawbacks to this, but perhaps there are.

Discussion:
keithing: Since this is touch the root `Endpoint` class, would you mind running `tools/smoke_test.py`?
stephen-hoover: @keithing , the smoke test completes successfully (and I merged in master and fixed the CHANGELOG conflicts).

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 061a244..9ee37b7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
 - `civis.io.dataframe_to_file` and `civis.io.json_to_file` convenience functions.
   (#262, #304)
 - Add the user's Python version to the User-Agent string. (#255, #301)
+- Added a `last_response` parameter to the `APIClient` object. (#153, #302)
 
 ### Fixed
 - Fix unintentional dependency on scikit-learn for `parallel` module tests. (#245, #303)
@@ -23,7 +24,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
 - Surfaced `civis.io.split_schema_tablename` in the Sphinx docs. (#294)
 - Loosen `joblib` version requirement to include v0.13 and add code to
   the Civis joblib backend which newer versions of `joblib` can take
-  advantage of. Also loosed version requirement on `cloudpickle` to
+  advantage of. Also loosened version requirement on `cloudpickle` to
   include v1. (#296, #299)
 
 ## 1.10.0 - 2019-04-09
diff --git a/civis/base.py b/civis/base.py
index 30bc1fc..7b98997 100644
--- a/civis/base.py
+++ b/civis/base.py
@@ -79,10 +79,11 @@ class Endpoint(object):
 
     _lock = threading.Lock()
 
-    def __init__(self, session_kwargs, return_type='civis'):
+    def __init__(self, session_kwargs, client, return_type='civis'):
         self._session_kwargs = session_kwargs
         self._return_type = return_type
         self._base_url = get_base_url()
+        self._client = client
 
     def _build_path(self, path):
         if not path:
@@ -112,12 +113,13 @@ class Endpoint(object):
         iterator = kwargs.pop('iterator', False)
 
         if iterator:
-            return PaginatedResponse(path, params, self)
+            resp = PaginatedResponse(path, params, self)
         else:
             resp = self._make_request(method, path, params, data, **kwargs)
             resp = convert_response_data_type(resp,
                                               return_type=self._return_type)
-            return resp
+        self._client.last_response = resp
+        return resp
 
 
 class CivisAsyncResultBase(futures.Future):
diff --git a/civis/civis.py b/civis/civis.py
index f2c252a..317f86e 100644
--- a/civis/civis.py
+++ b/civis/civis.py
@@ -351,6 +351,7 @@ class APIClient(MetaMixin):
         session_auth_key = get_api_key(api_key)
         self._session_kwargs = {'api_key': session_auth_key,
                                 'max_retries': retry_total}
+        self.last_response = None
 
         # Catch deprecation warnings from generate_classes_maybe_cached and
         # the functions it calls until the `resources` argument is removed.
@@ -364,7 +365,8 @@ class APIClient(MetaMixin):
                                                     api_version,
                                                     resources)
         for class_name, cls in classes.items():
-            setattr(self, class_name, cls(self._session_kwargs, return_type))
+            setattr(self, class_name, cls(self._session_kwargs, client=self,
+                                          return_type=return_type))
 
     @property
     def feature_flags(self):


Test patch:
diff --git a/civis/tests/test_base.py b/civis/tests/test_base.py
index e788cbe..527a539 100644
--- a/civis/tests/test_base.py
+++ b/civis/tests/test_base.py
@@ -17,6 +17,18 @@ def test_base_url_from_env():
 @mock.patch('civis.base.get_base_url', return_value='https://base.api.url/')
 def test_endpoint_base_url(mock_get_base_url):
     session = mock.MagicMock(spec=requests.Session)
-    endpoint = Endpoint(session)
+    endpoint = Endpoint(session, 'client')
 
     assert endpoint._base_url == 'https://base.api.url/'
+
+
+def test_store_last_response():
+    mock_client = mock.Mock()
+    endpoint = Endpoint({}, client=mock_client, return_type='raw')
+
+    returned_resp = {'value': 'response'}
+    endpoint._make_request = mock.Mock(return_value=returned_resp)
+
+    resp = endpoint._call_api('GET')
+    assert resp == returned_resp
+    assert mock_client.last_response is resp