Plotly's real-time streaming API accepts chunked-encoded HTTP requests.
import chunked_requests
import time
import json
# Initialize a streaming plot with Plotly's REST API
import plotly.plotly as py
from plotly.graph_objs import Scatter, Stream
py.sign_in('etpinard', "a35m7g6el5")
token = "o72o1p08y4"
py.iplot([Scatter(x=[], y=[],
stream=Stream(token=token))],
filename='chunked-request quickstart!')
# OK, let's initialize a connection to plotly's streaming servers at https://stream.plot.ly
# Plotly requires a special header: "plotly-streamtoken"
our_stream = chunked_requests.Stream('stream.plot.ly',
port=80,
headers={'plotly-streamtoken': token})
# Now, let's write some data. Plotly accepts new-line separated JSON objects
our_stream.write(json.dumps({'x': 1, 'y': 2})+'\n')
--------------------------------------------------------------------------- error Traceback (most recent call last) <ipython-input-8-36076ea7fa99> in <module>() 1 # Now, let's write some data. Plotly accepts new-line separated JSON objects ----> 2 our_stream.write(json.dumps({'x': 1, 'y': 2})+'\n') /home/etienne/plotly/plotly-misc-nb/chunked_request/chunked_requests.py in write(self, data, reconnect_on) 26 ''' 27 ---> 28 if not self._isconnected(): 29 30 # Attempt to get the response. /home/etienne/plotly/plotly-misc-nb/chunked_request/chunked_requests.py in _isconnected(self) 192 else: 193 # Unknown scenario --> 194 raise e 195 196 def _reconnect(self): error: [Errno 11] Resource temporarily unavailable
# If we're all good, then we just saw a point appear in our plot above! Let's send another to make a line:
our_stream.write(json.dumps({'x': 2, 'y': 3})+'\n')
--------------------------------------------------------------------------- error Traceback (most recent call last) <ipython-input-9-7a05b84c9221> in <module>() 1 # If we're all good, then we just saw a point appear in our plot above! Let's send another to make a line: ----> 2 our_stream.write(json.dumps({'x': 2, 'y': 3})+'\n') /home/etienne/plotly/plotly-misc-nb/chunked_request/chunked_requests.py in write(self, data, reconnect_on) 26 ''' 27 ---> 28 if not self._isconnected(): 29 30 # Attempt to get the response. /home/etienne/plotly/plotly-misc-nb/chunked_request/chunked_requests.py in _isconnected(self) 192 else: 193 # Unknown scenario --> 194 raise e 195 196 def _reconnect(self): error: [Errno 11] Resource temporarily unavailable
# How fun!
our_stream.write(json.dumps({'x': 3, 'y': 3})+'\n')
--------------------------------------------------------------------------- error Traceback (most recent call last) <ipython-input-10-c3dd84df8c20> in <module>() 1 # How fun! ----> 2 our_stream.write(json.dumps({'x': 3, 'y': 3})+'\n') /home/etienne/plotly/plotly-misc-nb/chunked_request/chunked_requests.py in write(self, data, reconnect_on) 26 ''' 27 ---> 28 if not self._isconnected(): 29 30 # Attempt to get the response. /home/etienne/plotly/plotly-misc-nb/chunked_request/chunked_requests.py in _isconnected(self) 192 else: 193 # Unknown scenario --> 194 raise e 195 196 def _reconnect(self): error: [Errno 11] Resource temporarily unavailable
response = our_stream.close()
response
''
Incredibly, that is actually the correct response (see http://stackoverflow.com/questions/7174927/when-does-socket-recvrecv-size-return) - instead of receiving a 200, we just receive an empty string!
# Create a new connection and plot
py.iplot([Scatter(x=[], y=[], stream=Stream(token=token))], filename='4xx - timeouts')
our_stream = chunked_requests.Stream('stream.plot.ly', port=80, headers={'plotly-streamtoken': token})
our_stream.write(json.dumps({'x': 1, 'y': 1})+'\n') # Should work - a point should appear in the plot above
time.sleep(70) # 70 secs
our_stream.write(json.dumps({'x': 1, 'y': 2})+'\n') # might fail
Hm... that worked. Let's try a little bit longer... 2 minutes.
# Create a new connection and plot
py.iplot([Scatter(x=[], y=[], stream=Stream(token=token))], filename='4xx - 2 minute timeout')
time.sleep(5)
our_stream = chunked_requests.Stream('stream.plot.ly', port=80, headers={'plotly-streamtoken': token})
our_stream.write(json.dumps({'x': 1, 'y': 1})+'\n') # Should work - a point should appear in the plot above
our_stream.write(json.dumps({'x': 1, 'y': 2})+'\n') # Should work - a point should appear in the plot above
our_stream.write(json.dumps({'x': 1, 'y': 3})+'\n') # Should work - a point should appear in the plot above
our_stream.write(json.dumps({'x': 1, 'y': 4})+'\n') # Should work - a point should appear in the plot above
our_stream.write(json.dumps({'x': 1, 'y': 5})+'\n') # Should work - a point should appear in the plot above
our_stream.write(json.dumps({'x': 1, 'y': 6})+'\n') # Should work - a point should appear in the plot above
time.sleep(120) # 120 secs
i = 1
while i<20:
i += 1
our_stream.write(json.dumps({'x': i, 'y': 6})+'\n') # Might fail
time.sleep(1)
--------------------------------------------------------------------------- Exception Traceback (most recent call last) <ipython-input-12-ecee3ceb2b54> in <module>() 13 while i<20: 14 i += 1 ---> 15 our_stream.write(json.dumps({'x': i, 'y': 6})+'\n') # Might fail 16 time.sleep(1) /Users/chris/plotlygithub/chunked-requests/chunked_requests/chunked_request.py in write(self, data, reconnect_on) 51 "and message: {msg}." 52 .format(status_code=response.status, ---> 53 msg=response.read())) 54 55 elif response == '': Exception: Server responded with status code: 408 and message: timeout on active data.
Cool, in this case we received a timeout. However, in other cases I'ved noted that the connection is still open, and although the points don't show up in the iframe, they do appear if you re-load -- perhaps the web-socket connection closed!
Let's go for this connection timeout though and try waiting a bit longer... 2 minutes + 20 seconds
# Create a new connection and plot
py.iplot([Scatter(x=[], y=[], stream=Stream(token=token))], filename='4xx - 3 minute timeout')
time.sleep(5)
our_stream = chunked_requests.Stream('stream.plot.ly', port=80, headers={'plotly-streamtoken': token})
our_stream.write(json.dumps({'x': 1, 'y': 1})+'\n') # Should work - a point should appear in the plot above
time.sleep(140) # 2 minutes + 20 seconds
our_stream.write(json.dumps({'x': 1, 'y': 2})+'\n') # Should fail
--------------------------------------------------------------------------- Exception Traceback (most recent call last) <ipython-input-13-45a96a97d8e2> in <module>() 5 our_stream.write(json.dumps({'x': 1, 'y': 1})+'\n') # Should work - a point should appear in the plot above 6 time.sleep(140) # 2 minutes + 20 seconds ----> 7 our_stream.write(json.dumps({'x': 1, 'y': 2})+'\n') # Should fail /Users/chris/plotlygithub/chunked-requests/chunked_requests/chunked_request.py in write(self, data, reconnect_on) 51 "and message: {msg}." 52 .format(status_code=response.status, ---> 53 msg=response.read())) 54 55 elif response == '': Exception: Server responded with status code: 408 and message: timeout on active data.
Nice, a 408: Timeout on active data.
Now let's initialize our stream to automatically reconnect on 408s.
To do this, inlude 408
in the reconnect_on tuple in the write
function, i.e.
stream.write(data, reconnect_on=(200, 408))
# Create a new connection and plot
py.iplot([Scatter(x=[], y=[], stream=Stream(token=token))], filename='reconnect on 408')
time.sleep(5)
our_stream = chunked_requests.Stream('stream.plot.ly', port=80, headers={'plotly-streamtoken': token})
our_stream.write(json.dumps({'x': 1, 'y': 1})+'\n') # Should work - a point should appear in the plot above
time.sleep(140) # 2 minutes + 20 sec
our_stream.write(json.dumps({'x': 1, 'y': 2})+'\n',
reconnect_on=(200, '', 408)) # Should reconnect, then plot.
Nice!
The second point didn't actually show up in this iframe (websocket closed?) but it does show up on re-loads, so it musta worked!
bad_token = 'some_random_string'
our_stream = chunked_requests.Stream(
'stream.plot.ly',
port=80,
headers={'plotly-streamtoken': bad_token})
time.sleep(1) # give 'er some time
our_stream.write(json.dumps({'x': 1, 'y': 1})+'\n') # Should fail with a 404
--------------------------------------------------------------------------- Exception Traceback (most recent call last) <ipython-input-17-832e0292c302> in <module>() 5 headers={'plotly-streamtoken': bad_token}) 6 time.sleep(1) # give 'er some time ----> 7 our_stream.write(json.dumps({'x': 1, 'y': 1})+'\n') # Should fail with a 404 /Users/chris/plotlygithub/chunked-requests/chunked_requests/chunked_request.py in write(self, data, reconnect_on) 51 "and message: {msg}." 52 .format(status_code=response.status, ---> 53 msg=response.read())) 54 55 elif response == '': Exception: Server responded with status code: 404 and message: streamtoken not registered or valid.
Fantastic!
our_stream = chunked_requests.Stream('stream.plot.ly', port=80) # don't include the header
time.sleep(1) # give 'er some time
our_stream.write(json.dumps({'x': 1, 'y': 1})+'\n') # Should fail with a 406
--------------------------------------------------------------------------- Exception Traceback (most recent call last) <ipython-input-18-10a90a994921> in <module>() 1 our_stream = chunked_requests.Stream('stream.plot.ly', port=80) # don't include the header 2 time.sleep(1) # give 'er some time ----> 3 our_stream.write(json.dumps({'x': 1, 'y': 1})+'\n') # Should fail with a 404 /Users/chris/plotlygithub/chunked-requests/chunked_requests/chunked_request.py in write(self, data, reconnect_on) 51 "and message: {msg}." 52 .format(status_code=response.status, ---> 53 msg=response.read())) 54 55 elif response == '': Exception: Server responded with status code: 406 and message: bad request header: missing 'plotly-streamtoken'.
Sweet.
Let's see how much junk we can send plotly before it tells us to stawp
our_stream = chunked_requests.Stream(
'stream.plot.ly',
port=80,
headers={'plotly-streamtoken': token})
time.sleep(1) # give 'er some time
n_messages = 0
while True:
our_stream.write(json.dumps({'x': 1, 'y': 1}))
n_messages += 1
time.sleep(50./1000)
--------------------------------------------------------------------------- KeyboardInterrupt Traceback (most recent call last) <ipython-input-19-d33dfc47170a> in <module>() 8 our_stream.write(json.dumps({'x': 1, 'y': 1})) 9 n_messages += 1 ---> 10 time.sleep(50./1000) KeyboardInterrupt:
Woah, we're sending a ton of data, and not receiving any errors. I'll just interrupt it
print n_messages
348
# CSS styling within IPython notebook
from IPython.core.display import HTML
import urllib2
def css_styling():
url = 'https://raw.githubusercontent.com/plotly/python-user-guide/master/custom.css'
styles = urllib2.urlopen(url).read()
return HTML(styles)
css_styling()