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