From 3bf7b002f7744ff45136e592d7bc3c50d77b06d2 Mon Sep 17 00:00:00 2001 From: Eli Dickinson Date: Thu, 15 Mar 2018 16:46:37 -0400 Subject: [PATCH 1/5] custom timeout for http requests TECH-3849 --- dive_sailthru_client/client.py | 36 +++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/dive_sailthru_client/client.py b/dive_sailthru_client/client.py index 30ca13f..ad1c023 100644 --- a/dive_sailthru_client/client.py +++ b/dive_sailthru_client/client.py @@ -1,8 +1,12 @@ -from sailthru.sailthru_client import SailthruClient +from sailthru import sailthru_client from errors import SailthruApiError # We need the SailthruClientError to be able to handle retries in api_get from sailthru.sailthru_error import SailthruClientError - +# for patched_sailthru_http_request +from sailthru.sailthru_response import SailthruResponse +import requests +import platform +# other libraries import datetime import time import re @@ -24,7 +28,33 @@ class DiveEmailTypes: Spotlight = "spotlight" -class DiveSailthruClient(SailthruClient): +# The following function overrides upstream client behavior to add custom HTTP timeout per TECH-3849 +def timeout_patched_sailthru_http_request(url, data, method, file_data=None, timeout=60): + """ + Perform an HTTP GET / POST / DELETE request + + This is a override of upstream sailthru_http_request with `timeout` added as a parameter and + with the default set to 60 instead of 10. + """ + data = sailthru_client.flatten_nested_hash(data) + method = method.upper() + params, data = (None, data) if method == 'POST' else (data, None) + + try: + headers = {'User-Agent': 'Sailthru API Python Client %s; Python Version: %s' % ('2.3.3-patched', platform.python_version())} + response = requests.request(method, url, params=params, data=data, files=file_data, headers=headers, timeout=timeout) + return SailthruResponse(response) + except requests.HTTPError as e: + raise SailthruClientError(str(e)) + except requests.RequestException as e: + raise SailthruClientError(str(e)) + + +# Now override the sailthru_http_request utility function in the superclass +sailthru_client.sailthru_http_request = timeout_patched_sailthru_http_request + + +class DiveSailthruClient(sailthru_client.SailthruClient): """ Our Sailthru client implementation that adds our own concepts. From 003f76d0188ed92e3e547751653bdb4f89457b82 Mon Sep 17 00:00:00 2001 From: Eli Dickinson Date: Thu, 15 Mar 2018 16:53:52 -0400 Subject: [PATCH 2/5] oops missed a reference --- dive_sailthru_client/client.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dive_sailthru_client/client.py b/dive_sailthru_client/client.py index ad1c023..d352826 100644 --- a/dive_sailthru_client/client.py +++ b/dive_sailthru_client/client.py @@ -4,6 +4,7 @@ from sailthru.sailthru_error import SailthruClientError # for patched_sailthru_http_request from sailthru.sailthru_response import SailthruResponse +from sailthru.sailthru_http import flatten_nested_hash import requests import platform # other libraries @@ -36,7 +37,7 @@ def timeout_patched_sailthru_http_request(url, data, method, file_data=None, tim This is a override of upstream sailthru_http_request with `timeout` added as a parameter and with the default set to 60 instead of 10. """ - data = sailthru_client.flatten_nested_hash(data) + data = flatten_nested_hash(data) method = method.upper() params, data = (None, data) if method == 'POST' else (data, None) From ea775da8cb559a54109c0f5a0fc2e54a165a224e Mon Sep 17 00:00:00 2001 From: Eli Dickinson Date: Fri, 16 Mar 2018 16:22:15 -0400 Subject: [PATCH 3/5] add comments and clean up for TECH-3849 --- dive_sailthru_client/client.py | 13 ++++++++++--- tox.ini | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/dive_sailthru_client/client.py b/dive_sailthru_client/client.py index d352826..3f13ea4 100644 --- a/dive_sailthru_client/client.py +++ b/dive_sailthru_client/client.py @@ -29,7 +29,10 @@ class DiveEmailTypes: Spotlight = "spotlight" -# The following function overrides upstream client behavior to add custom HTTP timeout per TECH-3849 +# There is some skullduggery below in order to override the hardcoded 10 second timeout on HTTP requests +# per TECH-3849. First we copy/paste the sailthru_http_request() function that originally exists here: +# https://github.com/sailthru/sailthru-python-client/blob/521fdaa30890a29da8fbb02726e7d22ed174b878/sailthru/sailthru_http.py#L30 +# Then we modify it to have a default timeout of 60 seconds and additionally to accept a timeout parameter def timeout_patched_sailthru_http_request(url, data, method, file_data=None, timeout=60): """ Perform an HTTP GET / POST / DELETE request @@ -51,11 +54,15 @@ def timeout_patched_sailthru_http_request(url, data, method, file_data=None, tim raise SailthruClientError(str(e)) -# Now override the sailthru_http_request utility function in the superclass +# Now we need to patch the altered sailthru_http_request into a place where even functions defined in the upstream +# SailthruClient class that call it will call our new altered version. In the upstream class, the sailthru_http_request() +# function is `import`ed into the sailthru_client module (not the class), so we need to import the sailthru_client module +# and then redefine the sailthru_http_request that it had imported to instead point to our version. Then later we +# have to make sure we are subclassing SailthruClient by refering to it specifically as sailthru_client.SailthruClient sailthru_client.sailthru_http_request = timeout_patched_sailthru_http_request -class DiveSailthruClient(sailthru_client.SailthruClient): +class DiveSailthruClient(sailthru_client.SailthruClient): # must import from sailthru_client.SailthruClient for patched HTTP timeout """ Our Sailthru client implementation that adds our own concepts. diff --git a/tox.ini b/tox.ini index 519dbce..6af7d9f 100644 --- a/tox.ini +++ b/tox.ini @@ -1,4 +1,4 @@ [flake8] -max-line-length = 128 +max-line-length = 140 max-complexity = 12 statistics = True From df192d141713e297009ba2577a06ad2473118eda Mon Sep 17 00:00:00 2001 From: Eli Dickinson Date: Fri, 16 Mar 2018 16:24:04 -0400 Subject: [PATCH 4/5] bump version to 0.0.16 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index ee667d1..cf4f866 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ setup( name="dive_sailthru_client", - version="0.0.15", + version="0.0.16", description="Industry Dive abstraction of the Sailthru API client", author='Industry Dive', author_email='tech.team@industrydive.com', From 9f51e96a963e7a073e89b7d3241ab298b71a33c6 Mon Sep 17 00:00:00 2001 From: Eli Dickinson Date: Mon, 19 Mar 2018 13:32:04 -0400 Subject: [PATCH 5/5] add sleep() to make tests pass --- dive_sailthru_client/tests/test_integration.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dive_sailthru_client/tests/test_integration.py b/dive_sailthru_client/tests/test_integration.py index 3ece272..3ec41de 100644 --- a/dive_sailthru_client/tests/test_integration.py +++ b/dive_sailthru_client/tests/test_integration.py @@ -5,6 +5,7 @@ import datetime import tempfile import StringIO +import time @attr('external') @@ -37,6 +38,10 @@ def test_get_set_var(self): value = self._get_user_var(self.test_email, self.test_var_key) self.assertNotEqual(value, new_value) self._set_user_var(self.test_email, self.test_var_key, new_value) + # We take a brief pause here to let Sailthru catch up. Sailthru API calls + # are only *eventually* consistent so sometimes you write a value and then + # read it back and still get the old value. + time.sleep(1) value = self._get_user_var(self.test_email, self.test_var_key) self.assertEqual(value, new_value)