From cf3d08658b1857147645421ae4b536b06736f6e4 Mon Sep 17 00:00:00 2001 From: Stephen Jung Date: Wed, 10 Apr 2019 12:51:19 -0700 Subject: [PATCH 1/3] Fix httmock dependency setup.py parses everything in requirements.txt and adds it to install_requires, so httmock was being pulled into the package dependencies even though it's only needed for tests. (Also, it's spelled "tests_require", not "test_requires".) --- requirements-test.txt | 3 +++ requirements.txt | 1 - setup.py | 2 +- tox.ini | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) create mode 100644 requirements-test.txt diff --git a/requirements-test.txt b/requirements-test.txt new file mode 100644 index 0000000..b06201c --- /dev/null +++ b/requirements-test.txt @@ -0,0 +1,3 @@ +-r requirements.txt + +httmock diff --git a/requirements.txt b/requirements.txt index 416d575..928b711 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,4 @@ certifi -httmock protobuf>=3.0.0 pyformance>=0.3.1 requests>=2.7.0 diff --git a/setup.py b/setup.py index 4b15db8..7e1c539 100644 --- a/setup.py +++ b/setup.py @@ -26,8 +26,8 @@ long_description=long_description, zip_safe=True, packages=find_packages(), - test_requires=test_requirements, install_requires=requirements, + tests_require=test_requirements, classifiers=[ 'Operating System :: OS Independent', 'Programming Language :: Python', diff --git a/tox.ini b/tox.ini index 40299e2..e1b158d 100644 --- a/tox.ini +++ b/tox.ini @@ -8,7 +8,7 @@ envlist = py27,py35,flake8 [testenv] usedevelop = True passenv = ORG_NAME_FOR_SIGNALFX_API_TOKEN_IS_SFx_Test SIGNALFX_API_TOKEN -deps = -r{toxinidir}/requirements.txt +deps = -r{toxinidir}/requirements-test.txt ignore_errors = True commands = python {toxinidir}/tests/test_data_reporting.py python {toxinidir}/tests/live_tests.py --metric_name 'MET' --tag_name 'TG' --key 'K' --value 'V' From 65356d2d95dc84b477aed1c391001d6f64393aa9 Mon Sep 17 00:00:00 2001 From: Stephen Jung Date: Wed, 10 Apr 2019 12:24:47 -0700 Subject: [PATCH 2/3] Add support for proxied SSE signalflow execution --- signalfx/signalflow/__init__.py | 5 +++-- signalfx/signalflow/sse.py | 10 ++++++++-- signalfx/signalflow/ws.py | 6 +++++- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/signalfx/signalflow/__init__.py b/signalfx/signalflow/__init__.py index 7c75942..fe058f4 100644 --- a/signalfx/signalflow/__init__.py +++ b/signalfx/signalflow/__init__.py @@ -16,8 +16,9 @@ class SignalFlowClient(object): def __init__(self, token, endpoint=constants.DEFAULT_STREAM_ENDPOINT, timeout=constants.DEFAULT_TIMEOUT, transport=ws.WebSocketTransport, - compress=True): - self._transport = transport(token, endpoint, timeout, compress) + compress=True, proxy_url=None): + self._transport = transport(token, endpoint, timeout, compress, + proxy_url) self._computations = set([]) def __enter__(self): diff --git a/signalfx/signalflow/sse.py b/signalfx/signalflow/sse.py index fe5dae0..66763cf 100644 --- a/signalfx/signalflow/sse.py +++ b/signalfx/signalflow/sse.py @@ -24,7 +24,8 @@ class SSETransport(transport._SignalFlowTransport): _SIGNALFLOW_ENDPOINT = 'v2/signalflow' def __init__(self, token, endpoint=constants.DEFAULT_STREAM_ENDPOINT, - timeout=constants.DEFAULT_TIMEOUT, compress=True): + timeout=constants.DEFAULT_TIMEOUT, compress=True, + proxy_url=None): super(SSETransport, self).__init__(token, endpoint, timeout) pool_args = { 'url': self._endpoint, @@ -43,7 +44,12 @@ def __init__(self, token, endpoint=constants.DEFAULT_STREAM_ENDPOINT, 'ca_certs': certifi.where() # Path to the Certifi bundle. }) - self._http = urllib3.connectionpool.connection_from_url(**pool_args) + if proxy_url: + proxy_manager = urllib3.poolmanager.proxy_from_url(proxy_url) + endpoint = pool_args.pop('url') + self._http = proxy_manager.connection_from_url(endpoint, pool_kwargs=pool_args) + else: + self._http = urllib3.connectionpool.connection_from_url(**pool_args) def __str__(self): return 'sse+{0}'.format(self._endpoint) diff --git a/signalfx/signalflow/ws.py b/signalfx/signalflow/ws.py index f551374..f2e0611 100644 --- a/signalfx/signalflow/ws.py +++ b/signalfx/signalflow/ws.py @@ -29,7 +29,11 @@ class WebSocketTransport(transport._SignalFlowTransport, WebSocketClient): _SIGNALFLOW_WEBSOCKET_ENDPOINT = 'v2/signalflow/connect' def __init__(self, token, endpoint=constants.DEFAULT_STREAM_ENDPOINT, - timeout=constants.DEFAULT_TIMEOUT, compress=True): + timeout=constants.DEFAULT_TIMEOUT, compress=True, + proxy_url=None): + if proxy_url: + raise NotImplementedError('Websocket transport cannot be proxied!') + ws_endpoint = '{0}/{1}'.format( endpoint.replace('http', 'ws', 1), WebSocketTransport._SIGNALFLOW_WEBSOCKET_ENDPOINT) From 40ac739075cb4651b71fd461ebfbdd288fb6a209 Mon Sep 17 00:00:00 2001 From: Stephen Jung Date: Tue, 16 Apr 2019 16:10:58 -0700 Subject: [PATCH 3/3] Handle all keys in JSON execute error response --- signalfx/signalflow/errors.py | 16 +++++++++++++--- signalfx/signalflow/sse.py | 3 ++- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/signalfx/signalflow/errors.py b/signalfx/signalflow/errors.py index 6827c2b..b0617c4 100644 --- a/signalfx/signalflow/errors.py +++ b/signalfx/signalflow/errors.py @@ -5,9 +5,10 @@ class SignalFlowException(Exception): """A generic error encountered when interacting with the SignalFx SignalFlow API.""" - def __init__(self, code, message=None): + def __init__(self, code, message=None, error_type=None): self._code = code self._message = message + self._error_type = error_type @property def code(self): @@ -19,10 +20,19 @@ def message(self): """Returns an optional error message attached to this error.""" return self._message + @property + def error_type(self): + """Returns an optional error type attached to this error.""" + return self._error_type + def __str__(self): + err = self._code + if self._error_type: + err = '{0} ({1})'.format(self._code, self._error_type) + if self._message: - return '{0}: {1}'.format(self._code, self._message) - return 'Error {0}'.format(self._code) + return '{0}: {1}'.format(err, self._message) + return 'Error {0}'.format(err) class ComputationAborted(Exception): diff --git a/signalfx/signalflow/sse.py b/signalfx/signalflow/sse.py index 66763cf..f3dc6ae 100644 --- a/signalfx/signalflow/sse.py +++ b/signalfx/signalflow/sse.py @@ -64,7 +64,8 @@ def _post(self, url, fields=None, body=None): if r.status != 200: try: if r.headers['Content-Type'] == 'application/json': - raise errors.SignalFlowException(**json.loads(r.read())) + rbody = json.loads(r.read()) + raise errors.SignalFlowException(r.status, rbody.get('message'), rbody.get('errorType')) raise errors.SignalFlowException(r.status) finally: r.close()