##// END OF EJS Templates
Major refactor of kernel connection management in the notebook....
Brian E. Granger -
Show More
@@ -4,20 +4,22 b''
4 # Imports
4 # Imports
5 #-----------------------------------------------------------------------------
5 #-----------------------------------------------------------------------------
6
6
7 import json
8 import logging
9 import os
10 import urllib
11
7
12 from tornado import web
8 from tornado import web
13 from tornado import websocket
9 from tornado import websocket
14
10
11 from zmq.eventloop import ioloop
12 from zmq.utils import jsonapi
13
14 from IPython.zmq.session import Session
15
15 try:
16 try:
16 from docutils.core import publish_string
17 from docutils.core import publish_string
17 except ImportError:
18 except ImportError:
18 publish_string = None
19 publish_string = None
19
20
20
21
22
21 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
22 # Top-level handlers
24 # Top-level handlers
23 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
@@ -52,15 +54,15 b' class NamedNotebookHandler(web.RequestHandler):'
52 class MainKernelHandler(web.RequestHandler):
54 class MainKernelHandler(web.RequestHandler):
53
55
54 def get(self):
56 def get(self):
55 rkm = self.application.routing_kernel_manager
57 km = self.application.kernel_manager
56 self.finish(json.dumps(rkm.kernel_ids))
58 self.finish(jsonapi.dumps(km.kernel_ids))
57
59
58 def post(self):
60 def post(self):
59 rkm = self.application.routing_kernel_manager
61 km = self.application.kernel_manager
60 notebook_id = self.get_argument('notebook', default=None)
62 notebook_id = self.get_argument('notebook', default=None)
61 kernel_id = rkm.start_kernel(notebook_id)
63 kernel_id = km.start_kernel(notebook_id)
62 self.set_header('Location', '/'+kernel_id)
64 self.set_header('Location', '/'+kernel_id)
63 self.finish(json.dumps(kernel_id))
65 self.finish(jsonapi.dumps(kernel_id))
64
66
65
67
66 class KernelHandler(web.RequestHandler):
68 class KernelHandler(web.RequestHandler):
@@ -68,8 +70,8 b' class KernelHandler(web.RequestHandler):'
68 SUPPORTED_METHODS = ('DELETE')
70 SUPPORTED_METHODS = ('DELETE')
69
71
70 def delete(self, kernel_id):
72 def delete(self, kernel_id):
71 rkm = self.application.routing_kernel_manager
73 km = self.application.kernel_manager
72 rkm.kill_kernel(kernel_id)
74 km.kill_kernel(kernel_id)
73 self.set_status(204)
75 self.set_status(204)
74 self.finish()
76 self.finish()
75
77
@@ -77,31 +79,124 b' class KernelHandler(web.RequestHandler):'
77 class KernelActionHandler(web.RequestHandler):
79 class KernelActionHandler(web.RequestHandler):
78
80
79 def post(self, kernel_id, action):
81 def post(self, kernel_id, action):
80 rkm = self.application.routing_kernel_manager
82 km = self.application.kernel_manager
81 if action == 'interrupt':
83 if action == 'interrupt':
82 rkm.interrupt_kernel(kernel_id)
84 km.interrupt_kernel(kernel_id)
83 self.set_status(204)
85 self.set_status(204)
84 if action == 'restart':
86 if action == 'restart':
85 new_kernel_id = rkm.restart_kernel(kernel_id)
87 new_kernel_id = km.restart_kernel(kernel_id)
86 self.write(json.dumps(new_kernel_id))
88 self.write(jsonapi.dumps(new_kernel_id))
87 self.finish()
89 self.finish()
88
90
89
91
90 class ZMQStreamHandler(websocket.WebSocketHandler):
92 class ZMQStreamHandler(websocket.WebSocketHandler):
91
93
92 def initialize(self, stream_name):
94 def _reserialize_reply(self, msg_list):
93 self.stream_name = stream_name
95 """Reserialize a reply message using JSON.
96
97 This takes the msg list from the ZMQ socket, unserializes it using
98 self.session and then serializes the result using JSON. This method
99 should be used by self._on_zmq_reply to build messages that can
100 be sent back to the browser.
101 """
102 idents, msg_list = self.session.feed_identities(msg_list)
103 msg = self.session.unserialize(msg_list)
104 msg['header'].pop('date')
105 msg.pop('buffers')
106 return jsonapi.dumps(msg)
107
108
109 class IOPubHandler(ZMQStreamHandler):
110
111 def initialize(self, *args, **kwargs):
112 self._kernel_alive = True
113 self._beating = False
114
115 def open(self, kernel_id):
116 km = self.application.kernel_manager
117 self.kernel_id = kernel_id
118 self.session = Session()
119 self.time_to_dead = km.time_to_dead
120 self.iopub_stream = km.create_iopub_stream(kernel_id)
121 self.hb_stream = km.create_hb_stream(kernel_id)
122 self.iopub_stream.on_recv(self._on_zmq_reply)
123 self.start_hb(self.kernel_died)
124
125 def _on_zmq_reply(self, msg_list):
126 msg = self._reserialize_reply(msg_list)
127 self.write_message(msg)
128
129 def on_close(self):
130 self.stop_hb()
131 self.iopub_stream.close()
132 self.hb_stream.close()
133
134 def start_hb(self, callback):
135 """Start the heartbeating and call the callback if the kernel dies."""
136 if not self._beating:
137 self._kernel_alive = True
138
139 def ping_or_dead():
140 if self._kernel_alive:
141 self._kernel_alive = False
142 self.hb_stream.send(b'ping')
143 else:
144 try:
145 callback()
146 except:
147 pass
148 finally:
149 self._hb_periodic_callback.stop()
150
151 def beat_received(msg):
152 self._kernel_alive = True
153
154 self.hb_stream.on_recv(beat_received)
155 self._hb_periodic_callback = ioloop.PeriodicCallback(ping_or_dead, self.time_to_dead*1000)
156 self._hb_periodic_callback.start()
157 self._beating= True
158
159 def stop_hb(self):
160 """Stop the heartbeating and cancel all related callbacks."""
161 if self._beating:
162 self._hb_periodic_callback.stop()
163 if not self.hb_stream.closed():
164 self.hb_stream.on_recv(None)
165
166 def kernel_died(self):
167 self.write_message(
168 {'header': {'msg_type': 'status'},
169 'parent_header': {},
170 'content': {'execution_state':'dead'}
171 }
172 )
173 self.on_close()
174
175
176 class ShellHandler(ZMQStreamHandler):
177
178 def initialize(self, *args, **kwargs):
179 pass
94
180
95 def open(self, kernel_id):
181 def open(self, kernel_id):
96 rkm = self.application.routing_kernel_manager
182 km = self.application.kernel_manager
97 self.router = rkm.get_router(kernel_id, self.stream_name)
183 self.max_msg_size = km.max_msg_size
98 self.client_id = self.router.register_client(self)
184 self.kernel_id = kernel_id
185 self.session = Session()
186 self.shell_stream = self.application.kernel_manager.create_shell_stream(kernel_id)
187 self.shell_stream.on_recv(self._on_zmq_reply)
188
189 def _on_zmq_reply(self, msg_list):
190 msg = self._reserialize_reply(msg_list)
191 self.write_message(msg)
99
192
100 def on_message(self, msg):
193 def on_message(self, msg):
101 self.router.forward_msg(self.client_id, msg)
194 if len(msg) < self.max_msg_size:
195 msg = jsonapi.loads(msg)
196 self.session.send(self.shell_stream, msg)
102
197
103 def on_close(self):
198 def on_close(self):
104 self.router.unregister_client(self.client_id)
199 self.shell_stream.close()
105
200
106
201
107 #-----------------------------------------------------------------------------
202 #-----------------------------------------------------------------------------
@@ -113,7 +208,7 b' class NotebookRootHandler(web.RequestHandler):'
113 def get(self):
208 def get(self):
114 nbm = self.application.notebook_manager
209 nbm = self.application.notebook_manager
115 files = nbm.list_notebooks()
210 files = nbm.list_notebooks()
116 self.finish(json.dumps(files))
211 self.finish(jsonapi.dumps(files))
117
212
118 def post(self):
213 def post(self):
119 nbm = self.application.notebook_manager
214 nbm = self.application.notebook_manager
@@ -125,7 +220,7 b' class NotebookRootHandler(web.RequestHandler):'
125 else:
220 else:
126 notebook_id = nbm.new_notebook()
221 notebook_id = nbm.new_notebook()
127 self.set_header('Location', '/'+notebook_id)
222 self.set_header('Location', '/'+notebook_id)
128 self.finish(json.dumps(notebook_id))
223 self.finish(jsonapi.dumps(notebook_id))
129
224
130
225
131 class NotebookHandler(web.RequestHandler):
226 class NotebookHandler(web.RequestHandler):
@@ -175,11 +270,10 b' class RSTHandler(web.RequestHandler):'
175 body = self.request.body.strip()
270 body = self.request.body.strip()
176 source = body
271 source = body
177 # template_path=os.path.join(os.path.dirname(__file__), u'templates', u'rst_template.html')
272 # template_path=os.path.join(os.path.dirname(__file__), u'templates', u'rst_template.html')
178 print template_path
179 defaults = {'file_insertion_enabled': 0,
273 defaults = {'file_insertion_enabled': 0,
180 'raw_enabled': 0,
274 'raw_enabled': 0,
181 '_disable_config': 1,
275 '_disable_config': 1,
182 'stylesheet_path': 0,
276 'stylesheet_path': 0
183 # 'template': template_path
277 # 'template': template_path
184 }
278 }
185 try:
279 try:
@@ -16,14 +16,13 b' import sys'
16 import uuid
16 import uuid
17
17
18 import zmq
18 import zmq
19 from zmq.eventloop.zmqstream import ZMQStream
19
20
20 from tornado import web
21 from tornado import web
21
22
22 from .routers import IOPubStreamRouter, ShellStreamRouter
23
24 from IPython.config.configurable import LoggingConfigurable
23 from IPython.config.configurable import LoggingConfigurable
25 from IPython.zmq.ipkernel import launch_kernel
24 from IPython.zmq.ipkernel import launch_kernel
26 from IPython.utils.traitlets import Instance, Dict, List, Unicode
25 from IPython.utils.traitlets import Instance, Dict, List, Unicode, Float, Int
27
26
28 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
29 # Classes
28 # Classes
@@ -110,6 +109,7 b' class KernelManager(LoggingConfigurable):'
110 else:
109 else:
111 kernel_process.send_signal(signal.SIGINT)
110 kernel_process.send_signal(signal.SIGINT)
112
111
112
113 def signal_kernel(self, kernel_id, signum):
113 def signal_kernel(self, kernel_id, signum):
114 """ Sends a signal to the kernel by its uuid.
114 """ Sends a signal to the kernel by its uuid.
115
115
@@ -182,34 +182,50 b' class KernelManager(LoggingConfigurable):'
182 else:
182 else:
183 raise KeyError("Kernel with id not found: %s" % kernel_id)
183 raise KeyError("Kernel with id not found: %s" % kernel_id)
184
184
185 def create_session_manager(self, kernel_id):
185 def create_connected_stream(self, ip, port, socket_type):
186 """Create a new session manager for a kernel by its uuid."""
186 sock = self.context.socket(socket_type)
187 from sessionmanager import SessionManager
187 addr = "tcp://%s:%i" % (ip, port)
188 return SessionManager(
188 self.log.info("Connecting to: %s" % addr)
189 kernel_id=kernel_id, kernel_manager=self,
189 sock.connect(addr)
190 config=self.config, context=self.context, log=self.log
190 return ZMQStream(sock)
191 )
191
192 def create_iopub_stream(self, kernel_id):
193 ip = self.get_kernel_ip(kernel_id)
194 ports = self.get_kernel_ports(kernel_id)
195 iopub_stream = self.create_connected_stream(ip, ports['iopub_port'], zmq.SUB)
196 iopub_stream.socket.setsockopt(zmq.SUBSCRIBE, b'')
197 return iopub_stream
192
198
199 def create_shell_stream(self, kernel_id):
200 ip = self.get_kernel_ip(kernel_id)
201 ports = self.get_kernel_ports(kernel_id)
202 shell_stream = self.create_connected_stream(ip, ports['shell_port'], zmq.XREQ)
203 return shell_stream
193
204
194 class RoutingKernelManager(LoggingConfigurable):
205 def create_hb_stream(self, kernel_id):
195 """A KernelManager that handles WebSocket routing and HTTP error handling"""
206 ip = self.get_kernel_ip(kernel_id)
207 ports = self.get_kernel_ports(kernel_id)
208 hb_stream = self.create_connected_stream(ip, ports['hb_port'], zmq.REQ)
209 return hb_stream
210
211
212 class MappingKernelManager(KernelManager):
213 """A KernelManager that handles notebok mapping and HTTP error handling"""
196
214
197 kernel_argv = List(Unicode)
215 kernel_argv = List(Unicode)
198 kernel_manager = Instance(KernelManager)
216 kernel_manager = Instance(KernelManager)
217 time_to_dead = Float(3.0, config=True, help="""Kernel heartbeat interval in seconds.""")
218 max_msg_size = Int(65536, config=True, help="""
219 The max raw message size accepted from the browser
220 over a WebSocket connection.
221 """)
199
222
200 _routers = Dict()
201 _session_dict = Dict()
202 _notebook_mapping = Dict()
223 _notebook_mapping = Dict()
203
224
204 #-------------------------------------------------------------------------
225 #-------------------------------------------------------------------------
205 # Methods for managing kernels and sessions
226 # Methods for managing kernels and sessions
206 #-------------------------------------------------------------------------
227 #-------------------------------------------------------------------------
207
228
208 @property
209 def kernel_ids(self):
210 """List the kernel ids."""
211 return self.kernel_manager.kernel_ids
212
213 def kernel_for_notebook(self, notebook_id):
229 def kernel_for_notebook(self, notebook_id):
214 """Return the kernel_id for a notebook_id or None."""
230 """Return the kernel_id for a notebook_id or None."""
215 return self._notebook_mapping.get(notebook_id)
231 return self._notebook_mapping.get(notebook_id)
@@ -234,7 +250,7 b' class RoutingKernelManager(LoggingConfigurable):'
234 del self._notebook_mapping[notebook_id]
250 del self._notebook_mapping[notebook_id]
235
251
236 def start_kernel(self, notebook_id=None):
252 def start_kernel(self, notebook_id=None):
237 """Start a kernel an return its kernel_id.
253 """Start a kernel for a notebok an return its kernel_id.
238
254
239 Parameters
255 Parameters
240 ----------
256 ----------
@@ -243,108 +259,48 b' class RoutingKernelManager(LoggingConfigurable):'
243 is not None, this kernel will be persistent whenever the notebook
259 is not None, this kernel will be persistent whenever the notebook
244 requests a kernel.
260 requests a kernel.
245 """
261 """
246 self.log.info
247 kernel_id = self.kernel_for_notebook(notebook_id)
262 kernel_id = self.kernel_for_notebook(notebook_id)
248 if kernel_id is None:
263 if kernel_id is None:
249 kwargs = dict()
264 kwargs = dict()
250 kwargs['extra_arguments'] = self.kernel_argv
265 kwargs['extra_arguments'] = self.kernel_argv
251 kernel_id = self.kernel_manager.start_kernel(**kwargs)
266 kernel_id = super(MappingKernelManager, self).start_kernel(**kwargs)
252 self.set_kernel_for_notebook(notebook_id, kernel_id)
267 self.set_kernel_for_notebook(notebook_id, kernel_id)
253 self.log.info("Kernel started: %s" % kernel_id)
268 self.log.info("Kernel started: %s" % kernel_id)
254 self.log.debug("Kernel args: %r" % kwargs)
269 self.log.debug("Kernel args: %r" % kwargs)
255 self.start_session_manager(kernel_id)
256 else:
270 else:
257 self.log.info("Using existing kernel: %s" % kernel_id)
271 self.log.info("Using existing kernel: %s" % kernel_id)
258 return kernel_id
272 return kernel_id
259
273
260 def start_session_manager(self, kernel_id):
261 """Start the ZMQ sockets (a "session") to connect to a kernel."""
262 sm = self.kernel_manager.create_session_manager(kernel_id)
263 self._session_dict[kernel_id] = sm
264 iopub_stream = sm.get_iopub_stream()
265 shell_stream = sm.get_shell_stream()
266 iopub_router = IOPubStreamRouter(
267 zmq_stream=iopub_stream, session=sm.session, config=self.config
268 )
269 shell_router = ShellStreamRouter(
270 zmq_stream=shell_stream, session=sm.session, config=self.config
271 )
272 self.set_router(kernel_id, 'iopub', iopub_router)
273 self.set_router(kernel_id, 'shell', shell_router)
274
275 def kill_kernel(self, kernel_id):
274 def kill_kernel(self, kernel_id):
276 """Kill a kernel and remove its notebook association."""
275 """Kill a kernel and remove its notebook association."""
277 if kernel_id not in self.kernel_manager:
276 if kernel_id not in self:
278 raise web.HTTPError(404)
277 raise web.HTTPError(404)
279 try:
278 super(MappingKernelManager, self).kill_kernel(kernel_id)
280 sm = self._session_dict.pop(kernel_id)
281 except KeyError:
282 raise web.HTTPError(404)
283 sm.stop()
284 self.kernel_manager.kill_kernel(kernel_id)
285 self.delete_mapping_for_kernel(kernel_id)
279 self.delete_mapping_for_kernel(kernel_id)
286 self.log.info("Kernel killed: %s" % kernel_id)
280 self.log.info("Kernel killed: %s" % kernel_id)
287
281
288 def interrupt_kernel(self, kernel_id):
282 def interrupt_kernel(self, kernel_id):
289 """Interrupt a kernel."""
283 """Interrupt a kernel."""
290 if kernel_id not in self.kernel_manager:
284 if kernel_id not in self:
291 raise web.HTTPError(404)
285 raise web.HTTPError(404)
292 self.kernel_manager.interrupt_kernel(kernel_id)
286 super(MappingKernelManager, self).interrupt_kernel(kernel_id)
293 self.log.debug("Kernel interrupted: %s" % kernel_id)
287 self.log.info("Kernel interrupted: %s" % kernel_id)
294
288
295 def restart_kernel(self, kernel_id):
289 def restart_kernel(self, kernel_id):
296 """Restart a kernel while keeping clients connected."""
290 """Restart a kernel while keeping clients connected."""
297 if kernel_id not in self.kernel_manager:
291 if kernel_id not in self:
298 raise web.HTTPError(404)
292 raise web.HTTPError(404)
299
293
300 # Get the notebook_id to preserve the kernel/notebook association
294 # Get the notebook_id to preserve the kernel/notebook association.
301 notebook_id = self.notebook_for_kernel(kernel_id)
295 notebook_id = self.notebook_for_kernel(kernel_id)
302 # Create the new kernel first so we can move the clients over.
296 # Create the new kernel first so we can move the clients over.
303 new_kernel_id = self.start_kernel()
297 new_kernel_id = self.start_kernel()
304
298 # Now kill the old kernel.
305 # Copy the clients over to the new routers.
306 old_iopub_router = self.get_router(kernel_id, 'iopub')
307 old_shell_router = self.get_router(kernel_id, 'shell')
308 new_iopub_router = self.get_router(new_kernel_id, 'iopub')
309 new_shell_router = self.get_router(new_kernel_id, 'shell')
310 new_iopub_router.copy_clients(old_iopub_router)
311 new_shell_router.copy_clients(old_shell_router)
312
313 # Shut down the old routers
314 old_shell_router.close()
315 old_iopub_router.close()
316 self.delete_router(kernel_id, 'shell')
317 self.delete_router(kernel_id, 'iopub')
318 del old_shell_router
319 del old_iopub_router
320
321 # Now shutdown the old session and the kernel.
322 # TODO: This causes a hard crash in ZMQStream.close, which sets
323 # self.socket to None to hastily. We will need to fix this in PyZMQ
324 # itself. For now, we just leave the old kernel running :(
325 # Maybe this is fixed now, but nothing was changed really.
326 self.kill_kernel(kernel_id)
299 self.kill_kernel(kernel_id)
327
328 # Now save the new kernel/notebook association. We have to save it
300 # Now save the new kernel/notebook association. We have to save it
329 # after the old kernel is killed as that will delete the mapping.
301 # after the old kernel is killed as that will delete the mapping.
330 self.set_kernel_for_notebook(notebook_id, new_kernel_id)
302 self.set_kernel_for_notebook(notebook_id, new_kernel_id)
331
332 self.log.debug("Kernel restarted: %s" % new_kernel_id)
303 self.log.debug("Kernel restarted: %s" % new_kernel_id)
333 return new_kernel_id
304 return new_kernel_id
334
305
335 def get_router(self, kernel_id, stream_name):
336 """Return the router for a given kernel_id and stream name."""
337 router = self._routers[(kernel_id, stream_name)]
338 return router
339
340 def set_router(self, kernel_id, stream_name, router):
341 """Set the router for a given kernel_id and stream_name."""
342 self._routers[(kernel_id, stream_name)] = router
343
344 def delete_router(self, kernel_id, stream_name):
345 """Delete a router for a kernel_id and stream_name."""
346 try:
347 del self._routers[(kernel_id, stream_name)]
348 except KeyError:
349 pass
350
306
@@ -27,12 +27,11 b' tornado.ioloop = ioloop'
27 from tornado import httpserver
27 from tornado import httpserver
28 from tornado import web
28 from tornado import web
29
29
30 from .kernelmanager import KernelManager, RoutingKernelManager
30 from .kernelmanager import MappingKernelManager
31 from .sessionmanager import SessionManager
32 from .handlers import (
31 from .handlers import (
33 NBBrowserHandler, NewHandler, NamedNotebookHandler,
32 NBBrowserHandler, NewHandler, NamedNotebookHandler,
34 MainKernelHandler, KernelHandler, KernelActionHandler, ZMQStreamHandler,
33 MainKernelHandler, KernelHandler, KernelActionHandler, IOPubHandler,
35 NotebookRootHandler, NotebookHandler, RSTHandler
34 ShellHandler, NotebookRootHandler, NotebookHandler, RSTHandler
36 )
35 )
37 from .notebookmanager import NotebookManager
36 from .notebookmanager import NotebookManager
38
37
@@ -45,7 +44,7 b' from IPython.zmq.ipkernel import ('
45 aliases as ipkernel_aliases,
44 aliases as ipkernel_aliases,
46 IPKernelApp
45 IPKernelApp
47 )
46 )
48 from IPython.utils.traitlets import Dict, Unicode, Int, Any, List, Enum
47 from IPython.utils.traitlets import Dict, Unicode, Int, List, Enum
49
48
50 #-----------------------------------------------------------------------------
49 #-----------------------------------------------------------------------------
51 # Module globals
50 # Module globals
@@ -71,7 +70,7 b' ipython notebook --port=5555 --ip=* # Listen on port 5555, all interfaces'
71
70
72 class NotebookWebApplication(web.Application):
71 class NotebookWebApplication(web.Application):
73
72
74 def __init__(self, routing_kernel_manager, notebook_manager, log):
73 def __init__(self, kernel_manager, notebook_manager, log):
75 handlers = [
74 handlers = [
76 (r"/", NBBrowserHandler),
75 (r"/", NBBrowserHandler),
77 (r"/new", NewHandler),
76 (r"/new", NewHandler),
@@ -79,8 +78,8 b' class NotebookWebApplication(web.Application):'
79 (r"/kernels", MainKernelHandler),
78 (r"/kernels", MainKernelHandler),
80 (r"/kernels/%s" % _kernel_id_regex, KernelHandler),
79 (r"/kernels/%s" % _kernel_id_regex, KernelHandler),
81 (r"/kernels/%s/%s" % (_kernel_id_regex, _kernel_action_regex), KernelActionHandler),
80 (r"/kernels/%s/%s" % (_kernel_id_regex, _kernel_action_regex), KernelActionHandler),
82 (r"/kernels/%s/iopub" % _kernel_id_regex, ZMQStreamHandler, dict(stream_name='iopub')),
81 (r"/kernels/%s/iopub" % _kernel_id_regex, IOPubHandler),
83 (r"/kernels/%s/shell" % _kernel_id_regex, ZMQStreamHandler, dict(stream_name='shell')),
82 (r"/kernels/%s/shell" % _kernel_id_regex, ShellHandler),
84 (r"/notebooks", NotebookRootHandler),
83 (r"/notebooks", NotebookRootHandler),
85 (r"/notebooks/%s" % _notebook_id_regex, NotebookHandler),
84 (r"/notebooks/%s" % _notebook_id_regex, NotebookHandler),
86 (r"/rstservice/render", RSTHandler)
85 (r"/rstservice/render", RSTHandler)
@@ -91,7 +90,7 b' class NotebookWebApplication(web.Application):'
91 )
90 )
92 web.Application.__init__(self, handlers, **settings)
91 web.Application.__init__(self, handlers, **settings)
93
92
94 self.routing_kernel_manager = routing_kernel_manager
93 self.kernel_manager = kernel_manager
95 self.log = log
94 self.log = log
96 self.notebook_manager = notebook_manager
95 self.notebook_manager = notebook_manager
97
96
@@ -114,7 +113,6 b' aliases.update({'
114 'port': 'IPythonNotebookApp.port',
113 'port': 'IPythonNotebookApp.port',
115 'keyfile': 'IPythonNotebookApp.keyfile',
114 'keyfile': 'IPythonNotebookApp.keyfile',
116 'certfile': 'IPythonNotebookApp.certfile',
115 'certfile': 'IPythonNotebookApp.certfile',
117 'colors': 'ZMQInteractiveShell.colors',
118 'notebook-dir': 'NotebookManager.notebook_dir'
116 'notebook-dir': 'NotebookManager.notebook_dir'
119 })
117 })
120
118
@@ -136,8 +134,7 b' class IPythonNotebookApp(BaseIPythonApplication):'
136 examples = _examples
134 examples = _examples
137
135
138 classes = [IPKernelApp, ZMQInteractiveShell, ProfileDir, Session,
136 classes = [IPKernelApp, ZMQInteractiveShell, ProfileDir, Session,
139 RoutingKernelManager, NotebookManager,
137 MappingKernelManager, NotebookManager]
140 KernelManager, SessionManager]
141 flags = Dict(flags)
138 flags = Dict(flags)
142 aliases = Dict(aliases)
139 aliases = Dict(aliases)
143
140
@@ -187,9 +184,8 b' class IPythonNotebookApp(BaseIPythonApplication):'
187 signal.signal(signal.SIGINT, signal.SIG_DFL)
184 signal.signal(signal.SIGINT, signal.SIG_DFL)
188
185
189 # Create a KernelManager and start a kernel.
186 # Create a KernelManager and start a kernel.
190 self.kernel_manager = KernelManager(config=self.config, log=self.log)
187 self.kernel_manager = MappingKernelManager(
191 self.routing_kernel_manager = RoutingKernelManager(config=self.config, log=self.log,
188 config=self.config, log=self.log, kernel_argv=self.kernel_argv
192 kernel_manager=self.kernel_manager, kernel_argv=self.kernel_argv
193 )
189 )
194 self.notebook_manager = NotebookManager(config=self.config, log=self.log)
190 self.notebook_manager = NotebookManager(config=self.config, log=self.log)
195
191
@@ -204,7 +200,7 b' class IPythonNotebookApp(BaseIPythonApplication):'
204 super(IPythonNotebookApp, self).initialize(argv)
200 super(IPythonNotebookApp, self).initialize(argv)
205 self.init_configurables()
201 self.init_configurables()
206 self.web_app = NotebookWebApplication(
202 self.web_app = NotebookWebApplication(
207 self.routing_kernel_manager, self.notebook_manager, self.log
203 self.kernel_manager, self.notebook_manager, self.log
208 )
204 )
209 if self.certfile:
205 if self.certfile:
210 ssl_options = dict(certfile=self.certfile)
206 ssl_options = dict(certfile=self.certfile)
@@ -11,6 +11,9 b' var IPython = (function (IPython) {'
11 this.kernel_id = null;
11 this.kernel_id = null;
12 this.base_url = "/kernels";
12 this.base_url = "/kernels";
13 this.kernel_url = null;
13 this.kernel_url = null;
14 this.shell_channel = null;
15 this.iopub_channel = null;
16 this.running = false;
14 };
17 };
15
18
16
19
@@ -28,33 +31,65 b' var IPython = (function (IPython) {'
28 return msg;
31 return msg;
29 }
32 }
30
33
31 Kernel.prototype.start_kernel = function (notebook_id, callback) {
34 Kernel.prototype.start = function (notebook_id, callback) {
32 var that = this;
35 var that = this;
33 var qs = $.param({notebook:notebook_id});
36 if (!this.running) {
34 $.post(this.base_url + '?' + qs,
37 var qs = $.param({notebook:notebook_id});
35 function (kernel_id) {
38 $.post(this.base_url + '?' + qs,
36 that._handle_start_kernel(kernel_id, callback);
39 function (kernel_id) {
37 },
40 that._handle_start_kernel(kernel_id, callback);
38 'json'
41 },
39 );
42 'json'
43 );
44 };
45 };
46
47
48 Kernel.prototype.restart = function (callback) {
49 IPython.kernel_status_widget.status_restarting();
50 var url = this.kernel_url + "/restart";
51 var that = this;
52 if (this.running) {
53 this.stop_channels();
54 $.post(url,
55 function (kernel_id) {
56 that._handle_start_kernel(kernel_id, callback);
57 },
58 'json'
59 );
60 };
40 };
61 };
41
62
42
63
43 Kernel.prototype._handle_start_kernel = function (kernel_id, callback) {
64 Kernel.prototype._handle_start_kernel = function (kernel_id, callback) {
65 this.running = true;
44 this.kernel_id = kernel_id;
66 this.kernel_id = kernel_id;
45 this.kernel_url = this.base_url + "/" + this.kernel_id;
67 this.kernel_url = this.base_url + "/" + this.kernel_id;
46 this._start_channels();
68 this.start_channels();
47 callback();
69 callback();
70 IPython.kernel_status_widget.status_idle();
48 };
71 };
49
72
50
73
51 Kernel.prototype._start_channels = function () {
74 Kernel.prototype.start_channels = function () {
75 this.stop_channels();
52 var ws_url = "ws://127.0.0.1:8888" + this.kernel_url;
76 var ws_url = "ws://127.0.0.1:8888" + this.kernel_url;
53 this.shell_channel = new WebSocket(ws_url + "/shell");
77 this.shell_channel = new WebSocket(ws_url + "/shell");
54 this.iopub_channel = new WebSocket(ws_url + "/iopub");
78 this.iopub_channel = new WebSocket(ws_url + "/iopub");
55 }
79 };
56
80
57
81
82 Kernel.prototype.stop_channels = function () {
83 if (this.shell_channel !== null) {
84 this.shell_channel.close();
85 this.shell_channel = null;
86 };
87 if (this.iopub_channel !== null) {
88 this.iopub_channel.close();
89 this.iopub_channel = null;
90 };
91 };
92
58 Kernel.prototype.execute = function (code) {
93 Kernel.prototype.execute = function (code) {
59 var content = {
94 var content = {
60 code : code,
95 code : code,
@@ -81,29 +116,21 b' var IPython = (function (IPython) {'
81
116
82
117
83 Kernel.prototype.interrupt = function () {
118 Kernel.prototype.interrupt = function () {
84 $.post(this.kernel_url + "/interrupt");
119 if (this.running) {
85 };
120 $.post(this.kernel_url + "/interrupt");
86
121 };
87
88 Kernel.prototype.restart = function () {
89 IPython.kernel_status_widget.status_restarting();
90 var url = this.kernel_url + "/restart"
91 var that = this;
92 $.post(url, function (kernel_id) {
93 console.log("Kernel restarted: " + kernel_id);
94 that.kernel_id = kernel_id;
95 that.kernel_url = that.base_url + "/" + that.kernel_id;
96 IPython.kernel_status_widget.status_idle();
97 }, 'json');
98 };
122 };
99
123
100
124
101 Kernel.prototype.kill = function () {
125 Kernel.prototype.kill = function () {
102 var settings = {
126 if (this.running) {
103 cache : false,
127 this.running = false;
104 type : "DELETE",
128 var settings = {
129 cache : false,
130 type : "DELETE",
131 };
132 $.ajax(this.kernel_url, settings);
105 };
133 };
106 $.ajax(this.kernel_url, settings);
107 };
134 };
108
135
109 IPython.Kernel = Kernel;
136 IPython.Kernel = Kernel;
@@ -482,7 +482,20 b' var IPython = (function (IPython) {'
482 Notebook.prototype.start_kernel = function () {
482 Notebook.prototype.start_kernel = function () {
483 this.kernel = new IPython.Kernel();
483 this.kernel = new IPython.Kernel();
484 var notebook_id = IPython.save_widget.get_notebook_id();
484 var notebook_id = IPython.save_widget.get_notebook_id();
485 this.kernel.start_kernel(notebook_id, $.proxy(this.kernel_started, this));
485 this.kernel.start(notebook_id, $.proxy(this.kernel_started, this));
486 };
487
488
489 Notebook.prototype.restart_kernel = function () {
490 var notebook_id = IPython.save_widget.get_notebook_id();
491 this.kernel.restart($.proxy(this.kernel_started, this));
492 };
493
494
495 Notebook.prototype.kernel_started = function () {
496 console.log("Kernel started: ", this.kernel.kernel_id);
497 this.kernel.shell_channel.onmessage = $.proxy(this.handle_shell_reply,this);
498 this.kernel.iopub_channel.onmessage = $.proxy(this.handle_iopub_reply,this);
486 };
499 };
487
500
488
501
@@ -528,16 +541,41 b' var IPython = (function (IPython) {'
528 var output_types = ['stream','display_data','pyout','pyerr'];
541 var output_types = ['stream','display_data','pyout','pyerr'];
529 if (output_types.indexOf(msg_type) >= 0) {
542 if (output_types.indexOf(msg_type) >= 0) {
530 this.handle_output(cell, msg_type, content);
543 this.handle_output(cell, msg_type, content);
531 } else if (msg_type === "status") {
544 } else if (msg_type === 'status') {
532 if (content.execution_state === "busy") {
545 if (content.execution_state === 'busy') {
533 IPython.kernel_status_widget.status_busy();
546 IPython.kernel_status_widget.status_busy();
534 } else if (content.execution_state === "idle") {
547 } else if (content.execution_state === 'idle') {
535 IPython.kernel_status_widget.status_idle();
548 IPython.kernel_status_widget.status_idle();
549 } else if (content.execution_state === 'dead') {
550 this.handle_status_dead();
536 };
551 };
537 }
552 }
538 };
553 };
539
554
540
555
556 Notebook.prototype.handle_status_dead = function () {
557 var that = this;
558 this.kernel.stop_channels();
559 var dialog = $('<div/>');
560 dialog.html('The kernel has died, would you like to restart it? If you do not restart the kernel, you will be able to save the notebook, but running code will not work until the notebook is reopened.');
561 $(document).append(dialog);
562 dialog.dialog({
563 resizable: false,
564 modal: true,
565 title: "Dead kernel",
566 buttons : {
567 "Yes": function () {
568 that.start_kernel();
569 $(this).dialog('close');
570 },
571 "No": function () {
572 $(this).dialog('close');
573 }
574 }
575 });
576 };
577
578
541 Notebook.prototype.handle_output = function (cell, msg_type, content) {
579 Notebook.prototype.handle_output = function (cell, msg_type, content) {
542 var json = {};
580 var json = {};
543 json.output_type = msg_type;
581 json.output_type = msg_type;
@@ -589,12 +627,6 b' var IPython = (function (IPython) {'
589 return json;
627 return json;
590 };
628 };
591
629
592 Notebook.prototype.kernel_started = function () {
593 console.log("Kernel started: ", this.kernel.kernel_id);
594 this.kernel.shell_channel.onmessage = $.proxy(this.handle_shell_reply,this);
595 this.kernel.iopub_channel.onmessage = $.proxy(this.handle_iopub_reply,this);
596 };
597
598
630
599 Notebook.prototype.execute_selected_cell = function (options) {
631 Notebook.prototype.execute_selected_cell = function (options) {
600 // add_new: should a new cell be added if we are at the end of the nb
632 // add_new: should a new cell be added if we are at the end of the nb
@@ -198,7 +198,7 b' var IPython = (function (IPython) {'
198 KernelSection.prototype.bind_events = function () {
198 KernelSection.prototype.bind_events = function () {
199 PanelSection.prototype.bind_events.apply(this);
199 PanelSection.prototype.bind_events.apply(this);
200 this.content.find('#restart_kernel').click(function () {
200 this.content.find('#restart_kernel').click(function () {
201 IPython.notebook.kernel.restart();
201 IPython.notebook.restart_kernel();
202 });
202 });
203 this.content.find('#int_kernel').click(function () {
203 this.content.find('#int_kernel').click(function () {
204 IPython.notebook.kernel.interrupt();
204 IPython.notebook.kernel.interrupt();
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now