Some experiments trying to figure out how to convert juniper.js to Python so we can run code in a remote MyBinder code execution sandbox using a minimal Python API....
#!pip3 install aiohttp asyncio
Juniper.js is a minimal javascript client, based on thebelab.js, that shows how to:
Can we use it as a crib for a minimal Python client for using MyBinder as a simple remote code execution environment?
We start a MyBinder repo from a standardised URL:
import requests
url = 'https://mybinder.org'
branch = 'master'
repo = 'binder-examples/requirements'
binderURL = '{url}/build/gh/{repo}/{branch}'.format(url=url, repo=repo, branch=branch)
binderURL
'https://mybinder.org/build/gh/binder-examples/requirements/master'
#!pip3 install sseclient-py
The connection seems to be a streaming one, so let's see if we can fire one up:
import json
import pprint
import sseclient
def with_urllib3(url):
"""Get a streaming response for the given event feed using urllib3."""
import urllib3
http = urllib3.PoolManager()
return http.request('GET', url, preload_content=False)
def with_requests(url):
"""Get a streaming response for the given event feed using requests."""
import requests
return requests.get(url, stream=True)
response = with_urllib3(binderURL) # or with_requests(url)
client = sseclient.SSEClient(response)
for event in client.events():
msg = json.loads(event.data)
pprint.pprint(msg)
/usr/local/lib/python3.7/site-packages/urllib3/connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings InsecureRequestWarning) /usr/local/lib/python3.7/site-packages/urllib3/connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings InsecureRequestWarning)
{'imageName': 'gcr.io/binder-prod/r2d-f18835fd-binder-2dexamples-2drequirements-55ab5c:da202b4dd88edf6aef2b5ec516ba5b991806691d', 'message': 'Found built image, launching...\n', 'phase': 'built'} {'message': 'Launching server...\n', 'phase': 'launching'} {'image': 'gcr.io/binder-prod/r2d-f18835fd-binder-2dexamples-2drequirements-55ab5c:da202b4dd88edf6aef2b5ec516ba5b991806691d', 'message': 'server running at ' 'https://hub.gke.mybinder.org/user/binder-examples-requirements-5jbonbcm/\n', 'phase': 'ready', 'repo_url': 'https://github.com/binder-examples/requirements', 'token': 'Cdfg0TYrSRe-ZSGrqNcrPA', 'url': 'https://hub.gke.mybinder.org/user/binder-examples-requirements-5jbonbcm/'}
So it looks like we can start a MyBinder server running.
Let's grab the details...
resp = json.loads(event.data)
resp
{'phase': 'ready', 'message': 'server running at https://hub.gke.mybinder.org/user/binder-examples-requirements-5jbonbcm/\n', 'image': 'gcr.io/binder-prod/r2d-f18835fd-binder-2dexamples-2drequirements-55ab5c:da202b4dd88edf6aef2b5ec516ba5b991806691d', 'repo_url': 'https://github.com/binder-examples/requirements', 'token': 'Cdfg0TYrSRe-ZSGrqNcrPA', 'url': 'https://hub.gke.mybinder.org/user/binder-examples-requirements-5jbonbcm/'}
Inspecting the Juniper.js traffic, it looks like good form to create some sort of numerical connection ID, perhaps as a callback identifie?, although in practice it appears to be optional. Let's create one anyway:
#We can generate a rand session ID as a long-ish number
import random
random.getrandbits(32)
1897797548
Now we can make a request for a kernel:
import requests
randsessionId = random.getrandbits(32)
r = requests.post('{}api/kernels?{}'.format(resp['url'], randsessionId),
headers={'Authorization': 'token {}'.format(resp['token'])})
r.json()
{'id': 'fc4bc8fb-9320-44f6-9702-5c7983ccc403', 'name': 'python3', 'last_activity': '2019-08-12T13:16:38.527822Z', 'execution_state': 'starting', 'connections': 0}
The kernel conversation is handled over a websocket. So we need to create a websocket connection
wss = 'wss://{}api/kernels/{}/channels?token={}'.format(resp['url'].split('//')[1],
r.json()['id'],
resp['token'])
#Also need &session_id=NNNN; does wss supply that?
wss
'wss://hub.gke.mybinder.org/user/binder-examples-requirements-5jbonbcm/api/kernels/fc4bc8fb-9320-44f6-9702-5c7983ccc403/channels?token=Cdfg0TYrSRe-ZSGrqNcrPA'
It looks like we can fire up a websocket client...
#https://github.com/websocket-client/websocket-client
#!pip3 install websocket_client wsaccel
import json
import websocket
ws = websocket.WebSocket()
ws.connect(wss)
--- request header --- GET /user/binder-examples-requirements-5jbonbcm/api/kernels/fc4bc8fb-9320-44f6-9702-5c7983ccc403/channels?token=Cdfg0TYrSRe-ZSGrqNcrPA HTTP/1.1 Upgrade: websocket Connection: Upgrade Host: hub.gke.mybinder.org Origin: http://hub.gke.mybinder.org Sec-WebSocket-Key: tQgOzdv2Uca53euGqV41Cg== Sec-WebSocket-Version: 13 ----------------------- --- response header --- HTTP/1.1 101 Switching Protocols Server: nginx/1.13.12 Date: Mon, 12 Aug 2019 13:28:29 GMT Connection: upgrade upgrade: websocket sec-websocket-accept: PtlEdANrqF0AebQU8QgzX6WZ3Ac= set-cookie: username-hub-gke-mybinder-org="2|1:0|10:1565616509|29:username-hub-gke-mybinder-org|44:ZDQyOGZhMmY0OWNkNDk1YzkxZDNjMjg0OTVjMTU3NDk=|a29549d881cb76b3623439d7102d569b9b03f978419f0ab5eccfec27490c7200"; expires=Wed, 11 Sep 2019 13:28:29 GMT; HttpOnly; Path=/user/binder-examples-requirements-5jbonbcm/; Secure Strict-Transport-Security: max-age=15724800; includeSubDomains -----------------------
from uuid import uuid4
session = str(uuid4())
kernel_info_request = {
'channel': 'shell',
'header': {
'msg_type': 'kernel_info_request',
'msg_id': str(uuid4()),
'username': '',
'session': session,
'version': "5.2"
},
'parent_header':{},
'metadata': {},
'content': {}
}
ws.send( json.dumps(kernel_info_request) )
ws.recv()
send: b'\x81\xfe\x00\xfe\xf8\xfb\xe8b\x83\xd9\x8b\n\x99\x95\x86\x07\x94\xd9\xd2B\xda\x88\x80\x07\x94\x97\xcaN\xd8\xd9\x80\x07\x99\x9f\x8d\x10\xda\xc1\xc8\x19\xda\x96\x9b\x05\xa7\x8f\x91\x12\x9d\xd9\xd2B\xda\x90\x8d\x10\x96\x9e\x84=\x91\x95\x8e\r\xa7\x89\x8d\x13\x8d\x9e\x9b\x16\xda\xd7\xc8@\x95\x88\x8f=\x91\x9f\xcaX\xd8\xd9\xda\x07\xc8\xc3\x8e\x06\x99\xcd\xc5V\xcf\x9e\x8bO\xcc\xc9\x8bS\xd5\x99\xdf\x00\xcf\xd6\xdbP\xc1\xc8\xd1V\xc8\xc3\xdfP\x9a\xc2\xcaN\xd8\xd9\x9d\x11\x9d\x89\x86\x03\x95\x9e\xcaX\xd8\xd9\xcaN\xd8\xd9\x9b\x07\x8b\x88\x81\r\x96\xd9\xd2B\xda\xcc\xda\x00\x9c\xc9\xdaW\xcf\xd6\xd0\x07\xcf\xc2\xc5V\xc9\x9f\xd8O\x99\xcc\xdd\x04\xd5\x9f\x8aP\xcb\xc2\xdbR\x9b\x9e\xd1U\xce\xd9\xc4B\xda\x8d\x8d\x10\x8b\x92\x87\x0c\xda\xc1\xc8@\xcd\xd5\xda@\x85\xd7\xc8@\x88\x9a\x9a\x07\x96\x8f\xb7\n\x9d\x9a\x8c\x07\x8a\xd9\xd2B\x83\x86\xc4B\xda\x96\x8d\x16\x99\x9f\x89\x16\x99\xd9\xd2B\x83\x86\xc4B\xda\x98\x87\x0c\x8c\x9e\x86\x16\xda\xc1\xc8\x19\x85\x86'
'{"header": {"version": "5.3", "date": "2019-08-12T13:19:04.959124Z", "session": "9ff46be9-0b5e75f9195cd16d23f9e3d6", "username": "jovyan", "msg_type": "status", "msg_id": "caa8283c-1ab9b0ba4223efdf77a89c6c"}, "msg_id": "caa8283c-1ab9b0ba4223efdf77a89c6c", "msg_type": "status", "parent_header": {"msg_type": "kernel_info_request", "msg_id": "2e08fda6-47ec-42c1-b7b7-3293940872b9", "username": "", "session": "72bd2257-8e79-41d0-a75f-db23930ce976", "version": "5.2", "date": "2019-08-12T13:19:04.956714Z"}, "metadata": {}, "content": {"execution_state": "busy"}, "buffers": [], "channel": "iopub"}'
ws.recv()
'{"header": {"version": "5.3", "date": "2019-08-12T13:19:04.959563Z", "session": "9ff46be9-0b5e75f9195cd16d23f9e3d6", "username": "jovyan", "msg_type": "kernel_info_reply", "msg_id": "7321f2d2-b6a8d8bb46bfe467287c2df3"}, "msg_id": "7321f2d2-b6a8d8bb46bfe467287c2df3", "msg_type": "kernel_info_reply", "parent_header": {"msg_type": "kernel_info_request", "msg_id": "2e08fda6-47ec-42c1-b7b7-3293940872b9", "username": "", "session": "72bd2257-8e79-41d0-a75f-db23930ce976", "version": "5.2", "date": "2019-08-12T13:19:04.956714Z"}, "metadata": {}, "content": {"status": "ok", "protocol_version": "5.1", "implementation": "ipython", "implementation_version": "7.6.1", "language_info": {"name": "python", "version": "3.6.7", "mimetype": "text/x-python", "codemirror_mode": {"name": "ipython", "version": 3}, "pygments_lexer": "ipython3", "nbconvert_exporter": "python", "file_extension": ".py"}, "banner": "Python 3.6.7 | packaged by conda-forge | (default, Jul 2 2019, 02:18:42) \\nType \'copyright\', \'credits\' or \'license\' for more information\\nIPython 7.6.1 -- An enhanced Interactive Python. Type \'?\' for help.\\n", "help_links": [{"text": "Python Reference", "url": "https://docs.python.org/3.6"}, {"text": "IPython Reference", "url": "https://ipython.org/documentation.html"}, {"text": "NumPy Reference", "url": "https://docs.scipy.org/doc/numpy/reference/"}, {"text": "SciPy Reference", "url": "https://docs.scipy.org/doc/scipy/reference/"}, {"text": "Matplotlib Reference", "url": "https://matplotlib.org/contents.html"}, {"text": "SymPy Reference", "url": "http://docs.sympy.org/latest/index.html"}, {"text": "pandas Reference", "url": "https://pandas.pydata.org/pandas-docs/stable/"}]}, "buffers": [], "channel": "shell"}'
ws.recv()
'{"header": {"version": "5.3", "date": "2019-08-12T13:19:04.960844Z", "session": "9ff46be9-0b5e75f9195cd16d23f9e3d6", "username": "jovyan", "msg_type": "status", "msg_id": "91c3cba1-d7e85fc5eaaa453097a49d24"}, "msg_id": "91c3cba1-d7e85fc5eaaa453097a49d24", "msg_type": "status", "parent_header": {"msg_type": "kernel_info_request", "msg_id": "2e08fda6-47ec-42c1-b7b7-3293940872b9", "username": "", "session": "72bd2257-8e79-41d0-a75f-db23930ce976", "version": "5.2", "date": "2019-08-12T13:19:04.956714Z"}, "metadata": {}, "content": {"execution_state": "idle"}, "buffers": [], "channel": "iopub"}'
iopub_messages = []
shell_messages = []
ws.send( json.dumps(kernel_info_request) )
import time
got_execute_reply = False
got_idle_status = False
while not (got_execute_reply and got_idle_status):
time.sleep(1)
msg = json.loads(ws.recv())
print(msg)
if msg['channel'] == 'shell':
shell_messages.append(msg)
# an execute_reply message signifies the computation is done
if msg['header']['msg_type'] == 'kernel_info_reply':
got_execute_reply = True
elif msg['channel'] == 'iopub':
iopub_messages.append(msg)
# the kernel status idle message signifies the kernel is done
if (msg['header']['msg_type'] == 'status' and
msg['content']['execution_state'] == 'idle'):
got_idle_status = True
send: b"\x81\xfe\x00\xfee\xa4\x1f\x9d\x1e\x86|\xf5\x04\xcaq\xf8\t\x86%\xbdG\xd7w\xf8\t\xc8=\xb1E\x86w\xf8\x04\xc0z\xefG\x9e?\xe6G\xc9l\xfa:\xd0f\xed\x00\x86%\xbdG\xcfz\xef\x0b\xc1s\xc2\x0c\xcay\xf2:\xd6z\xec\x10\xc1l\xe9G\x88?\xbf\x08\xd7x\xc2\x0c\xc0=\xa7E\x86-\xf8U\x9cy\xf9\x04\x922\xa9R\xc1|\xb0Q\x96|\xacH\xc6(\xffR\x89,\xaf\\\x97&\xa9U\x9c(\xaf\x07\x9d=\xb1E\x86j\xee\x00\xd6q\xfc\x08\xc1=\xa7E\x86=\xb1E\x86l\xf8\x16\xd7v\xf2\x0b\x86%\xbdG\x93-\xff\x01\x96-\xa8R\x89'\xf8R\x9d2\xa9T\xc0/\xb0\x04\x93*\xfbH\xc0}\xafV\x9d,\xad\x06\xc1&\xaaS\x863\xbdG\xd2z\xef\x16\xcdp\xf3G\x9e?\xbfP\x8a-\xbf\x18\x88?\xbf\x15\xc5m\xf8\x0b\xd0@\xf5\x00\xc5{\xf8\x17\x86%\xbd\x1e\xd93\xbdG\xc9z\xe9\x04\xc0~\xe9\x04\x86%\xbd\x1e\xd93\xbdG\xc7p\xf3\x11\xc1q\xe9G\x9e?\xe6\x18\xd9" send: b'\x8a\x80\xaf4\xc0\xe2' send: b'\x8a\x80m\x01\x14\x00'
{'header': {'version': '5.3', 'date': '2019-08-12T13:31:06.161132Z', 'session': '9ff46be9-0b5e75f9195cd16d23f9e3d6', 'username': 'jovyan', 'msg_type': 'status', 'msg_id': '0ad839e0-30782ebc6f64fa2d7f19dc30'}, 'msg_id': '0ad839e0-30782ebc6f64fa2d7f19dc30', 'msg_type': 'status', 'parent_header': {'msg_type': 'kernel_info_request', 'msg_id': '2e08fda6-47ec-42c1-b7b7-3293940872b9', 'username': '', 'session': '72bd2257-8e79-41d0-a75f-db23930ce976', 'version': '5.2', 'date': '2019-08-12T13:31:06.158324Z'}, 'metadata': {}, 'content': {'execution_state': 'busy'}, 'buffers': [], 'channel': 'iopub'} {'header': {'version': '5.3', 'date': '2019-08-12T13:31:06.161587Z', 'session': '9ff46be9-0b5e75f9195cd16d23f9e3d6', 'username': 'jovyan', 'msg_type': 'kernel_info_reply', 'msg_id': '14b159bd-2d437064897587725524068d'}, 'msg_id': '14b159bd-2d437064897587725524068d', 'msg_type': 'kernel_info_reply', 'parent_header': {'msg_type': 'kernel_info_request', 'msg_id': '2e08fda6-47ec-42c1-b7b7-3293940872b9', 'username': '', 'session': '72bd2257-8e79-41d0-a75f-db23930ce976', 'version': '5.2', 'date': '2019-08-12T13:31:06.158324Z'}, 'metadata': {}, 'content': {'status': 'ok', 'protocol_version': '5.1', 'implementation': 'ipython', 'implementation_version': '7.6.1', 'language_info': {'name': 'python', 'version': '3.6.7', 'mimetype': 'text/x-python', 'codemirror_mode': {'name': 'ipython', 'version': 3}, 'pygments_lexer': 'ipython3', 'nbconvert_exporter': 'python', 'file_extension': '.py'}, 'banner': "Python 3.6.7 | packaged by conda-forge | (default, Jul 2 2019, 02:18:42) \nType 'copyright', 'credits' or 'license' for more information\nIPython 7.6.1 -- An enhanced Interactive Python. Type '?' for help.\n", 'help_links': [{'text': 'Python Reference', 'url': 'https://docs.python.org/3.6'}, {'text': 'IPython Reference', 'url': 'https://ipython.org/documentation.html'}, {'text': 'NumPy Reference', 'url': 'https://docs.scipy.org/doc/numpy/reference/'}, {'text': 'SciPy Reference', 'url': 'https://docs.scipy.org/doc/scipy/reference/'}, {'text': 'Matplotlib Reference', 'url': 'https://matplotlib.org/contents.html'}, {'text': 'SymPy Reference', 'url': 'http://docs.sympy.org/latest/index.html'}, {'text': 'pandas Reference', 'url': 'https://pandas.pydata.org/pandas-docs/stable/'}]}, 'buffers': [], 'channel': 'shell'} {'header': {'version': '5.3', 'date': '2019-08-12T13:31:06.162923Z', 'session': '9ff46be9-0b5e75f9195cd16d23f9e3d6', 'username': 'jovyan', 'msg_type': 'status', 'msg_id': '6bb7b2fd-ef6da7c2337e25f958e4505e'}, 'msg_id': '6bb7b2fd-ef6da7c2337e25f958e4505e', 'msg_type': 'status', 'parent_header': {'msg_type': 'kernel_info_request', 'msg_id': '2e08fda6-47ec-42c1-b7b7-3293940872b9', 'username': '', 'session': '72bd2257-8e79-41d0-a75f-db23930ce976', 'version': '5.2', 'date': '2019-08-12T13:31:06.158324Z'}, 'metadata': {}, 'content': {'execution_state': 'idle'}, 'buffers': [], 'channel': 'iopub'}
code = 'print("Hello World")'
execute_request = {
'channel': 'shell',
'header': {
'msg_type': 'execute_request',
'msg_id': str(uuid4()),
'username': '', 'session': session,
},
'parent_header':{},
'metadata': {},
'content': {
'code': code,
'silent': False,
'stop_on_error': False,
'user_expressions': {},
'allow_stdin': True,
'store_history': True,
}
}
#code: "%matplotlib inline↵import numpy as np↵import matplotlib.pyplot as plt↵plt.ion()↵fig, ax = plt.subplots()↵ax.scatter(*np.random.randn(2, 100), c=np.random.randn(100))↵ax.set(title="Wow, an interactive plot!")"
ws.send( json.dumps(execute_request) )
ws.recv()
'{"header": {"version": "5.3", "date": "2019-08-12T13:01:39.492225Z", "session": "9c5668e9-d94e3ab740cb5b155db252b0", "username": "jovyan", "msg_type": "execute_input", "msg_id": "99adcdae-86561b987871fd23a25cdc88"}, "msg_id": "99adcdae-86561b987871fd23a25cdc88", "msg_type": "execute_input", "parent_header": {"msg_type": "execute_request", "msg_id": "56b6e765-d54e-4682-a35f-b2947b45cde8", "username": "", "session": "2744dbd5-7d72-4bc9-947d-c9c5730ed70a", "date": "2019-08-12T13:01:39.489574Z", "version": "5.0"}, "metadata": {}, "content": {"code": "print(\\"Hello World\\")", "execution_count": 1}, "buffers": [], "channel": "iopub"}'
ws.recv()
'{"header": {"version": "5.3", "date": "2019-08-12T13:01:39.495367Z", "session": "9c5668e9-d94e3ab740cb5b155db252b0", "username": "jovyan", "msg_type": "stream", "msg_id": "3fa93ef1-0c80124e0407ea96537dd12d"}, "msg_id": "3fa93ef1-0c80124e0407ea96537dd12d", "msg_type": "stream", "parent_header": {"msg_type": "execute_request", "msg_id": "56b6e765-d54e-4682-a35f-b2947b45cde8", "username": "", "session": "2744dbd5-7d72-4bc9-947d-c9c5730ed70a", "date": "2019-08-12T13:01:39.489574Z", "version": "5.0"}, "metadata": {}, "content": {"name": "stdout", "text": "Hello World\\n"}, "buffers": [], "channel": "iopub"}'
#https://github.com/sagemath/sagecell/blob/master/contrib/sagecell-client/sagecell-client.py
"""
A small client illustrating how to interact with the Sage Cell Server, version 2
Requires the websocket-client package: http://pypi.python.org/pypi/websocket-client
"""
import websocket
import json
import requests
class MyBinderCell(object):
def __init__(self, binderURL, timeout=10):
response = with_urllib3(binderURL) # or with_requests(url)
client = sseclient.SSEClient(response)
for event in client.events():
#msg = json.loads(event.data)
#pprint.pprint(msg)
pass
resp = json.loads(event.data)
self._binder = resp
randsessionId = random.getrandbits(32)
r = requests.post('{}api/kernels?{}'.format(resp['url'], randsessionId),
headers={'Authorization': 'token {}'.format(resp['token'])})
wss = 'wss://{}api/kernels/{}/channels?token={}'.format(resp['url'].split('//')[1],
r.json()['id'],
resp['token'])
self.kernel_url = wss
print(self.kernel_url)
websocket.setdefaulttimeout(timeout)
self._ws = websocket.WebSocket()
self._ws.connect(wss)
# initialize our list of messages
self.shell_messages = []
self.iopub_messages = []
def _wait_on_response(self, response):
# Wait until we get both a kernel status idle message and an execute_reply message
got_execute_reply = False
got_idle_status = False
while not (got_execute_reply and got_idle_status):
msg = json.loads(self._ws.recv())
if msg['channel'] == 'shell':
self.shell_messages.append(msg)
# an execute_reply message signifies the computation is done
if msg['header']['msg_type'] == response:
got_execute_reply = True
elif msg['channel'] == 'iopub':
self.iopub_messages.append(msg)
# the kernel status idle message signifies the kernel is done
if (msg['header']['msg_type'] == 'status' and
msg['content']['execution_state'] == 'idle'):
got_idle_status = True
return {'shell': self.shell_messages, 'iopub': self.iopub_messages}
def execute_request(self, code):
# zero out our list of messages, in case this is not the first request
self.shell_messages = []
self.iopub_messages = []
# Send the JSON execute_request message string down the shell channel
msg = self._make_execute_request(code)
self._ws.send(msg)
return self._wait_on_response('execute_reply')
def _make_execute_request(self, code):
from uuid import uuid4
session = str(uuid4())
# Here is the general form for an execute_request message
execute_request = {
'channel': 'shell',
'header': {
'msg_type': 'execute_request',
'msg_id': str(uuid4()),
'username': '', 'session': session,
},
'parent_header':{},
'metadata': {},
'content': {
'code': code,
'silent': False,
'stop_on_error': False,
'user_expressions': {},
'allow_stdin': True,
'store_history': True,
}
}
return json.dumps(execute_request)
def _make_kernel_info_request(self):
kernel_info_request = {
'channel': 'shell',
'header': {
'msg_type': 'kernel_info_request',
'msg_id': str(uuid4()),
'username': '',
'session': session,
'version': "5.2"
},
'parent_header':{},
'metadata': {},
'content': {}
}
return json.dumps(kernel_info_request)
def kernel_info__request(self):
# zero out our list of messages, in case this is not the first request
self.shell_messages = []
self.iopub_messages = []
# Send the JSON execute_request message string down the shell channel
msg = self._make_kernel_info_request()
self._ws.send(msg)
return self._wait_on_response('kernel_info_reply')
def close(self):
# If we define this, we can use the closing() context manager to automatically close the channels
self._ws.close()
b = MyBinderCell(binderURL)
/usr/local/lib/python3.7/site-packages/urllib3/connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings InsecureRequestWarning) /usr/local/lib/python3.7/site-packages/urllib3/connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings InsecureRequestWarning)
wss://hub.gke.mybinder.org/user/binder-examples-requirements-5xthjxjt/api/kernels/77345c0b-14ed-4724-bc01-bc745512a038/channels?token=p9rVYouBR5eAMJ5LzsX86w
--- request header --- GET /user/binder-examples-requirements-5xthjxjt/api/kernels/77345c0b-14ed-4724-bc01-bc745512a038/channels?token=p9rVYouBR5eAMJ5LzsX86w HTTP/1.1 Upgrade: websocket Connection: Upgrade Host: hub.gke.mybinder.org Origin: http://hub.gke.mybinder.org Sec-WebSocket-Key: l24dihI38SVrRDbkz1icvQ== Sec-WebSocket-Version: 13 ----------------------- --- response header --- HTTP/1.1 101 Switching Protocols Server: nginx/1.13.12 Date: Mon, 12 Aug 2019 13:48:57 GMT Connection: upgrade upgrade: websocket sec-websocket-accept: veILnARiJVSZUR5MMg5ebknw5UE= set-cookie: username-hub-gke-mybinder-org="2|1:0|10:1565617737|29:username-hub-gke-mybinder-org|44:NjU5NzAxYzgxYmVkNDhiNzlmNDUyMTNhZTgyZTQxY2Y=|1c3e9a2721b0726fbb7edd7c8369ffb1f980de54f94e5125f88b001371c75583"; expires=Wed, 11 Sep 2019 13:48:57 GMT; HttpOnly; Path=/user/binder-examples-requirements-5xthjxjt/; Secure Strict-Transport-Security: max-age=15724800; includeSubDomains -----------------------
b._binder
{'phase': 'ready', 'message': 'server running at https://hub.gke.mybinder.org/user/binder-examples-requirements-5xthjxjt/\n', 'image': 'gcr.io/binder-prod/r2d-f18835fd-binder-2dexamples-2drequirements-55ab5c:da202b4dd88edf6aef2b5ec516ba5b991806691d', 'repo_url': 'https://github.com/binder-examples/requirements', 'token': 'p9rVYouBR5eAMJ5LzsX86w', 'url': 'https://hub.gke.mybinder.org/user/binder-examples-requirements-5xthjxjt/'}
b.kernel_info__request()
send: b'\x81\xfe\x00\xfe,;9\xebW\x19Z\x83MUW\x8e@\x19\x03\xcb\x0eHQ\x8e@W\x1b\xc7\x0c\x19Q\x8eM_\\\x99\x0e\x01\x19\x90\x0eVJ\x8csO@\x9bI\x19\x03\xcb\x0eP\\\x99B^U\xb4EU_\x84sI\\\x9aY^J\x9f\x0e\x17\x19\xc9AH^\xb4E_\x1b\xd1\x0c\x19X\xdb\x14\t\r\x8eH\x0e\x14\x88\x18ZX\xc6\x18XX\xd3\x01\x03_\xdd\x1b\x16\\\xd3\x1b\x08[\x8eH\x0b]\x89\x1f\t\x1b\xc7\x0c\x19L\x98IIW\x8aA^\x1b\xd1\x0c\x19\x1b\xc7\x0c\x19J\x8e_HP\x84B\x19\x03\xcb\x0e\x0c\x0b\x89H\t\x0b\xde\x1b\x16\x01\x8e\x1b\x02\x14\xdf\x1d_\t\xc6M\x0c\x0c\x8d\x01_[\xd9\x1f\x02\n\xdbO^\x00\xdc\x1a\x19\x15\xcb\x0eM\\\x99_RV\x85\x0e\x01\x19\xc9\x19\x15\x0b\xc9Q\x17\x19\xc9\\ZK\x8eBOf\x83IZ]\x8e^\x19\x03\xcbWF\x15\xcb\x0eV\\\x9fM_X\x9fM\x19\x03\xcbWF\x15\xcb\x0eXV\x85X^W\x9f\x0e\x01\x19\x90QF'
{'shell': [{'header': {'version': '5.3', 'date': '2019-08-12T13:48:58.055802Z', 'session': '627c3889-70a3d665f8c21b2b6f562930', 'username': 'jovyan', 'msg_type': 'kernel_info_reply', 'msg_id': 'a98648d4-98374863867dedd1905886c4'}, 'msg_id': 'a98648d4-98374863867dedd1905886c4', 'msg_type': 'kernel_info_reply', 'parent_header': {'msg_type': 'kernel_info_request', 'msg_id': 'a0824ed5-c4aa-4ca8-8f67-e873bed0db32', 'username': '', 'session': '72bd2257-8e79-41d0-a75f-db23930ce976', 'version': '5.2', 'date': '2019-08-12T13:48:58.053317Z'}, 'metadata': {}, 'content': {'status': 'ok', 'protocol_version': '5.1', 'implementation': 'ipython', 'implementation_version': '7.6.1', 'language_info': {'name': 'python', 'version': '3.6.7', 'mimetype': 'text/x-python', 'codemirror_mode': {'name': 'ipython', 'version': 3}, 'pygments_lexer': 'ipython3', 'nbconvert_exporter': 'python', 'file_extension': '.py'}, 'banner': "Python 3.6.7 | packaged by conda-forge | (default, Jul 2 2019, 02:18:42) \nType 'copyright', 'credits' or 'license' for more information\nIPython 7.6.1 -- An enhanced Interactive Python. Type '?' for help.\n", 'help_links': [{'text': 'Python Reference', 'url': 'https://docs.python.org/3.6'}, {'text': 'IPython Reference', 'url': 'https://ipython.org/documentation.html'}, {'text': 'NumPy Reference', 'url': 'https://docs.scipy.org/doc/numpy/reference/'}, {'text': 'SciPy Reference', 'url': 'https://docs.scipy.org/doc/scipy/reference/'}, {'text': 'Matplotlib Reference', 'url': 'https://matplotlib.org/contents.html'}, {'text': 'SymPy Reference', 'url': 'http://docs.sympy.org/latest/index.html'}, {'text': 'pandas Reference', 'url': 'https://pandas.pydata.org/pandas-docs/stable/'}]}, 'buffers': [], 'channel': 'shell'}], 'iopub': [{'header': {'version': '5.3', 'date': '2019-08-12T13:48:58.055436Z', 'session': '627c3889-70a3d665f8c21b2b6f562930', 'username': 'jovyan', 'msg_type': 'status', 'msg_id': 'f96c5abe-8e009f5c6284005859bac6f0'}, 'msg_id': 'f96c5abe-8e009f5c6284005859bac6f0', 'msg_type': 'status', 'parent_header': {'msg_type': 'kernel_info_request', 'msg_id': 'a0824ed5-c4aa-4ca8-8f67-e873bed0db32', 'username': '', 'session': '72bd2257-8e79-41d0-a75f-db23930ce976', 'version': '5.2', 'date': '2019-08-12T13:48:58.053317Z'}, 'metadata': {}, 'content': {'execution_state': 'busy'}, 'buffers': [], 'channel': 'iopub'}, {'header': {'version': '5.3', 'date': '2019-08-12T13:48:58.056936Z', 'session': '627c3889-70a3d665f8c21b2b6f562930', 'username': 'jovyan', 'msg_type': 'status', 'msg_id': 'e4559151-bb5a502ccd6857921124d010'}, 'msg_id': 'e4559151-bb5a502ccd6857921124d010', 'msg_type': 'status', 'parent_header': {'msg_type': 'kernel_info_request', 'msg_id': 'a0824ed5-c4aa-4ca8-8f67-e873bed0db32', 'username': '', 'session': '72bd2257-8e79-41d0-a75f-db23930ce976', 'version': '5.2', 'date': '2019-08-12T13:48:58.053317Z'}, 'metadata': {}, 'content': {'execution_state': 'idle'}, 'buffers': [], 'channel': 'iopub'}]}
b.execute_request("print('hello world')")
send: b'\x81\xfe\x01s\xd9\xb0P\x97\xa2\x923\xff\xb8\xde>\xf2\xb5\x92j\xb7\xfb\xc38\xf2\xb5\xdcr\xbb\xf9\x928\xf2\xb8\xd45\xe5\xfb\x8ap\xec\xfb\xdd#\xf0\x86\xc4)\xe7\xbc\x92j\xb7\xfb\xd5(\xf2\xba\xc5$\xf2\x86\xc25\xe6\xac\xd5#\xe3\xfb\x9cp\xb5\xb4\xc37\xc8\xb0\xd4r\xad\xf9\x92d\xa1\xbc\x83b\xaf\xe1\xd3}\xa5\xe0\x884\xba\xed\xd5f\xf3\xf4\xd2e\xf2\xe9\x9dg\xa5\xe8\xd66\xa2\xbf\x895\xf4\xbd\xd5r\xbb\xf9\x92%\xe4\xbc\xc2>\xf6\xb4\xd5r\xad\xf9\x92r\xbb\xf9\x92#\xf2\xaa\xc39\xf8\xb7\x92j\xb7\xfb\x84b\xf6\xbb\x83g\xf6\xef\x9d`\xf1\xbb\x85}\xa3\xe1\x83f\xba\xbb\x87h\xf5\xf4\xd25\xf6\xe0\x81c\xf3\xb8\xd1f\xf2\xec\x92-\xbb\xf9\x92 \xf6\xab\xd5>\xe3\x86\xd85\xf6\xbd\xd5"\xb5\xe3\x90+\xea\xf5\x90r\xfa\xbc\xc41\xf3\xb8\xc41\xb5\xe3\x90+\xea\xf5\x90r\xf4\xb6\xde$\xf2\xb7\xc4r\xad\xf9\xcbr\xf4\xb6\xd45\xb5\xe3\x90r\xe7\xab\xd9>\xe3\xf1\x978\xf2\xb5\xdc?\xb7\xae\xdf"\xfb\xbd\x97y\xb5\xf5\x90r\xe4\xb0\xdc5\xf9\xad\x92j\xb7\xbf\xd1<\xe4\xbc\x9cp\xb5\xaa\xc4?\xe7\x86\xdf>\xc8\xbc\xc2"\xf8\xab\x92j\xb7\xbf\xd1<\xe4\xbc\x9cp\xb5\xac\xc35\xe5\x86\xd5(\xe7\xab\xd5#\xe4\xb0\xdf>\xe4\xfb\x8ap\xec\xa4\x9cp\xb5\xb8\xdc<\xf8\xae\xef#\xe3\xbd\xd9>\xb5\xe3\x90$\xe5\xac\xd5|\xb7\xfb\xc3$\xf8\xab\xd5\x0f\xff\xb0\xc3$\xf8\xab\xc9r\xad\xf9\xc4"\xe2\xbc\xcd-'
{'shell': [{'header': {'version': '5.3', 'date': '2019-08-12T13:48:58.188788Z', 'session': '627c3889-70a3d665f8c21b2b6f562930', 'username': 'jovyan', 'msg_type': 'execute_reply', 'msg_id': '703e9049-9c5ffd94196cf9d9b94fc207'}, 'msg_id': '703e9049-9c5ffd94196cf9d9b94fc207', 'msg_type': 'execute_reply', 'parent_header': {'msg_type': 'execute_request', 'msg_id': '46e3288c-298d-4e6d-b5e0-721ff5f9ecde', 'username': '', 'session': '42ab37a6-0fb5-4836-b78b-bea913daa6e5', 'date': '2019-08-12T13:48:58.178891Z', 'version': '5.0'}, 'metadata': {'started': '2019-08-12T13:48:58.182227Z', 'dependencies_met': True, 'engine': '2b4176b6-91dc-4b5f-8767-4c980d2f70a6', 'status': 'ok'}, 'content': {'status': 'ok', 'execution_count': 1, 'user_expressions': {}, 'payload': []}, 'buffers': [], 'channel': 'shell'}], 'iopub': [{'header': {'version': '5.3', 'date': '2019-08-12T13:48:58.181493Z', 'session': '627c3889-70a3d665f8c21b2b6f562930', 'username': 'jovyan', 'msg_type': 'status', 'msg_id': '725c49f1-d48ffd8dc52703a49e91d781'}, 'msg_id': '725c49f1-d48ffd8dc52703a49e91d781', 'msg_type': 'status', 'parent_header': {'msg_type': 'execute_request', 'msg_id': '46e3288c-298d-4e6d-b5e0-721ff5f9ecde', 'username': '', 'session': '42ab37a6-0fb5-4836-b78b-bea913daa6e5', 'date': '2019-08-12T13:48:58.178891Z', 'version': '5.0'}, 'metadata': {}, 'content': {'execution_state': 'busy'}, 'buffers': [], 'channel': 'iopub'}, {'header': {'version': '5.3', 'date': '2019-08-12T13:48:58.182312Z', 'session': '627c3889-70a3d665f8c21b2b6f562930', 'username': 'jovyan', 'msg_type': 'execute_input', 'msg_id': '0dd15980-9bfabc31fbbafc8e2c21a3ec'}, 'msg_id': '0dd15980-9bfabc31fbbafc8e2c21a3ec', 'msg_type': 'execute_input', 'parent_header': {'msg_type': 'execute_request', 'msg_id': '46e3288c-298d-4e6d-b5e0-721ff5f9ecde', 'username': '', 'session': '42ab37a6-0fb5-4836-b78b-bea913daa6e5', 'date': '2019-08-12T13:48:58.178891Z', 'version': '5.0'}, 'metadata': {}, 'content': {'code': "print('hello world')", 'execution_count': 1}, 'buffers': [], 'channel': 'iopub'}, {'header': {'version': '5.3', 'date': '2019-08-12T13:48:58.186739Z', 'session': '627c3889-70a3d665f8c21b2b6f562930', 'username': 'jovyan', 'msg_type': 'stream', 'msg_id': '227dee1b-1edf0b620656881b9e29db50'}, 'msg_id': '227dee1b-1edf0b620656881b9e29db50', 'msg_type': 'stream', 'parent_header': {'msg_type': 'execute_request', 'msg_id': '46e3288c-298d-4e6d-b5e0-721ff5f9ecde', 'username': '', 'session': '42ab37a6-0fb5-4836-b78b-bea913daa6e5', 'date': '2019-08-12T13:48:58.178891Z', 'version': '5.0'}, 'metadata': {}, 'content': {'name': 'stdout', 'text': 'hello world\n'}, 'buffers': [], 'channel': 'iopub'}, {'header': {'version': '5.3', 'date': '2019-08-12T13:48:58.189964Z', 'session': '627c3889-70a3d665f8c21b2b6f562930', 'username': 'jovyan', 'msg_type': 'status', 'msg_id': '6cac0e2b-a946d941f3dfc1545cc5f419'}, 'msg_id': '6cac0e2b-a946d941f3dfc1545cc5f419', 'msg_type': 'status', 'parent_header': {'msg_type': 'execute_request', 'msg_id': '46e3288c-298d-4e6d-b5e0-721ff5f9ecde', 'username': '', 'session': '42ab37a6-0fb5-4836-b78b-bea913daa6e5', 'date': '2019-08-12T13:48:58.178891Z', 'version': '5.0'}, 'metadata': {}, 'content': {'execution_state': 'idle'}, 'buffers': [], 'channel': 'iopub'}]}