Refactoring the notebook app to support the new config system.

This commit is contained in:
Brian E. Granger 2011-07-12 12:10:34 -07:00
parent 52b29af5bd
commit d04eb09f2e
2 changed files with 141 additions and 45 deletions

View File

@ -1,5 +1,9 @@
"""A kernel manager for multiple kernels."""
#-----------------------------------------------------------------------------
# Imports
#-----------------------------------------------------------------------------
import logging
import signal
import sys
@ -11,6 +15,9 @@ from IPython.config.configurable import Configurable
from IPython.zmq.ipkernel import launch_kernel
from IPython.utils.traitlets import Instance, Dict, Unicode
#-----------------------------------------------------------------------------
# Classes
#-----------------------------------------------------------------------------
class DuplicateKernelError(Exception):
pass

View File

@ -1,3 +1,9 @@
"""A tornado based IPython notebook server."""
#-----------------------------------------------------------------------------
# Imports
#-----------------------------------------------------------------------------
import logging
import os
@ -14,22 +20,30 @@ from tornado import options
from tornado import web
from kernelmanager import KernelManager
from sessionmanager import SessionManager
from handlers import (
MainHandler, KernelHandler, KernelActionHandler, ZMQStreamHandler,
NotebookRootHandler, NotebookHandler
)
from routers import IOPubStreamRouter, ShellStreamRouter
options.define("port", default=8888, help="run on the given port", type=int)
from IPython.core.application import BaseIPythonApplication
from IPython.zmq.session import Session
#-----------------------------------------------------------------------------
# Module globals
#-----------------------------------------------------------------------------
_kernel_id_regex = r"(?P<kernel_id>\w+-\w+-\w+-\w+-\w+)"
_kernel_action_regex = r"(?P<action>restart|interrupt)"
#-----------------------------------------------------------------------------
# The Tornado web application
#-----------------------------------------------------------------------------
class NotebookWebApplication(web.Application):
def __init__(self):
def __init__(self, kernel_manager, log):
handlers = [
(r"/", MainHandler),
(r"/kernels", KernelHandler),
@ -45,9 +59,8 @@ class NotebookWebApplication(web.Application):
)
web.Application.__init__(self, handlers, **settings)
self.context = zmq.Context()
self.kernel_manager = KernelManager(self.context)
self._session_dict = {}
self.kernel_manager = kernel_manager
self.log = log
self._routers = {}
#-------------------------------------------------------------------------
@ -59,14 +72,32 @@ class NotebookWebApplication(web.Application):
return self.kernel_manager.kernel_ids
def start_kernel(self):
# TODO: pass command line options to the kernel in start_kernel()
kernel_id = self.kernel_manager.start_kernel()
logging.info("Kernel started: %s" % kernel_id)
self.start_session(kernel_id)
self.log.info("Kernel started: %s" % kernel_id)
self.start_session_manager(kernel_id)
return kernel_id
def start_session_manager(self, kernel_id):
sm = self.kernel_manager.create_session_manager(kernel_id)
self._session_dict[kernel_id] = sm
iopub_stream = sm.get_iopub_stream()
shell_stream = sm.get_shell_stream()
iopub_router = IOPubStreamRouter(iopub_stream)
shell_router = ShellStreamRouter(shell_stream)
self._routers[(kernel_id, 'iopub')] = iopub_router
self._routers[(kernel_id, 'shell')] = shell_router
self.log.debug("Session manager started for kernel: %s" % kernel_id)
def kill_kernel(self, kernel_id):
sm = self._session_dict.pop(kernel_id)
sm.stop()
self.kernel_manager.kill_kernel(kernel_id)
self.log.info("Kernel killed: %s" % kernel_id)
def interrupt_kernel(self, kernel_id):
self.kernel_manager.interrupt_kernel(kernel_id)
logging.info("Kernel interrupted: %s" % kernel_id)
self.log.debug("Kernel interrupted: %s" % kernel_id)
def restart_kernel(self, kernel_id):
# Create the new kernel first so we can move the clients over.
@ -84,48 +115,106 @@ class NotebookWebApplication(web.Application):
# TODO: This causes a hard crash in ZMQStream.close, which sets
# self.socket to None to hastily. We will need to fix this in PyZMQ
# itself. For now, we just leave the old kernel running :(
# sm = self.kernel_manager.get_session_manager(kernel_id)
# session_id = self._session_dict[kernel_id]
# sm.stop_session(session_id)
# self.kernel_manager.kill_kernel(kernel_id)
# self.kill_kernel(kernel_id)
logging.info("Kernel restarted")
self.log.debug("Kernel restarted: %s -> %s" % (kernel_id, new_kernel_id))
return new_kernel_id
def start_session(self, kernel_id):
sm = self.kernel_manager.get_session_manager(kernel_id)
session_id = sm.start_session()
self._session_dict[kernel_id] = session_id
iopub_stream = sm.get_iopub_stream(session_id)
shell_stream = sm.get_shell_stream(session_id)
iopub_router = IOPubStreamRouter(iopub_stream)
shell_router = ShellStreamRouter(shell_stream)
self._routers[(kernel_id, session_id, 'iopub')] = iopub_router
self._routers[(kernel_id, session_id, 'shell')] = shell_router
logging.info("Session started: %s, %s" % (kernel_id, session_id))
def stop_session(self, kernel_id):
# TODO: finish this!
sm = self.kernel_manager.get_session_manager(kernel_id)
session_id = self._session_dict[kernel_id]
def get_router(self, kernel_id, stream_name):
session_id = self._session_dict[kernel_id]
router = self._routers[(kernel_id, session_id, stream_name)]
router = self._routers[(kernel_id, stream_name)]
return router
#-----------------------------------------------------------------------------
# Aliases and Flags
#-----------------------------------------------------------------------------
flags = dict(ipkernel_flags)
# the flags that are specific to the frontend
# these must be scrubbed before being passed to the kernel,
# or it will raise an error on unrecognized flags
notebook_flags = []
aliases = dict(ipkernel_aliases)
aliases.update(dict(
ip = 'IPythonNotebookApp.ip',
port = 'IPythonNotebookApp.port'
colors = 'ZMQInteractiveShell.colors',
editor = 'IPythonWidget.editor',
))
#-----------------------------------------------------------------------------
# IPythonNotebookApp
#-----------------------------------------------------------------------------
class IPythonNotebookApp(BaseIPythonApplication):
name = 'ipython-notebook'
default_config_file_name='ipython_notebook_config.py'
description = """
The IPython HTML Notebook.
This launches a Tornado based HTML Notebook Server that serves up an
HTML5/Javascript Notebook client.
"""
classes = [IPKernelApp, ZMQInteractiveShell, ProfileDir, Session,
KernelManager, SessionManager]
flags = Dict(flags)
aliases = Dict(aliases)
kernel_argv = List(Unicode)
# connection info:
ip = Unicode(LOCALHOST, config=True,
help="The IP address the notebook server will listen on."
)
port = Int(8888, config=True,
help="The port the notebook server will listen on."
)
# the factory for creating a widget
widget_factory = Any(RichIPythonWidget)
def parse_command_line(self, argv=None):
super(IPythonNotebookApp, self).parse_command_line(argv)
if argv is None:
argv = sys.argv[1:]
self.kernel_argv = list(argv) # copy
# kernel should inherit default config file from frontend
self.kernel_argv.append("KernelApp.parent_appname='%s'"%self.name)
# scrub frontend-specific flags
for a in argv:
if a.startswith('--') and a[2:] in qt_flags:
self.kernel_argv.remove(a)
def init_kernel_manager(self):
# Don't let Qt or ZMQ swallow KeyboardInterupts.
signal.signal(signal.SIGINT, signal.SIG_DFL)
# Create a KernelManager and start a kernel.
self.kernel_manager = KernelManager(config=self.config, log=self.log)
def initialize(self, argv=None):
super(IPythonNotebookApp, self).initialize(argv)
self.init_kernel_mananger()
self.web_app = NotebookWebApplication()
self.http_server = httpserver.HTTPServer(self.web_app)
self.http_server.listen(self.port)
def start(self):
self.log.info("The IPython Notebook is running at: http://%s:%i" % (self.ip, self.port))
ioloop.IOLoop.instance().start()
#-----------------------------------------------------------------------------
# Main entry point
#-----------------------------------------------------------------------------
def launch_new_instance():
options.parse_command_line()
application = NotebookWebApplication()
http_server = httpserver.HTTPServer(application)
http_server.listen(options.options.port)
print "IPython Notebook running at: http://127.0.0.1:8888"
print "The github master of tornado is required to run this server:"
print " https://github.com/facebook/tornado/tree/master/tornado"
ioloop.IOLoop.instance().start()
if __name__ == "__main__":
main()
app = IPythonNotebookApp()
app.initialize()
app.start()