##// END OF EJS Templates
Added note about tornado version to main script.
Brian Granger -
Show More
@@ -1,273 +1,275 b''
1 1 import datetime
2 2 import json
3 3 import logging
4 4 import os
5 5 import urllib
6 6 import uuid
7 7 from Queue import Queue
8 8
9 9 import zmq
10 10
11 11 # Install the pyzmq ioloop. This has to be done before anything else from
12 12 # tornado is imported.
13 13 from zmq.eventloop import ioloop
14 14 import tornado.ioloop
15 15 tornado.ioloop = ioloop
16 16
17 17 from tornado import httpserver
18 18 from tornado import options
19 19 from tornado import web
20 20 from tornado import websocket
21 21
22 22 from kernelmanager import KernelManager
23 23
24 24 options.define("port", default=8888, help="run on the given port", type=int)
25 25
26 26 _kernel_id_regex = r"(?P<kernel_id>\w+-\w+-\w+-\w+-\w+)"
27 27 _kernel_action_regex = r"(?P<action>restart|interrupt)"
28 28
29 29 class MainHandler(web.RequestHandler):
30 30 def get(self):
31 31 self.render('notebook.html')
32 32
33 33
34 34 class KernelHandler(web.RequestHandler):
35 35
36 36 def get(self):
37 37 self.write(json.dumps(self.application.kernel_ids))
38 38
39 39 def post(self):
40 40 kernel_id = self.application.start_kernel()
41 41 self.write(json.dumps(kernel_id))
42 42
43 43
44 44 class KernelActionHandler(web.RequestHandler):
45 45
46 46 def post(self, kernel_id, action):
47 47 # TODO: figure out a better way of handling RPC style calls.
48 48 if action == 'interrupt':
49 49 self.application.interrupt_kernel(kernel_id)
50 50 if action == 'restart':
51 51 new_kernel_id = self.application.restart_kernel(kernel_id)
52 52 self.write(json.dumps(new_kernel_id))
53 53
54 54
55 55 class ZMQStreamRouter(object):
56 56
57 57 def __init__(self, zmq_stream):
58 58 self.zmq_stream = zmq_stream
59 59 self._clients = {}
60 60 self.zmq_stream.on_recv(self._on_zmq_reply)
61 61
62 62 def register_client(self, client):
63 63 client_id = uuid.uuid4()
64 64 self._clients[client_id] = client
65 65 return client_id
66 66
67 67 def unregister_client(self, client_id):
68 68 del self._clients[client_id]
69 69
70 70 def copy_clients(self, router):
71 71 # Copy the clients of another router.
72 72 for client_id, client in router._clients.items():
73 73 client.router = self
74 74 self._clients[client_id] = client
75 75
76 76
77 77 class IOPubStreamRouter(ZMQStreamRouter):
78 78
79 79 def _on_zmq_reply(self, msg_list):
80 80 for client_id, client in self._clients.items():
81 81 for msg in msg_list:
82 82 client.write_message(msg)
83 83
84 84 def forward_unicode(self, client_id, msg):
85 85 # This is a SUB stream that we should never write to.
86 86 pass
87 87
88 88
89 89 class ShellStreamRouter(ZMQStreamRouter):
90 90
91 91 def __init__(self, zmq_stream):
92 92 ZMQStreamRouter.__init__(self, zmq_stream)
93 93 self._request_queue = Queue()
94 94
95 95 def _on_zmq_reply(self, msg_list):
96 96 client_id = self._request_queue.get(block=False)
97 97 client = self._clients.get(client_id)
98 98 if client is not None:
99 99 for msg in msg_list:
100 100 client.write_message(msg)
101 101
102 102 def forward_unicode(self, client_id, msg):
103 103 self._request_queue.put(client_id)
104 104 self.zmq_stream.send_unicode(msg)
105 105
106 106
107 107 class ZMQStreamHandler(websocket.WebSocketHandler):
108 108
109 109 def initialize(self, stream_name):
110 110 self.stream_name = stream_name
111 111
112 112 def open(self, kernel_id):
113 113 self.router = self.application.get_router(kernel_id, self.stream_name)
114 114 self.client_id = self.router.register_client(self)
115 115 logging.info("Connection open: %s, %s" % (kernel_id, self.client_id))
116 116
117 117 def on_message(self, msg):
118 118 self.router.forward_unicode(self.client_id, msg)
119 119
120 120 def on_close(self):
121 121 self.router.unregister_client(self.client_id)
122 122 logging.info("Connection closed: %s" % self.client_id)
123 123
124 124
125 125 class NotebookRootHandler(web.RequestHandler):
126 126
127 127 def get(self):
128 128 files = os.listdir(os.getcwd())
129 129 files = [file for file in files if file.endswith(".ipynb")]
130 130 self.write(json.dumps(files))
131 131
132 132
133 133 class NotebookHandler(web.RequestHandler):
134 134
135 135 SUPPORTED_METHODS = ("GET", "DELETE", "PUT")
136 136
137 137 def find_path(self, filename):
138 138 filename = urllib.unquote(filename)
139 139 if not filename.endswith('.ipynb'):
140 140 raise web.HTTPError(400)
141 141 path = os.path.join(os.getcwd(), filename)
142 142 return path
143 143
144 144 def get(self, filename):
145 145 path = self.find_path(filename)
146 146 if not os.path.isfile(path):
147 147 raise web.HTTPError(404)
148 148 info = os.stat(path)
149 149 self.set_header("Content-Type", "application/unknown")
150 150 self.set_header("Last-Modified", datetime.datetime.utcfromtimestamp(
151 151 info.st_mtime))
152 152 f = open(path, "r")
153 153 try:
154 154 self.finish(f.read())
155 155 finally:
156 156 f.close()
157 157
158 158 def put(self, filename):
159 159 path = self.find_path(filename)
160 160 f = open(path, "w")
161 161 f.write(self.request.body)
162 162 f.close()
163 163 self.finish()
164 164
165 165 def delete(self, filename):
166 166 path = self.find_path(filename)
167 167 if not os.path.isfile(path):
168 168 raise web.HTTPError(404)
169 169 os.unlink(path)
170 170 self.set_status(204)
171 171 self.finish()
172 172
173 173
174 174 class NotebookApplication(web.Application):
175 175
176 176 def __init__(self):
177 177 handlers = [
178 178 (r"/", MainHandler),
179 179 (r"/kernels", KernelHandler),
180 180 (r"/kernels/%s/%s" % (_kernel_id_regex, _kernel_action_regex), KernelActionHandler),
181 181 (r"/kernels/%s/iopub" % _kernel_id_regex, ZMQStreamHandler, dict(stream_name='iopub')),
182 182 (r"/kernels/%s/shell" % _kernel_id_regex, ZMQStreamHandler, dict(stream_name='shell')),
183 183 (r"/notebooks", NotebookRootHandler),
184 184 (r"/notebooks/([^/]+)", NotebookHandler)
185 185 ]
186 186 settings = dict(
187 187 template_path=os.path.join(os.path.dirname(__file__), "templates"),
188 188 static_path=os.path.join(os.path.dirname(__file__), "static"),
189 189 )
190 190 web.Application.__init__(self, handlers, **settings)
191 191
192 192 self.context = zmq.Context()
193 193 self.kernel_manager = KernelManager(self.context)
194 194 self._session_dict = {}
195 195 self._routers = {}
196 196
197 197 #-------------------------------------------------------------------------
198 198 # Methods for managing kernels and sessions
199 199 #-------------------------------------------------------------------------
200 200
201 201 @property
202 202 def kernel_ids(self):
203 203 return self.kernel_manager.kernel_ids
204 204
205 205 def start_kernel(self):
206 206 kernel_id = self.kernel_manager.start_kernel()
207 207 logging.info("Kernel started: %s" % kernel_id)
208 208 self.start_session(kernel_id)
209 209 return kernel_id
210 210
211 211 def interrupt_kernel(self, kernel_id):
212 212 self.kernel_manager.interrupt_kernel(kernel_id)
213 213 logging.info("Kernel interrupted: %s" % kernel_id)
214 214
215 215 def restart_kernel(self, kernel_id):
216 216 # Create the new kernel first so we can move the clients over.
217 217 new_kernel_id = self.start_kernel()
218 218
219 219 # Copy the clients over to the new routers.
220 220 old_iopub_router = self.get_router(kernel_id, 'iopub')
221 221 old_shell_router = self.get_router(kernel_id, 'shell')
222 222 new_iopub_router = self.get_router(new_kernel_id, 'iopub')
223 223 new_shell_router = self.get_router(new_kernel_id, 'shell')
224 224 new_iopub_router.copy_clients(old_iopub_router)
225 225 new_shell_router.copy_clients(old_shell_router)
226 226
227 227 # Now shutdown the old session and the kernel.
228 228 # TODO: This causes a hard crash in ZMQStream.close, which sets
229 229 # self.socket to None to hastily. We will need to fix this in PyZMQ
230 230 # itself. For now, we just leave the old kernel running :(
231 231 # sm = self.kernel_manager.get_session_manager(kernel_id)
232 232 # session_id = self._session_dict[kernel_id]
233 233 # sm.stop_session(session_id)
234 234 # self.kernel_manager.kill_kernel(kernel_id)
235 235
236 236 logging.info("Kernel restarted")
237 237 return new_kernel_id
238 238
239 239 def start_session(self, kernel_id):
240 240 sm = self.kernel_manager.get_session_manager(kernel_id)
241 241 session_id = sm.start_session()
242 242 self._session_dict[kernel_id] = session_id
243 243 iopub_stream = sm.get_iopub_stream(session_id)
244 244 shell_stream = sm.get_shell_stream(session_id)
245 245 iopub_router = IOPubStreamRouter(iopub_stream)
246 246 shell_router = ShellStreamRouter(shell_stream)
247 247 self._routers[(kernel_id, session_id, 'iopub')] = iopub_router
248 248 self._routers[(kernel_id, session_id, 'shell')] = shell_router
249 249 logging.info("Session started: %s, %s" % (kernel_id, session_id))
250 250
251 251 def stop_session(self, kernel_id):
252 252 # TODO: finish this!
253 253 sm = self.kernel_manager.get_session_manager(kernel_id)
254 254 session_id = self._session_dict[kernel_id]
255 255
256 256 def get_router(self, kernel_id, stream_name):
257 257 session_id = self._session_dict[kernel_id]
258 258 router = self._routers[(kernel_id, session_id, stream_name)]
259 259 return router
260 260
261 261
262 262 def main():
263 263 options.parse_command_line()
264 264 application = NotebookApplication()
265 265 http_server = httpserver.HTTPServer(application)
266 266 http_server.listen(options.options.port)
267 267 print "IPython Notebook running at: http://127.0.0.1:8888"
268 print "The github master of tornado is required to run this server:"
269 print " https://github.com/facebook/tornado/tree/master/tornado"
268 270 ioloop.IOLoop.instance().start()
269 271
270 272
271 273 if __name__ == "__main__":
272 274 main()
273 275
General Comments 0
You need to be logged in to leave comments. Login now