diff --git a/IPython/frontend/html/notebook/kernelmanager.py b/IPython/frontend/html/notebook/kernelmanager.py index 9a94f2c..48f61a0 100644 --- a/IPython/frontend/html/notebook/kernelmanager.py +++ b/IPython/frontend/html/notebook/kernelmanager.py @@ -1,11 +1,14 @@ import signal import sys -import uuid from IPython.zmq.ipkernel import launch_kernel from session import SessionManager +class DuplicateKernelError(Exception): + pass + + class KernelManager(object): ip = '127.0.0.1' @@ -27,8 +30,9 @@ class KernelManager(object): else: return False - def start_kernel(self): - kid = str(uuid.uuid4()) + def start_kernel(self, kernel_id): + if kernel_id in self._kernels: + raise DuplicateKernelError("Kernel already exists: %s" % kernel_id) (process, shell_port, iopub_port, stdin_port, hb_port) = launch_kernel() d = dict( process = process, @@ -36,10 +40,10 @@ class KernelManager(object): iopub_port = iopub_port, shell_port = shell_port, hb_port = hb_port, - session_manager = SessionManager(self, kid, self.context) + session_manager = SessionManager(self, kernel_id, self.context) ) - self._kernels[kid] = d - return kid + self._kernels[kernel_id] = d + return kernel_id def kill_kernel(self, kernel_id): kernel_process = self.get_kernel_process(kernel_id) diff --git a/IPython/frontend/html/notebook/notebook.py b/IPython/frontend/html/notebook/notebook.py index d03545b..7d3f554 100644 --- a/IPython/frontend/html/notebook/notebook.py +++ b/IPython/frontend/html/notebook/notebook.py @@ -1,13 +1,11 @@ import json import logging import os -import uuid import zmq # Install the pyzmq ioloop. This has to be done before anything else from # tornado is imported. -from zmq.eventloop.zmqstream import ZMQStream from zmq.eventloop import ioloop import tornado.ioloop tornado.ioloop = ioloop @@ -21,8 +19,8 @@ from kernelmanager import KernelManager options.define("port", default=8888, help="run on the given port", type=int) -_kernel_id_regex = r"(?P\w+-\w+-\w+-\w+-\w+)" -_session_id_regex = r"(?P\w+)" +_session_id_regex = r"(?P\w+-\w+-\w+-\w+-\w+)" +_kernel_id_regex = r"(?P\w+)" class MainHandler(web.RequestHandler): @@ -30,30 +28,44 @@ class MainHandler(web.RequestHandler): self.render('notebook.html') -class KernelHandler(web.RequestHandler): +class BaseKernelHandler(object): + + def get_kernel(self): + return self.application.kernel_manager + + def get_session(self, kernel_id): + km = self.get_kernel() + sm = km.get_session_manager(kernel_id) + return sm + + +class KernelHandler(web.RequestHandler, BaseKernelHandler): def get(self): - self.write(json.dumps(self.application.kernel_manager.kernel_ids)) + self.write(json.dumps(self.get_kernel().kernel_ids)) + + def post(self, *args, **kwargs): + kernel_id = kwargs['kernel_id'] + self.get_kernel().start_kernel(kernel_id) + logging.info("Starting kernel: %s" % kernel_id) + self.write(json.dumps(kernel_id)) - def post(self): - kid = self.application.kernel_manager.start_kernel() - logging.info("Starting kernel: %s" % kid) - self.write(json.dumps(kid)) +class SessionHandler(web.RequestHandler, BaseKernelHandler): -class SessionHandler(web.RequestHandler): + def get(self, *args, **kwargs): + kernel_id = kwargs['kernel_id'] + self.write(json.dumps(self.get_session(kernel_id).session_ids)) def post(self, *args, **kwargs): kernel_id = kwargs['kernel_id'] - session_id = kwargs['session_id'] - logging.info("Starting session: %s, %s" % (kernel_id,session_id)) - km = self.application.kernel_manager - sm = km.get_session_manager(kernel_id) - sm.start_session(session_id) - self.finish() + sm = self.get_session(kernel_id) + session_id = sm.start_session() + logging.info("Starting session: %s, %s" % (kernel_id, session_id)) + self.write(json.dumps(session_id)) -class ZMQStreamHandler(websocket.WebSocketHandler): +class ZMQStreamHandler(websocket.WebSocketHandler, BaseKernelHandler): stream_name = '' @@ -61,20 +73,17 @@ class ZMQStreamHandler(websocket.WebSocketHandler): kernel_id = kwargs['kernel_id'] session_id = kwargs['session_id'] logging.info("Connection open: %s, %s" % (kernel_id,session_id)) - sm = self.application.kernel_manager.get_session_manager(kernel_id) + sm = self.get_session(kernel_id) method_name = "get_%s_stream" % self.stream_name method = getattr(sm, method_name) self.zmq_stream = method(session_id) self.zmq_stream.on_recv(self._on_zmq_reply) - self.session_manager = sm - self.session_id = session_id def on_message(self, msg): logging.info("Message received: %r" % msg) - self.zmq_stream.send(msg) + self.zmq_stream.send_unicode(msg) def on_close(self): - logging.info("Connection closed: %s, %s" % (kernel_id,session_id)) self.zmq_stream.close() def _on_zmq_reply(self, msg): @@ -97,8 +106,8 @@ class NotebookApplication(web.Application): def __init__(self): handlers = [ (r"/", MainHandler), - (r"/kernels", KernelHandler), - (r"/kernels/%s/sessions/%s" % (_kernel_id_regex,_session_id_regex), SessionHandler), + (r"/kernels/%s" % (_kernel_id_regex,), KernelHandler), + (r"/kernels/%s/sessions" % (_kernel_id_regex,), SessionHandler), (r"/kernels/%s/sessions/%s/iopub" % (_kernel_id_regex,_session_id_regex), IOPubStreamHandler), (r"/kernels/%s/sessions/%s/shell" % (_kernel_id_regex,_session_id_regex), ShellStreamHandler), ] diff --git a/IPython/frontend/html/notebook/session.py b/IPython/frontend/html/notebook/session.py index bf7df13..5fe5160 100644 --- a/IPython/frontend/html/notebook/session.py +++ b/IPython/frontend/html/notebook/session.py @@ -1,4 +1,5 @@ import logging +import uuid import zmq from zmq.eventloop.zmqstream import ZMQStream @@ -15,7 +16,21 @@ class SessionManager(object): def __del__(self): self.stop_all() - def start_session(self, session_id): + @property + def session_ids(self): + return self._session.keys() + + def __len__(self): + return len(self.session_ids) + + def __contains__(self, session_id): + if session_id in self.session_ids: + return True + else: + return False + + def start_session(self): + session_id = str(uuid.uuid4()) ports = self.kernel_manager.get_kernel_ports(self.kernel_id) iopub_stream = self.create_connected_stream(ports['iopub_port'], zmq.SUB) shell_stream = self.create_connected_stream(ports['shell_port'], zmq.XREQ) @@ -23,6 +38,7 @@ class SessionManager(object): iopub_stream = iopub_stream, shell_stream = shell_stream ) + return session_id def stop_session(self, session_id): session_dict = self._sessions.get(session_id) diff --git a/IPython/frontend/html/notebook/zmqws.py b/IPython/frontend/html/notebook/zmqws.py deleted file mode 100644 index e2eab10..0000000 --- a/IPython/frontend/html/notebook/zmqws.py +++ /dev/null @@ -1,23 +0,0 @@ -"""A simple WebSocket to ZMQ forwarder.""" - -from tornado import websocket - -class ZMQWebSocketBridge(websocket.WebSocketHandler): - """A handler to forward between a WebSocket at ZMQ socket.""" - - def open(self): - self.stream - - @property - def stream(self): - raise NotImplementedError("stream property must be implemented in a subclass") - - def on_message(self, message): - self.stream.send(message) - - def on_zmq_reply(self, reply_list): - for part in reply_list: - self.write_message(part) - - def on_close(self): - print "WebSocket closed"