From dd486e5d0424945ea5dd9dde7d1ee598c416b3a6 Mon Sep 17 00:00:00 2001 From: Chris Borrill Date: Fri, 20 Jun 2014 20:36:32 +1200 Subject: [PATCH 1/9] Add heartbeat function to keep connection alive if there is no data to stream within the 60 second window. --- plotly/plotly/plotly.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/plotly/plotly/plotly.py b/plotly/plotly/plotly.py index eb4a56a30e7..37d9cb0ff38 100644 --- a/plotly/plotly/plotly.py +++ b/plotly/plotly/plotly.py @@ -405,6 +405,14 @@ def open(self): 80, {'Host': streaming_url, 'plotly-streamtoken': self.stream_id}) + + def heartbeat(self, reconnect_on=(200, '', 408)): + try: + self._stream.write("\n", reconnect_on=reconnect_on) + except AttributeError: + raise exceptions.PlotlyError("Stream has not been opened yet, " + "cannot write to a closed connection. " + "Call `open()` on the stream to open the stream.") def write(self, data, layout=None, validate=True, reconnect_on=(200, '', 408)): From cd5bea20282ca4574c012fe5cbcc88916402dd6d Mon Sep 17 00:00:00 2001 From: Chris Borrill Date: Fri, 20 Jun 2014 22:28:19 +1200 Subject: [PATCH 2/9] Add comment to heartbeat function --- plotly/plotly/plotly.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plotly/plotly/plotly.py b/plotly/plotly/plotly.py index 37d9cb0ff38..65d4a74ac45 100644 --- a/plotly/plotly/plotly.py +++ b/plotly/plotly/plotly.py @@ -407,6 +407,8 @@ def open(self): 'plotly-streamtoken': self.stream_id}) def heartbeat(self, reconnect_on=(200, '', 408)): + """ Write a heartbeat to keep the connection alive if there is no data to stream within the 60 second window. + """ try: self._stream.write("\n", reconnect_on=reconnect_on) except AttributeError: From 4d502363105d008b9ffcfa87649ff156c701e7c5 Mon Sep 17 00:00:00 2001 From: Chris Borrill Date: Sat, 21 Jun 2014 19:10:55 +1200 Subject: [PATCH 3/9] Fix indentation problem --- plotly/plotly/plotly.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plotly/plotly/plotly.py b/plotly/plotly/plotly.py index 65d4a74ac45..0d4779450af 100644 --- a/plotly/plotly/plotly.py +++ b/plotly/plotly/plotly.py @@ -407,7 +407,8 @@ def open(self): 'plotly-streamtoken': self.stream_id}) def heartbeat(self, reconnect_on=(200, '', 408)): - """ Write a heartbeat to keep the connection alive if there is no data to stream within the 60 second window. + """ Write a heartbeat to keep the connection alive if there is no data to + stream within the 60 second window. """ try: self._stream.write("\n", reconnect_on=reconnect_on) From 669fd8f02565ab979445a602f72f958bfa9fb293 Mon Sep 17 00:00:00 2001 From: Chris Borrill Date: Sat, 21 Jun 2014 19:19:38 +1200 Subject: [PATCH 4/9] Replace tab with four spaces --- plotly/plotly/plotly.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plotly/plotly/plotly.py b/plotly/plotly/plotly.py index 0d4779450af..a7b7a9c860a 100644 --- a/plotly/plotly/plotly.py +++ b/plotly/plotly/plotly.py @@ -405,11 +405,11 @@ def open(self): 80, {'Host': streaming_url, 'plotly-streamtoken': self.stream_id}) - + def heartbeat(self, reconnect_on=(200, '', 408)): - """ Write a heartbeat to keep the connection alive if there is no data to - stream within the 60 second window. - """ + """ Write a heartbeat to keep the connection alive if there is no data to + stream within the 60 second window. + """ try: self._stream.write("\n", reconnect_on=reconnect_on) except AttributeError: From 327011437d7949ec17da40aed86afbce50b5c1a7 Mon Sep 17 00:00:00 2001 From: Chris Borrill Date: Wed, 25 Jun 2014 20:41:30 +1200 Subject: [PATCH 5/9] Add heartbeat() function to chunked_request --- .../chunked_requests/chunked_request.py | 46 +++++++++++++++++++ plotly/plotly/plotly.py | 2 +- 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/plotly/plotly/chunked_requests/chunked_request.py b/plotly/plotly/chunked_requests/chunked_request.py index 9fbe4f59dae..1e76737902b 100644 --- a/plotly/plotly/chunked_requests/chunked_request.py +++ b/plotly/plotly/chunked_requests/chunked_request.py @@ -66,6 +66,52 @@ def write(self, data, reconnect_on=('', 200, )): self._reconnect() self.write(data) + def heartbeat(self, reconnect_on=('', 200, )): + ''' Send `\n` to the server + Check the connection before writing and reconnect + if disconnected and if the response status code is in `reconnect_on`. + + The response may either be an HTTPResponse object or an empty string. + ''' + + if not self._isconnected(): + + # Attempt to get the response. + response = self._getresponse() + + # Reconnect depending on the status code. + if ((response == '' and '' in reconnect_on) or + (response and isinstance(response, httplib.HTTPResponse) and + response.status in reconnect_on)): + self._reconnect() + + elif response and isinstance(response, httplib.HTTPResponse): + # If an HTTPResponse was recieved then + # make the users aware instead of + # auto-reconnecting in case the + # server is responding with an important + # message that might prevent + # future requests from going through, + # like Invalid Credentials. + # This allows the user to determine when + # to reconnect. + raise Exception("Server responded with " + "status code: {status_code}\n" + "and message: {msg}." + .format(status_code=response.status, + msg=response.read())) + + elif response == '': + raise Exception("Attempted to write but socket " + "was not connected.") + + try: + # Send the \n + self._conn.send('\n') + except httplib.socket.error: + self._reconnect() + self.heartbeat() + def _connect(self): ''' Initialize an HTTP connection with chunked Transfer-Encoding to server:port with optional headers. diff --git a/plotly/plotly/plotly.py b/plotly/plotly/plotly.py index a7b7a9c860a..8e986f0644a 100644 --- a/plotly/plotly/plotly.py +++ b/plotly/plotly/plotly.py @@ -411,7 +411,7 @@ def heartbeat(self, reconnect_on=(200, '', 408)): stream within the 60 second window. """ try: - self._stream.write("\n", reconnect_on=reconnect_on) + self._stream.heartbeat(reconnect_on=reconnect_on) except AttributeError: raise exceptions.PlotlyError("Stream has not been opened yet, " "cannot write to a closed connection. " From 46ceb7d76e4d22e5b9dccb1933b2f1149e9efc00 Mon Sep 17 00:00:00 2001 From: Chris Borrill Date: Wed, 25 Jun 2014 21:40:10 +1200 Subject: [PATCH 6/9] Refactor write() and heartbeat() to use new verifyConnection() method --- .../chunked_requests/chunked_request.py | 66 ++++++------------- 1 file changed, 20 insertions(+), 46 deletions(-) diff --git a/plotly/plotly/chunked_requests/chunked_request.py b/plotly/plotly/chunked_requests/chunked_request.py index 1e76737902b..45a3c275ef6 100644 --- a/plotly/plotly/chunked_requests/chunked_request.py +++ b/plotly/plotly/chunked_requests/chunked_request.py @@ -18,43 +18,11 @@ def __init__(self, server, port=80, headers={}): self._connect() def write(self, data, reconnect_on=('', 200, )): - ''' Send `data` to the server in chunk-encoded form. - Check the connection before writing and reconnect - if disconnected and if the response status code is in `reconnect_on`. - - The response may either be an HTTPResponse object or an empty string. + ''' Send `data` to the server in chunk-encoded form, verifying + the connection is still open before doing so. ''' - - if not self._isconnected(): - - # Attempt to get the response. - response = self._getresponse() - - # Reconnect depending on the status code. - if ((response == '' and '' in reconnect_on) or - (response and isinstance(response, httplib.HTTPResponse) and - response.status in reconnect_on)): - self._reconnect() - - elif response and isinstance(response, httplib.HTTPResponse): - # If an HTTPResponse was recieved then - # make the users aware instead of - # auto-reconnecting in case the - # server is responding with an important - # message that might prevent - # future requests from going through, - # like Invalid Credentials. - # This allows the user to determine when - # to reconnect. - raise Exception("Server responded with " - "status code: {status_code}\n" - "and message: {msg}." - .format(status_code=response.status, - msg=response.read())) - - elif response == '': - raise Exception("Attempted to write but socket " - "was not connected.") + + _verifyConnection(reconnect_on) try: msg = data @@ -67,9 +35,22 @@ def write(self, data, reconnect_on=('', 200, )): self.write(data) def heartbeat(self, reconnect_on=('', 200, )): - ''' Send `\n` to the server - Check the connection before writing and reconnect - if disconnected and if the response status code is in `reconnect_on`. + ''' Send `\n` to the server, verifying the connection is still + open before doing so. + ''' + + _verifyConnection(reconnect_on) + + try: + # Send the \n + self._conn.send('\n') + except httplib.socket.error: + self._reconnect() + self.heartbeat() + + def _verifyConnection(self, reconnect_on): + ''' Check the connection, reconnect if disconnected and if the + response status code is in `reconnect_on`. The response may either be an HTTPResponse object or an empty string. ''' @@ -105,13 +86,6 @@ def heartbeat(self, reconnect_on=('', 200, )): raise Exception("Attempted to write but socket " "was not connected.") - try: - # Send the \n - self._conn.send('\n') - except httplib.socket.error: - self._reconnect() - self.heartbeat() - def _connect(self): ''' Initialize an HTTP connection with chunked Transfer-Encoding to server:port with optional headers. From bd8c6bf5258f9e993a70630e78b077a7a4f06dc7 Mon Sep 17 00:00:00 2001 From: Chris Borrill Date: Thu, 26 Jun 2014 19:11:48 +1200 Subject: [PATCH 7/9] Add missing ref to self in call to verifyConnection() --- plotly/plotly/chunked_requests/chunked_request.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plotly/plotly/chunked_requests/chunked_request.py b/plotly/plotly/chunked_requests/chunked_request.py index 45a3c275ef6..d5239e08a97 100644 --- a/plotly/plotly/chunked_requests/chunked_request.py +++ b/plotly/plotly/chunked_requests/chunked_request.py @@ -22,7 +22,7 @@ def write(self, data, reconnect_on=('', 200, )): the connection is still open before doing so. ''' - _verifyConnection(reconnect_on) + self._verifyConnection(reconnect_on) try: msg = data @@ -39,7 +39,7 @@ def heartbeat(self, reconnect_on=('', 200, )): open before doing so. ''' - _verifyConnection(reconnect_on) + self._verifyConnection(reconnect_on) try: # Send the \n From ea493720f2e5808040819dd38245c4e35e00471a Mon Sep 17 00:00:00 2001 From: Chris Borrill Date: Thu, 26 Jun 2014 21:12:13 +1200 Subject: [PATCH 8/9] Add documentation of heartbeat() function to readme --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 24ba2f5a4f4..db03c4cd0c1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,10 @@ Plotly's Python-API =================== +#### Functionality I Have Added + +heartbeat() function - call this when want to keep the connection alive, but do not want to send data within the 60s window. + #### Shareable, interactive, publication-quality plots in your web browser. [![Contour subplots in Python](http://i.imgur.com/9QKmUQb.png)](https://plot.ly/~test-runner/10) From 62c5ee19c096f5ddb68cb769a9ab9f59acbd8390 Mon Sep 17 00:00:00 2001 From: Chris Borrill Date: Thu, 26 Jun 2014 21:13:44 +1200 Subject: [PATCH 9/9] Correct wording --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index db03c4cd0c1..0f736e563fa 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Plotly's Python-API #### Functionality I Have Added -heartbeat() function - call this when want to keep the connection alive, but do not want to send data within the 60s window. +heartbeat() - call this function when you want to keep the connection alive, but do not want to send data within the 60s window. #### Shareable, interactive, publication-quality plots in your web browser.