Permalink
Browse files

Work on the server side of the html notebook.

  • Loading branch information...
ellisonbg committed Mar 23, 2011
1 parent d7f2add commit 398f176bf6d9fc4d698d7068a85ba1c6aaef23d0
@@ -0,0 +1,98 @@
+import signal
+import sys
+import uuid
+
+from IPython.zmq.ipkernel import launch_kernel
+from session import SessionManager
+
+
+class KernelManager(object):
+
+ ip = '127.0.0.1'
+
+ def __init__(self, context):
+ self.context = context
+ self._kernels = {}
+
+ @property
+ def kernel_ids(self):
+ return self._kernels.keys()
+
+ def __len__(self):
+ return len(self.kernel_ids)
+
+ def __contains__(self, kernel_id):
+ if kernel_id in self.kernel_ids:
+ return True
+ else:
+ return False
+
+ def start_kernel(self):
+ kid = str(uuid.uuid4())
+ (process, shell_port, iopub_port, stdin_port, hb_port) = launch_kernel()
+ d = dict(
+ process = process,
+ stdin_port = stdin_port,
+ iopub_port = iopub_port,
+ shell_port = shell_port,
+ hb_port = hb_port,
+ session_manager = SessionManager(self, kid, self.context)
+ )
+ self._kernels[kid] = d
+ return kid
+
+ def kill_kernel(self, kernel_id):
+ kernel_process = self.get_kernel_process(kernel_id)
+ if kernel_process is not None:
+ # Attempt to kill the kernel.
+ try:
+ kernel_process.kill()
+ except OSError, e:
+ # In Windows, we will get an Access Denied error if the process
+ # has already terminated. Ignore it.
+ if not (sys.platform == 'win32' and e.winerror == 5):
+ raise
+ del self._kernels[kernel_id]
+
+ def interrupt_kernel(self, kernel_id):
+ kernel_process = self.get_kernel_process(kernel_id)
+ if kernel_process is not None:
+ if sys.platform == 'win32':
+ from parentpoller import ParentPollerWindows as Poller
+ Poller.send_interrupt(kernel_process.win32_interrupt_event)
+ else:
+ kernel_process.send_signal(signal.SIGINT)
+
+ def signal_kernel(self, kernel_id, signum):
+ """ Sends a signal to the kernel. Note that since only SIGTERM is
+ supported on Windows, this function is only useful on Unix systems.
+ """
+ kernel_process = self.get_kernel_process(kernel_id)
+ if kernel_process is not None:
+ kernel_process.send_signal(signum)
+
+ def get_kernel_process(self, kernel_id):
+ d = self._kernels.get(kernel_id)
+ if d is not None:
+ return d['process']
+ else:
+ raise KeyError("Kernel with id not found: %s" % kernel_id)
+
+ def get_kernel_ports(self, kernel_id):
+ d = self._kernels.get(kernel_id)
+ if d is not None:
+ dcopy = d.copy()
+ dcopy.pop('process')
+ return dcopy
+ else:
+ raise KeyError("Kernel with id not found: %s" % kernel_id)
+
+ def get_session_manager(self, kernel_id):
+ d = self._kernels.get(kernel_id)
+ if d is not None:
+ return d['session_manager']
+ else:
+ raise KeyError("Kernel with id not found: %s" % kernel_id)
+
+
+
@@ -1,38 +1,122 @@
-
+import json
+import logging
import os
+import uuid
+
+import zmq
-import tornado.httpserver
+# 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
-import tornado.options
-import tornado.web
+tornado.ioloop = ioloop
+
+from tornado import httpserver
+from tornado import options
+from tornado import web
+from tornado import websocket
+
+from kernelmanager import KernelManager
-from tornado.options import define, options
+options.define("port", default=8888, help="run on the given port", type=int)
-define("port", default=8888, help="run on the given port", type=int)
+_kernel_id_regex = r"(?P<kernel_id>\w+-\w+-\w+-\w+-\w+)"
+_session_id_regex = r"(?P<session_id>\w+)"
-class MainHandler(tornado.web.RequestHandler):
+
+class MainHandler(web.RequestHandler):
def get(self):
self.render('notebook.html')
-class NotebookApplication(tornado.web.Application):
+class KernelHandler(web.RequestHandler):
+
+ def get(self):
+ self.write(json.dumps(self.application.kernel_manager.kernel_ids))
+
+ 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):
+
+ 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()
+
+
+class ZMQStreamHandler(websocket.WebSocketHandler):
+
+ stream_name = ''
+
+ def open(self, *args, **kwargs):
+ 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)
+ 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)
+
+ def on_close(self):
+ logging.info("Connection closed: %s, %s" % (kernel_id,session_id))
+ self.zmq_stream.close()
+
+ def _on_zmq_reply(self, msg):
+ logging.info("Message reply: %r" % msg)
+ self.write_message(msg)
+
+
+class IOPubStreamHandler(ZMQStreamHandler):
+
+ stream_name = 'iopub'
+
+
+class ShellStreamHandler(ZMQStreamHandler):
+
+ stream_name = 'shell'
+
+
+class NotebookApplication(web.Application):
+
def __init__(self):
handlers = [
- (r"/", MainHandler)
+ (r"/", MainHandler),
+ (r"/kernels", KernelHandler),
+ (r"/kernels/%s/sessions/%s" % (_kernel_id_regex,_session_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),
]
settings = dict(
template_path=os.path.join(os.path.dirname(__file__), "templates"),
static_path=os.path.join(os.path.dirname(__file__), "static"),
)
- tornado.web.Application.__init__(self, handlers, **settings)
+ web.Application.__init__(self, handlers, **settings)
+ self.context = zmq.Context()
+ self.kernel_manager = KernelManager(self.context)
def main():
- tornado.options.parse_command_line()
+ options.parse_command_line()
application = NotebookApplication()
- http_server = tornado.httpserver.HTTPServer(application)
- http_server.listen(options.port)
- tornado.ioloop.IOLoop.instance().start()
+ http_server = httpserver.HTTPServer(application)
+ http_server.listen(options.options.port)
+ ioloop.IOLoop.instance().start()
if __name__ == "__main__":
@@ -0,0 +1,56 @@
+import logging
+
+import zmq
+from zmq.eventloop.zmqstream import ZMQStream
+
+
+class SessionManager(object):
+
+ def __init__(self, kernel_manager, kernel_id, context):
+ self.context = context
+ self.kernel_manager = kernel_manager
+ self.kernel_id = kernel_id
+ self._sessions = {}
+
+ def __del__(self):
+ self.stop_all()
+
+ def start_session(self, session_id):
+ 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)
+ self._sessions[session_id] = dict(
+ iopub_stream = iopub_stream,
+ shell_stream = shell_stream
+ )
+
+ def stop_session(self, session_id):
+ session_dict = self._sessions.get(session_id)
+ if session_dict is not None:
+ for name, stream in session_dict.items():
+ stream.close()
+ del self._sessions[session_id]
+
+ def stop_all(self):
+ for session_id in self._sessions.keys():
+ self.stop_session(session_id)
+
+ def create_connected_stream(self, port, socket_type):
+ sock = self.context.socket(socket_type)
+ addr = "tcp://%s:%i" % (self.kernel_manager.ip, port)
+ logging.info("Connecting to: %s" % addr)
+ sock.connect(addr)
+ return ZMQStream(sock)
+
+ def get_stream(self, session_id, stream_name):
+ session_dict = self._sessions.get(session_id)
+ if session_dict is not None:
+ return session_dict[stream_name]
+ else:
+ raise KeyError("Session with id not found: %s" % session_id)
+
+ def get_iopub_stream(self, session_id):
+ return self.get_stream(session_id, 'iopub_stream')
+
+ def get_shell_stream(self, session_id):
+ return self.get_stream(session_id, 'shell_stream')
@@ -8,10 +8,11 @@ var IPYTHON = {};
var Notebook = function (selector) {
this.element = $(selector);
+ this.element.scroll();
this.element.data("notebook", this);
this.next_prompt_number = 1;
this.bind_events();
-}
+};
Notebook.prototype.bind_events = function () {
@@ -197,6 +198,7 @@ Notebook.prototype.move_cell_down = function (index) {
Notebook.prototype.sort_cells = function () {
var ncells = this.ncells();
+ var sindex = this.selected_index();
var swapped;
do {
swapped = false
@@ -209,6 +211,7 @@ Notebook.prototype.sort_cells = function () {
};
};
} while (swapped);
+ this.select(sindex);
return this;
};
@@ -464,7 +467,7 @@ TextCell.prototype.select = function () {
TextCell.prototype.edit = function () {
var text_cell = this.element;
var input = text_cell.find("textarea.text_cell_input");
- var output = text_cell.find("div.text_cell_render");
+ var output = text_cell.find("div.text_cell_render");
output.hide();
input.show().trigger('focus');
};
@@ -475,6 +478,10 @@ TextCell.prototype.render = function () {
var input = text_cell.find("textarea.text_cell_input");
var output = text_cell.find("div.text_cell_render");
var text = input.val();
+ if (text === "") {
+ text = this.placeholder;
+ input.val(text);
+ };
output.html(text)
input.html(text);
MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
@@ -501,6 +508,42 @@ TextCell.prototype.config_mathjax = function () {
//============================================================================
+var KernelManager = function () {
+ this.kernelid = null;
+ this.baseurl = "/kernels";
+};
+
+
+KernelManager.prototype.create_kernel = function () {
+ var that = this;
+ $.post(this.baseurl, function (data) {
+ that.kernelid = data;
+ }, 'json');
+}
+
+
+KernelManager.prototype.execute = function (code, callback) {
+ var msg = {
+ header : {msg_id : 0, username : "bgranger", session: 0},
+ msg_type : "execute_request",
+ content : {code : code}
+ };
+ var settings = {
+ data : JSON.stringify(msg),
+ processData : false,
+ contentType : "application/json",
+ success : callback,
+ type : "POST"
+ }
+ var url = this.baseurl + "/" + this.kernelid + "/" + ""
+}
+
+
+//============================================================================
+// On document ready
+//============================================================================
+
+
$(document).ready(function () {
MathJax.Hub.Config({
Oops, something went wrong.

0 comments on commit 398f176

Please sign in to comment.