Show More
@@ -13,7 +13,7 b' from tornado import websocket' | |||
|
13 | 13 | |
|
14 | 14 | |
|
15 | 15 | #----------------------------------------------------------------------------- |
|
16 |
# |
|
|
16 | # Top-level handlers | |
|
17 | 17 | #----------------------------------------------------------------------------- |
|
18 | 18 | |
|
19 | 19 | |
@@ -38,25 +38,45 b' class NamedNotebookHandler(web.RequestHandler):' | |||
|
38 | 38 | self.render('notebook.html', notebook_id=notebook_id) |
|
39 | 39 | |
|
40 | 40 | |
|
41 | class KernelHandler(web.RequestHandler): | |
|
41 | #----------------------------------------------------------------------------- | |
|
42 | # Kernel handlers | |
|
43 | #----------------------------------------------------------------------------- | |
|
44 | ||
|
45 | ||
|
46 | class MainKernelHandler(web.RequestHandler): | |
|
42 | 47 | |
|
43 | 48 | def get(self): |
|
44 | self.finish(json.dumps(self.application.kernel_ids)) | |
|
49 | rkm = self.application.routing_kernel_manager | |
|
50 | self.finish(json.dumps(rkm.kernel_ids)) | |
|
45 | 51 | |
|
46 | 52 | def post(self): |
|
47 |
|
|
|
53 | rkm = self.application.routing_kernel_manager | |
|
54 | notebook_id = self.get_argument('notebook', default=None) | |
|
55 | kernel_id = rkm.start_kernel(notebook_id) | |
|
48 | 56 | self.set_header('Location', '/'+kernel_id) |
|
49 | 57 | self.finish(json.dumps(kernel_id)) |
|
50 | 58 | |
|
51 | 59 | |
|
60 | class KernelHandler(web.RequestHandler): | |
|
61 | ||
|
62 | SUPPORTED_METHODS = ('DELETE') | |
|
63 | ||
|
64 | def delete(self, kernel_id): | |
|
65 | rkm = self.application.routing_kernel_manager | |
|
66 | self.kill_kernel(kernel_id) | |
|
67 | self.set_status(204) | |
|
68 | self.finish() | |
|
69 | ||
|
70 | ||
|
52 | 71 | class KernelActionHandler(web.RequestHandler): |
|
53 | 72 | |
|
54 | 73 | def post(self, kernel_id, action): |
|
55 | # TODO: figure out a better way of handling RPC style calls. | |
|
74 | rkm = self.application.routing_kernel_manager | |
|
56 | 75 | if action == 'interrupt': |
|
57 |
|
|
|
76 | rkm.interrupt_kernel(kernel_id) | |
|
77 | self.set_status(204) | |
|
58 | 78 | if action == 'restart': |
|
59 |
new_kernel_id = |
|
|
79 | new_kernel_id = rkm.restart_kernel(kernel_id) | |
|
60 | 80 | self.write(json.dumps(new_kernel_id)) |
|
61 | 81 | self.finish() |
|
62 | 82 | |
@@ -67,7 +87,8 b' class ZMQStreamHandler(websocket.WebSocketHandler):' | |||
|
67 | 87 | self.stream_name = stream_name |
|
68 | 88 | |
|
69 | 89 | def open(self, kernel_id): |
|
70 |
|
|
|
90 | rkm = self.application.routing_kernel_manager | |
|
91 | self.router = rkm.get_router(kernel_id, self.stream_name) | |
|
71 | 92 | self.client_id = self.router.register_client(self) |
|
72 | 93 | logging.info("Connection open: %s, %s" % (kernel_id, self.client_id)) |
|
73 | 94 | |
@@ -79,6 +100,10 b' class ZMQStreamHandler(websocket.WebSocketHandler):' | |||
|
79 | 100 | logging.info("Connection closed: %s" % self.client_id) |
|
80 | 101 | |
|
81 | 102 | |
|
103 | #----------------------------------------------------------------------------- | |
|
104 | # Notebook web service handlers | |
|
105 | #----------------------------------------------------------------------------- | |
|
106 | ||
|
82 | 107 | class NotebookRootHandler(web.RequestHandler): |
|
83 | 108 | |
|
84 | 109 | def get(self): |
@@ -17,9 +17,13 b' import uuid' | |||
|
17 | 17 | |
|
18 | 18 | import zmq |
|
19 | 19 | |
|
20 | from tornado import web | |
|
21 | ||
|
22 | from .routers import IOPubStreamRouter, ShellStreamRouter | |
|
23 | ||
|
20 | 24 | from IPython.config.configurable import LoggingConfigurable |
|
21 | 25 | from IPython.zmq.ipkernel import launch_kernel |
|
22 | from IPython.utils.traitlets import Instance, Dict | |
|
26 | from IPython.utils.traitlets import Instance, Dict, List, Unicode | |
|
23 | 27 | |
|
24 | 28 | #----------------------------------------------------------------------------- |
|
25 | 29 | # Classes |
@@ -55,7 +59,7 b' class KernelManager(LoggingConfigurable):' | |||
|
55 | 59 | |
|
56 | 60 | def start_kernel(self, **kwargs): |
|
57 | 61 | """Start a new kernel.""" |
|
58 |
kernel_id = |
|
|
62 | kernel_id = unicode(uuid.uuid4()) | |
|
59 | 63 | (process, shell_port, iopub_port, stdin_port, hb_port) = launch_kernel(**kwargs) |
|
60 | 64 | # Store the information for contacting the kernel. This assumes the kernel is |
|
61 | 65 | # running on localhost. |
@@ -186,3 +190,117 b' class KernelManager(LoggingConfigurable):' | |||
|
186 | 190 | config=self.config, context=self.context, log=self.log |
|
187 | 191 | ) |
|
188 | 192 | |
|
193 | ||
|
194 | class RoutingKernelManager(LoggingConfigurable): | |
|
195 | """A KernelManager that handles WebSocket routing and HTTP error handling""" | |
|
196 | ||
|
197 | kernel_argv = List(Unicode) | |
|
198 | kernel_manager = Instance(KernelManager) | |
|
199 | ||
|
200 | _routers = Dict() | |
|
201 | _session_dict = Dict() | |
|
202 | _notebook_mapping = Dict() | |
|
203 | ||
|
204 | #------------------------------------------------------------------------- | |
|
205 | # Methods for managing kernels and sessions | |
|
206 | #------------------------------------------------------------------------- | |
|
207 | ||
|
208 | @property | |
|
209 | def kernel_ids(self): | |
|
210 | return self.kernel_manager.kernel_ids | |
|
211 | ||
|
212 | def notebook_for_kernel(self, kernel_id): | |
|
213 | notebook_ids = [k for k, v in self._notebook_mapping.iteritems() if v == kernel_id] | |
|
214 | if len(notebook_ids) == 1: | |
|
215 | return notebook_ids[0] | |
|
216 | else: | |
|
217 | return None | |
|
218 | ||
|
219 | def delete_mapping_for_kernel(self, kernel_id): | |
|
220 | notebook_id = self.notebook_for_kernel(kernel_id) | |
|
221 | if notebook_id is not None: | |
|
222 | del self._notebook_mapping[notebook_id] | |
|
223 | ||
|
224 | def start_kernel(self, notebook_id=None): | |
|
225 | self.log.info | |
|
226 | kernel_id = self._notebook_mapping.get(notebook_id) | |
|
227 | if kernel_id is None: | |
|
228 | kwargs = dict() | |
|
229 | kwargs['extra_arguments'] = self.kernel_argv | |
|
230 | kernel_id = self.kernel_manager.start_kernel(**kwargs) | |
|
231 | if notebook_id is not None: | |
|
232 | self._notebook_mapping[notebook_id] = kernel_id | |
|
233 | self.log.info("Kernel started for notebook %s: %s" % (notebook_id,kernel_id)) | |
|
234 | self.log.debug("Kernel args: %r" % kwargs) | |
|
235 | self.start_session_manager(kernel_id) | |
|
236 | else: | |
|
237 | self.log.info("Using existing kernel: %s" % kernel_id) | |
|
238 | return kernel_id | |
|
239 | ||
|
240 | def start_session_manager(self, kernel_id): | |
|
241 | sm = self.kernel_manager.create_session_manager(kernel_id) | |
|
242 | self._session_dict[kernel_id] = sm | |
|
243 | iopub_stream = sm.get_iopub_stream() | |
|
244 | shell_stream = sm.get_shell_stream() | |
|
245 | iopub_router = IOPubStreamRouter( | |
|
246 | zmq_stream=iopub_stream, session=sm.session, config=self.config | |
|
247 | ) | |
|
248 | shell_router = ShellStreamRouter( | |
|
249 | zmq_stream=shell_stream, session=sm.session, config=self.config | |
|
250 | ) | |
|
251 | self._routers[(kernel_id, 'iopub')] = iopub_router | |
|
252 | self._routers[(kernel_id, 'shell')] = shell_router | |
|
253 | ||
|
254 | def kill_kernel(self, kernel_id): | |
|
255 | if kernel_id not in self.kernel_manager: | |
|
256 | raise web.HTTPError(404) | |
|
257 | try: | |
|
258 | sm = self._session_dict.pop(kernel_id) | |
|
259 | except KeyError: | |
|
260 | raise web.HTTPError(404) | |
|
261 | sm.stop() | |
|
262 | self.kernel_manager.kill_kernel(kernel_id) | |
|
263 | self.delete_mapping_for_kernel(kernel_id) | |
|
264 | self.log.info("Kernel killed: %s" % kernel_id) | |
|
265 | ||
|
266 | def interrupt_kernel(self, kernel_id): | |
|
267 | if kernel_id not in self.kernel_manager: | |
|
268 | raise web.HTTPError(404) | |
|
269 | self.kernel_manager.interrupt_kernel(kernel_id) | |
|
270 | self.log.debug("Kernel interrupted: %s" % kernel_id) | |
|
271 | ||
|
272 | def restart_kernel(self, kernel_id): | |
|
273 | if kernel_id not in self.kernel_manager: | |
|
274 | raise web.HTTPError(404) | |
|
275 | ||
|
276 | # Get the notebook_id to preserve the kernel/notebook association | |
|
277 | notebook_id = self.notebook_for_kernel(kernel_id) | |
|
278 | # Create the new kernel first so we can move the clients over. | |
|
279 | new_kernel_id = self.start_kernel() | |
|
280 | ||
|
281 | # Copy the clients over to the new routers. | |
|
282 | old_iopub_router = self.get_router(kernel_id, 'iopub') | |
|
283 | old_shell_router = self.get_router(kernel_id, 'shell') | |
|
284 | new_iopub_router = self.get_router(new_kernel_id, 'iopub') | |
|
285 | new_shell_router = self.get_router(new_kernel_id, 'shell') | |
|
286 | new_iopub_router.copy_clients(old_iopub_router) | |
|
287 | new_shell_router.copy_clients(old_shell_router) | |
|
288 | ||
|
289 | # Now shutdown the old session and the kernel. | |
|
290 | # TODO: This causes a hard crash in ZMQStream.close, which sets | |
|
291 | # self.socket to None to hastily. We will need to fix this in PyZMQ | |
|
292 | # itself. For now, we just leave the old kernel running :( | |
|
293 | # Maybe this is fixed now, but nothing was changed really. | |
|
294 | self.kill_kernel(kernel_id) | |
|
295 | ||
|
296 | # Now save the new kernel/notebook association. We have to save it | |
|
297 | # after the old kernel is killed as that will delete the mapping. | |
|
298 | self._notebook_mapping[notebook_id] = kernel_id | |
|
299 | ||
|
300 | self.log.debug("Kernel restarted: %s -> %s" % (kernel_id, new_kernel_id)) | |
|
301 | return new_kernel_id | |
|
302 | ||
|
303 | def get_router(self, kernel_id, stream_name): | |
|
304 | router = self._routers[(kernel_id, stream_name)] | |
|
305 | return router | |
|
306 |
@@ -27,14 +27,13 b' tornado.ioloop = ioloop' | |||
|
27 | 27 | from tornado import httpserver |
|
28 | 28 | from tornado import web |
|
29 | 29 | |
|
30 | from .kernelmanager import KernelManager | |
|
30 | from .kernelmanager import KernelManager, RoutingKernelManager | |
|
31 | 31 | from .sessionmanager import SessionManager |
|
32 | 32 | from .handlers import ( |
|
33 | 33 | NBBrowserHandler, NewHandler, NamedNotebookHandler, |
|
34 | KernelHandler, KernelActionHandler, ZMQStreamHandler, | |
|
34 | MainKernelHandler, KernelHandler, KernelActionHandler, ZMQStreamHandler, | |
|
35 | 35 | NotebookRootHandler, NotebookHandler |
|
36 | 36 | ) |
|
37 | from .routers import IOPubStreamRouter, ShellStreamRouter | |
|
38 | 37 | from .notebookmanager import NotebookManager |
|
39 | 38 | |
|
40 | 39 | from IPython.core.application import BaseIPythonApplication |
@@ -65,12 +64,13 b" LOCALHOST = '127.0.0.1'" | |||
|
65 | 64 | |
|
66 | 65 | class NotebookWebApplication(web.Application): |
|
67 | 66 | |
|
68 |
def __init__(self, kernel_manager, log |
|
|
67 | def __init__(self, routing_kernel_manager, notebook_manager, log): | |
|
69 | 68 | handlers = [ |
|
70 | 69 | (r"/", NBBrowserHandler), |
|
71 | 70 | (r"/new", NewHandler), |
|
72 | 71 | (r"/%s" % _notebook_id_regex, NamedNotebookHandler), |
|
73 | (r"/kernels", KernelHandler), | |
|
72 | (r"/kernels", MainKernelHandler), | |
|
73 | (r"/kernels/%s" % _kernel_id_regex, KernelHandler), | |
|
74 | 74 | (r"/kernels/%s/%s" % (_kernel_id_regex, _kernel_action_regex), KernelActionHandler), |
|
75 | 75 | (r"/kernels/%s/iopub" % _kernel_id_regex, ZMQStreamHandler, dict(stream_name='iopub')), |
|
76 | 76 | (r"/kernels/%s/shell" % _kernel_id_regex, ZMQStreamHandler, dict(stream_name='shell')), |
@@ -83,81 +83,9 b' class NotebookWebApplication(web.Application):' | |||
|
83 | 83 | ) |
|
84 | 84 | web.Application.__init__(self, handlers, **settings) |
|
85 | 85 | |
|
86 | self.kernel_manager = kernel_manager | |
|
86 | self.routing_kernel_manager = routing_kernel_manager | |
|
87 | 87 | self.log = log |
|
88 | self.kernel_argv = kernel_argv | |
|
89 | self.config = config | |
|
90 | self._routers = {} | |
|
91 | self._session_dict = {} | |
|
92 | self.notebook_manager = NotebookManager(config=self.config) | |
|
93 | ||
|
94 | #------------------------------------------------------------------------- | |
|
95 | # Methods for managing kernels and sessions | |
|
96 | #------------------------------------------------------------------------- | |
|
97 | ||
|
98 | @property | |
|
99 | def kernel_ids(self): | |
|
100 | return self.kernel_manager.kernel_ids | |
|
101 | ||
|
102 | def start_kernel(self): | |
|
103 | kwargs = dict() | |
|
104 | kwargs['extra_arguments'] = self.kernel_argv | |
|
105 | kernel_id = self.kernel_manager.start_kernel(**kwargs) | |
|
106 | self.log.info("Kernel started: %s" % kernel_id) | |
|
107 | self.log.debug("Kernel args: %r" % kwargs) | |
|
108 | self.start_session_manager(kernel_id) | |
|
109 | return kernel_id | |
|
110 | ||
|
111 | def start_session_manager(self, kernel_id): | |
|
112 | sm = self.kernel_manager.create_session_manager(kernel_id) | |
|
113 | self._session_dict[kernel_id] = sm | |
|
114 | iopub_stream = sm.get_iopub_stream() | |
|
115 | shell_stream = sm.get_shell_stream() | |
|
116 | iopub_router = IOPubStreamRouter( | |
|
117 | zmq_stream=iopub_stream, session=sm.session, config=self.config | |
|
118 | ) | |
|
119 | shell_router = ShellStreamRouter( | |
|
120 | zmq_stream=shell_stream, session=sm.session, config=self.config | |
|
121 | ) | |
|
122 | self._routers[(kernel_id, 'iopub')] = iopub_router | |
|
123 | self._routers[(kernel_id, 'shell')] = shell_router | |
|
124 | ||
|
125 | def kill_kernel(self, kernel_id): | |
|
126 | sm = self._session_dict.pop(kernel_id) | |
|
127 | sm.stop() | |
|
128 | self.kernel_manager.kill_kernel(kernel_id) | |
|
129 | self.log.info("Kernel killed: %s" % kernel_id) | |
|
130 | ||
|
131 | def interrupt_kernel(self, kernel_id): | |
|
132 | self.kernel_manager.interrupt_kernel(kernel_id) | |
|
133 | self.log.debug("Kernel interrupted: %s" % kernel_id) | |
|
134 | ||
|
135 | def restart_kernel(self, kernel_id): | |
|
136 | # Create the new kernel first so we can move the clients over. | |
|
137 | new_kernel_id = self.start_kernel() | |
|
138 | ||
|
139 | # Copy the clients over to the new routers. | |
|
140 | old_iopub_router = self.get_router(kernel_id, 'iopub') | |
|
141 | old_shell_router = self.get_router(kernel_id, 'shell') | |
|
142 | new_iopub_router = self.get_router(new_kernel_id, 'iopub') | |
|
143 | new_shell_router = self.get_router(new_kernel_id, 'shell') | |
|
144 | new_iopub_router.copy_clients(old_iopub_router) | |
|
145 | new_shell_router.copy_clients(old_shell_router) | |
|
146 | ||
|
147 | # Now shutdown the old session and the kernel. | |
|
148 | # TODO: This causes a hard crash in ZMQStream.close, which sets | |
|
149 | # self.socket to None to hastily. We will need to fix this in PyZMQ | |
|
150 | # itself. For now, we just leave the old kernel running :( | |
|
151 | # Maybe this is fixed now, but nothing was changed really. | |
|
152 | self.kill_kernel(kernel_id) | |
|
153 | ||
|
154 | self.log.debug("Kernel restarted: %s -> %s" % (kernel_id, new_kernel_id)) | |
|
155 | return new_kernel_id | |
|
156 | ||
|
157 | def get_router(self, kernel_id, stream_name): | |
|
158 | router = self._routers[(kernel_id, stream_name)] | |
|
159 | return router | |
|
160 | ||
|
88 | self.notebook_manager = notebook_manager | |
|
161 | 89 | |
|
162 | 90 | |
|
163 | 91 | #----------------------------------------------------------------------------- |
@@ -196,6 +124,7 b' class IPythonNotebookApp(BaseIPythonApplication):' | |||
|
196 | 124 | """ |
|
197 | 125 | |
|
198 | 126 | classes = [IPKernelApp, ZMQInteractiveShell, ProfileDir, Session, |
|
127 | RoutingKernelManager, NotebookManager, | |
|
199 | 128 | KernelManager, SessionManager, RichIPythonWidget] |
|
200 | 129 | flags = Dict(flags) |
|
201 | 130 | aliases = Dict(aliases) |
@@ -232,12 +161,16 b' class IPythonNotebookApp(BaseIPythonApplication):' | |||
|
232 | 161 | if a.startswith('-') and a.lstrip('-') in notebook_flags: |
|
233 | 162 | self.kernel_argv.remove(a) |
|
234 | 163 | |
|
235 |
def init_ |
|
|
164 | def init_configurables(self): | |
|
236 | 165 | # Don't let Qt or ZMQ swallow KeyboardInterupts. |
|
237 | 166 | signal.signal(signal.SIGINT, signal.SIG_DFL) |
|
238 | 167 | |
|
239 | 168 | # Create a KernelManager and start a kernel. |
|
240 | 169 | self.kernel_manager = KernelManager(config=self.config, log=self.log) |
|
170 | self.routing_kernel_manager = RoutingKernelManager(config=self.config, log=self.log, | |
|
171 | kernel_manager=self.kernel_manager, kernel_argv=self.kernel_argv | |
|
172 | ) | |
|
173 | self.notebook_manager = NotebookManager(config=self.config, log=self.log) | |
|
241 | 174 | |
|
242 | 175 | def init_logging(self): |
|
243 | 176 | super(IPythonNotebookApp, self).init_logging() |
@@ -248,9 +181,9 b' class IPythonNotebookApp(BaseIPythonApplication):' | |||
|
248 | 181 | |
|
249 | 182 | def initialize(self, argv=None): |
|
250 | 183 | super(IPythonNotebookApp, self).initialize(argv) |
|
251 |
self.init_ |
|
|
184 | self.init_configurables() | |
|
252 | 185 | self.web_app = NotebookWebApplication( |
|
253 |
self.kernel_manager, self.log |
|
|
186 | self.routing_kernel_manager, self.notebook_manager, self.log | |
|
254 | 187 | ) |
|
255 | 188 | self.http_server = httpserver.HTTPServer(self.web_app) |
|
256 | 189 | self.http_server.listen(self.port) |
@@ -15,7 +15,7 b' import uuid' | |||
|
15 | 15 | |
|
16 | 16 | from tornado import web |
|
17 | 17 | |
|
18 | from IPython.config.configurable import Configurable | |
|
18 | from IPython.config.configurable import LoggingConfigurable | |
|
19 | 19 | from IPython.nbformat import current |
|
20 | 20 | from IPython.utils.traitlets import Unicode, List, Dict |
|
21 | 21 | |
@@ -25,7 +25,7 b' from IPython.utils.traitlets import Unicode, List, Dict' | |||
|
25 | 25 | #----------------------------------------------------------------------------- |
|
26 | 26 | |
|
27 | 27 | |
|
28 | class NotebookManager(Configurable): | |
|
28 | class NotebookManager(LoggingConfigurable): | |
|
29 | 29 | |
|
30 | 30 | notebook_dir = Unicode(os.getcwd()) |
|
31 | 31 | filename_ext = Unicode(u'.ipynb') |
@@ -28,9 +28,10 b' var IPython = (function (IPython) {' | |||
|
28 | 28 | return msg; |
|
29 | 29 | } |
|
30 | 30 | |
|
31 | Kernel.prototype.start_kernel = function (callback) { | |
|
31 | Kernel.prototype.start_kernel = function (notebook_id, callback) { | |
|
32 | 32 | var that = this; |
|
33 | $.post(this.base_url, | |
|
33 | var qs = $.param({notebook:notebook_id}); | |
|
34 | $.post(this.base_url + '?' + qs, | |
|
34 | 35 | function (kernel_id) { |
|
35 | 36 | that._handle_start_kernel(kernel_id, callback); |
|
36 | 37 | }, |
@@ -391,7 +391,8 b' var IPython = (function (IPython) {' | |||
|
391 | 391 | |
|
392 | 392 | Notebook.prototype.start_kernel = function () { |
|
393 | 393 | this.kernel = new IPython.Kernel(); |
|
394 | this.kernel.start_kernel($.proxy(this.kernel_started, this)); | |
|
394 | var notebook_id = IPython.save_widget.get_notebook_id(); | |
|
395 | this.kernel.start_kernel(notebook_id, $.proxy(this.kernel_started, this)); | |
|
395 | 396 | }; |
|
396 | 397 | |
|
397 | 398 |
General Comments 0
You need to be logged in to leave comments.
Login now