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 |
|
|
57 | km = self.application.kernel_manager | |
56 |
self.finish(json.dumps( |
|
58 | self.finish(jsonapi.dumps(km.kernel_ids)) | |
57 |
|
59 | |||
58 | def post(self): |
|
60 | def post(self): | |
59 |
|
|
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 = |
|
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 |
|
|
73 | km = self.application.kernel_manager | |
72 |
|
|
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 |
|
|
82 | km = self.application.kernel_manager | |
81 | if action == 'interrupt': |
|
83 | if action == 'interrupt': | |
82 |
|
|
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 = |
|
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 |
|
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 |
|
|
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 |
|
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 |
|
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 |
|
284 | if kernel_id not in self: | |
291 | raise web.HTTPError(404) |
|
285 | raise web.HTTPError(404) | |
292 |
self |
|
286 | super(MappingKernelManager, self).interrupt_kernel(kernel_id) | |
293 |
self.log. |
|
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 |
|
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 |
|
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, |
|
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, |
|
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, |
|
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, |
|
81 | (r"/kernels/%s/iopub" % _kernel_id_regex, IOPubHandler), | |
83 |
(r"/kernels/%s/shell" % _kernel_id_regex, |
|
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. |
|
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 |
|
|
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( |
|
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. |
|
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 |
|
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 |
|
|
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. |
|
68 | this.start_channels(); | |
47 | callback(); |
|
69 | callback(); | |
|
70 | IPython.kernel_status_widget.status_idle(); | |||
48 | }; |
|
71 | }; | |
49 |
|
72 | |||
50 |
|
73 | |||
51 |
Kernel.prototype. |
|
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 |
|
|
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 |
|
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 === |
|
544 | } else if (msg_type === 'status') { | |
532 |
if (content.execution_state === |
|
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 === |
|
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. |
|
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