Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagecell
Path: blob/master/contrib/sagecell-client/sagecell-client.py
447 views
1
#!/usr/bin/env python3
2
"""
3
A small client illustrating how to interact with the Sage Cell Server, version 2
4
5
Requires the websocket-client package: http://pypi.python.org/pypi/websocket-client
6
"""
7
8
import websocket
9
import json
10
import requests
11
12
13
class SageCell(object):
14
15
def __init__(self, url, timeout=10):
16
if not url.endswith('/'):
17
url += '/'
18
# POST or GET <url>/kernel
19
# if there is a terms of service agreement, you need to
20
# indicate acceptance in the data parameter below (see the API docs)
21
response = requests.post(
22
url + 'kernel',
23
data={'accepted_tos': 'true'},
24
headers={'Accept': 'application/json'}).json()
25
# RESPONSE: {"id": "ce20fada-f757-45e5-92fa-05e952dd9c87", "ws_url": "ws://localhost:8888/"}
26
# construct the websocket channel url from that
27
self.kernel_url = '{ws_url}kernel/{id}/'.format(**response)
28
print(self.kernel_url)
29
websocket.setdefaulttimeout(timeout)
30
self._ws = websocket.create_connection(
31
self.kernel_url + 'channels',
32
header={'Jupyter-Kernel-ID': response['id']})
33
# initialize our list of messages
34
self.shell_messages = []
35
self.iopub_messages = []
36
37
def execute_request(self, code):
38
# zero out our list of messages, in case this is not the first request
39
self.shell_messages = []
40
self.iopub_messages = []
41
42
# Send the JSON execute_request message string down the shell channel
43
msg = self._make_execute_request(code)
44
self._ws.send(msg)
45
46
# Wait until we get both a kernel status idle message and an execute_reply message
47
got_execute_reply = False
48
got_idle_status = False
49
while not (got_execute_reply and got_idle_status):
50
msg = json.loads(self._ws.recv())
51
if msg['channel'] == 'shell':
52
self.shell_messages.append(msg)
53
# an execute_reply message signifies the computation is done
54
if msg['header']['msg_type'] == 'execute_reply':
55
got_execute_reply = True
56
elif msg['channel'] == 'iopub':
57
self.iopub_messages.append(msg)
58
# the kernel status idle message signifies the kernel is done
59
if (msg['header']['msg_type'] == 'status' and
60
msg['content']['execution_state'] == 'idle'):
61
got_idle_status = True
62
63
return {'shell': self.shell_messages, 'iopub': self.iopub_messages}
64
65
def _make_execute_request(self, code):
66
from uuid import uuid4
67
session = str(uuid4())
68
69
# Here is the general form for an execute_request message
70
execute_request = {
71
'channel': 'shell',
72
'header': {
73
'msg_type': 'execute_request',
74
'msg_id': str(uuid4()),
75
'username': '', 'session': session,
76
},
77
'parent_header':{},
78
'metadata': {},
79
'content': {
80
'code': code,
81
'silent': False,
82
'user_expressions': {
83
'_sagecell_files': 'sys._sage_.new_files()',
84
},
85
'allow_stdin': False,
86
}
87
}
88
return json.dumps(execute_request)
89
90
def close(self):
91
# If we define this, we can use the closing() context manager to automatically close the channels
92
self._ws.close()
93
94
if __name__ == "__main__":
95
import sys
96
if len(sys.argv) >= 2:
97
# argv[1] is the web address
98
url = sys.argv[1]
99
else:
100
url = 'https://sagecell.sagemath.org'
101
a = SageCell(url)
102
import pprint
103
pprint.pprint(a.execute_request('factorial(2020)'))
104
105