##// END OF EJS Templates
CodeMirror code editing added....
CodeMirror code editing added. * codemirror2 repo has been added to IPython. * Custom coloring used to match the qtconsole.

File last commit:

r4326:ff6dc82b
r4332:f5911d3c
Show More
notebook.py
275 lines | 8.9 KiB | text/x-python | PythonLexer
Brian Granger
Server side of file based notebook store implemented.
r4301 import datetime
Brian Granger
Work on the server side of the html notebook.
r4297 import json
import logging
Brian Granger
Initial draft of HTML5/JS/CSS3 notebook.
r4292 import os
Brian Granger
Server side of file based notebook store implemented.
r4301 import urllib
Brian Granger
Different clients now share a single zmq session....
r4306 import uuid
from Queue import Queue
Brian Granger
Work on the server side of the html notebook.
r4297
import zmq
Brian Granger
Initial draft of HTML5/JS/CSS3 notebook.
r4292
Brian Granger
Work on the server side of the html notebook.
r4297 # Install the pyzmq ioloop. This has to be done before anything else from
# tornado is imported.
from zmq.eventloop import ioloop
Brian Granger
Initial draft of HTML5/JS/CSS3 notebook.
r4292 import tornado.ioloop
Brian Granger
Work on the server side of the html notebook.
r4297 tornado.ioloop = ioloop
from tornado import httpserver
from tornado import options
from tornado import web
from tornado import websocket
from kernelmanager import KernelManager
Brian Granger
Initial draft of HTML5/JS/CSS3 notebook.
r4292
Brian Granger
Work on the server side of the html notebook.
r4297 options.define("port", default=8888, help="run on the given port", type=int)
Brian Granger
Initial draft of HTML5/JS/CSS3 notebook.
r4292
Brian Granger
Different clients now share a single zmq session....
r4306 _kernel_id_regex = r"(?P<kernel_id>\w+-\w+-\w+-\w+-\w+)"
Brian Granger
Cleaned up kernel action interface....
r4309 _kernel_action_regex = r"(?P<action>restart|interrupt)"
Brian Granger
Work on the server side of the html notebook.
r4297
class MainHandler(web.RequestHandler):
Brian Granger
Initial draft of HTML5/JS/CSS3 notebook.
r4292 def get(self):
self.render('notebook.html')
Brian Granger
Different clients now share a single zmq session....
r4306 class KernelHandler(web.RequestHandler):
Brian Granger
Work on the server side of the html notebook.
r4297
def get(self):
Brian Granger
Different clients now share a single zmq session....
r4306 self.write(json.dumps(self.application.kernel_ids))
Brian Granger
Basic server for htmlnotebook working.
r4298
Brian Granger
Different clients now share a single zmq session....
r4306 def post(self):
kernel_id = self.application.start_kernel()
Brian Granger
Basic server for htmlnotebook working.
r4298 self.write(json.dumps(kernel_id))
Brian Granger
Work on the server side of the html notebook.
r4297
Brian Granger
Interrupt and restart work for kernels.
r4308 class KernelActionHandler(web.RequestHandler):
Brian Granger
Cleaned up kernel action interface....
r4309 def post(self, kernel_id, action):
Brian Granger
Interrupt and restart work for kernels.
r4308 # TODO: figure out a better way of handling RPC style calls.
Brian Granger
Cleaned up kernel action interface....
r4309 if action == 'interrupt':
Brian Granger
Interrupt and restart work for kernels.
r4308 self.application.interrupt_kernel(kernel_id)
Brian Granger
Cleaned up kernel action interface....
r4309 if action == 'restart':
Brian Granger
Interrupt and restart work for kernels.
r4308 new_kernel_id = self.application.restart_kernel(kernel_id)
self.write(json.dumps(new_kernel_id))
Brian Granger
Different clients now share a single zmq session....
r4306 class ZMQStreamRouter(object):
Brian Granger
Work on the server side of the html notebook.
r4297
Brian Granger
Different clients now share a single zmq session....
r4306 def __init__(self, zmq_stream):
self.zmq_stream = zmq_stream
self._clients = {}
self.zmq_stream.on_recv(self._on_zmq_reply)
Brian Granger
Work on the server side of the html notebook.
r4297
Brian Granger
Different clients now share a single zmq session....
r4306 def register_client(self, client):
client_id = uuid.uuid4()
self._clients[client_id] = client
return client_id
Brian Granger
Work on the server side of the html notebook.
r4297
Brian Granger
Different clients now share a single zmq session....
r4306 def unregister_client(self, client_id):
del self._clients[client_id]
Brian Granger
Work on the server side of the html notebook.
r4297
Brian Granger
Interrupt and restart work for kernels.
r4308 def copy_clients(self, router):
# Copy the clients of another router.
for client_id, client in router._clients.items():
client.router = self
self._clients[client_id] = client
Brian Granger
Work on the server side of the html notebook.
r4297
Brian Granger
Different clients now share a single zmq session....
r4306 class IOPubStreamRouter(ZMQStreamRouter):
Brian Granger
Work on the server side of the html notebook.
r4297
Brian Granger
Different clients now share a single zmq session....
r4306 def _on_zmq_reply(self, msg_list):
for client_id, client in self._clients.items():
for msg in msg_list:
client.write_message(msg)
Brian Granger
Work on the server side of the html notebook.
r4297
Brian Granger
Different clients now share a single zmq session....
r4306 def forward_unicode(self, client_id, msg):
# This is a SUB stream that we should never write to.
pass
Brian Granger
Work on the server side of the html notebook.
r4297
Brian Granger
Different clients now share a single zmq session....
r4306
class ShellStreamRouter(ZMQStreamRouter):
def __init__(self, zmq_stream):
ZMQStreamRouter.__init__(self, zmq_stream)
self._request_queue = Queue()
Brian Granger
Work on the server side of the html notebook.
r4297
Brian Granger
Initial reply handling implemented along with css fixes.
r4299 def _on_zmq_reply(self, msg_list):
Brian Granger
Different clients now share a single zmq session....
r4306 client_id = self._request_queue.get(block=False)
client = self._clients.get(client_id)
if client is not None:
for msg in msg_list:
client.write_message(msg)
def forward_unicode(self, client_id, msg):
self._request_queue.put(client_id)
self.zmq_stream.send_unicode(msg)
Brian Granger
Work on the server side of the html notebook.
r4297
Brian Granger
Different clients now share a single zmq session....
r4306 class ZMQStreamHandler(websocket.WebSocketHandler):
Brian Granger
Work on the server side of the html notebook.
r4297
Brian Granger
Different clients now share a single zmq session....
r4306 def initialize(self, stream_name):
self.stream_name = stream_name
Brian Granger
Work on the server side of the html notebook.
r4297
Brian Granger
Different clients now share a single zmq session....
r4306 def open(self, kernel_id):
self.router = self.application.get_router(kernel_id, self.stream_name)
self.client_id = self.router.register_client(self)
logging.info("Connection open: %s, %s" % (kernel_id, self.client_id))
Brian Granger
Work on the server side of the html notebook.
r4297
Brian Granger
Different clients now share a single zmq session....
r4306 def on_message(self, msg):
self.router.forward_unicode(self.client_id, msg)
Brian Granger
Work on the server side of the html notebook.
r4297
Brian Granger
Different clients now share a single zmq session....
r4306 def on_close(self):
self.router.unregister_client(self.client_id)
logging.info("Connection closed: %s" % self.client_id)
Brian Granger
Work on the server side of the html notebook.
r4297
Brian Granger
Server side of file based notebook store implemented.
r4301 class NotebookRootHandler(web.RequestHandler):
def get(self):
files = os.listdir(os.getcwd())
Brian Granger
Basic notebook saving and loading....
r4315 files = [file for file in files if file.endswith(".ipynb")]
Brian Granger
Server side of file based notebook store implemented.
r4301 self.write(json.dumps(files))
class NotebookHandler(web.RequestHandler):
SUPPORTED_METHODS = ("GET", "DELETE", "PUT")
def find_path(self, filename):
Brian Granger
Basic notebook saving and loading....
r4315 filename = urllib.unquote(filename)
if not filename.endswith('.ipynb'):
raise web.HTTPError(400)
Brian Granger
Server side of file based notebook store implemented.
r4301 path = os.path.join(os.getcwd(), filename)
return path
def get(self, filename):
path = self.find_path(filename)
if not os.path.isfile(path):
raise web.HTTPError(404)
info = os.stat(path)
self.set_header("Content-Type", "application/unknown")
self.set_header("Last-Modified", datetime.datetime.utcfromtimestamp(
info.st_mtime))
f = open(path, "r")
try:
self.finish(f.read())
finally:
f.close()
def put(self, filename):
path = self.find_path(filename)
f = open(path, "w")
f.write(self.request.body)
f.close()
self.finish()
def delete(self, filename):
path = self.find_path(filename)
if not os.path.isfile(path):
raise web.HTTPError(404)
os.unlink(path)
self.set_status(204)
self.finish()
Brian Granger
Work on the server side of the html notebook.
r4297 class NotebookApplication(web.Application):
Brian Granger
Initial draft of HTML5/JS/CSS3 notebook.
r4292 def __init__(self):
handlers = [
Brian Granger
Work on the server side of the html notebook.
r4297 (r"/", MainHandler),
Brian Granger
Different clients now share a single zmq session....
r4306 (r"/kernels", KernelHandler),
Brian Granger
Cleaned up kernel action interface....
r4309 (r"/kernels/%s/%s" % (_kernel_id_regex, _kernel_action_regex), KernelActionHandler),
Brian Granger
Different clients now share a single zmq session....
r4306 (r"/kernels/%s/iopub" % _kernel_id_regex, ZMQStreamHandler, dict(stream_name='iopub')),
(r"/kernels/%s/shell" % _kernel_id_regex, ZMQStreamHandler, dict(stream_name='shell')),
Brian Granger
Server side of file based notebook store implemented.
r4301 (r"/notebooks", NotebookRootHandler),
(r"/notebooks/([^/]+)", NotebookHandler)
Brian Granger
Initial draft of HTML5/JS/CSS3 notebook.
r4292 ]
settings = dict(
template_path=os.path.join(os.path.dirname(__file__), "templates"),
static_path=os.path.join(os.path.dirname(__file__), "static"),
)
Brian Granger
Work on the server side of the html notebook.
r4297 web.Application.__init__(self, handlers, **settings)
Brian Granger
Different clients now share a single zmq session....
r4306
Brian Granger
Work on the server side of the html notebook.
r4297 self.context = zmq.Context()
self.kernel_manager = KernelManager(self.context)
Brian Granger
Different clients now share a single zmq session....
r4306 self._session_dict = {}
self._routers = {}
#-------------------------------------------------------------------------
# Methods for managing kernels and sessions
#-------------------------------------------------------------------------
@property
def kernel_ids(self):
return self.kernel_manager.kernel_ids
def start_kernel(self):
kernel_id = self.kernel_manager.start_kernel()
logging.info("Kernel started: %s" % kernel_id)
Brian Granger
Interrupt and restart work for kernels.
r4308 self.start_session(kernel_id)
Brian Granger
Different clients now share a single zmq session....
r4306 return kernel_id
Brian Granger
Interrupt and restart work for kernels.
r4308 def interrupt_kernel(self, kernel_id):
self.kernel_manager.interrupt_kernel(kernel_id)
logging.info("Kernel interrupted: %s" % kernel_id)
def restart_kernel(self, kernel_id):
# Create the new kernel first so we can move the clients over.
new_kernel_id = self.start_kernel()
# Copy the clients over to the new routers.
old_iopub_router = self.get_router(kernel_id, 'iopub')
old_shell_router = self.get_router(kernel_id, 'shell')
new_iopub_router = self.get_router(new_kernel_id, 'iopub')
new_shell_router = self.get_router(new_kernel_id, 'shell')
new_iopub_router.copy_clients(old_iopub_router)
new_shell_router.copy_clients(old_shell_router)
# Now shutdown the old session and the kernel.
# 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)
logging.info("Kernel restarted")
return new_kernel_id
Brian Granger
Different clients now share a single zmq session....
r4306 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)]
return router
Brian Granger
Initial draft of HTML5/JS/CSS3 notebook.
r4292
def main():
Brian Granger
Work on the server side of the html notebook.
r4297 options.parse_command_line()
Brian Granger
Initial draft of HTML5/JS/CSS3 notebook.
r4292 application = NotebookApplication()
Brian Granger
Work on the server side of the html notebook.
r4297 http_server = httpserver.HTTPServer(application)
http_server.listen(options.options.port)
Brian Granger
Added message to help users open notebook.
r4303 print "IPython Notebook running at: http://127.0.0.1:8888"
Brian Granger
Added note about tornado version to main script.
r4326 print "The github master of tornado is required to run this server:"
print " https://github.com/facebook/tornado/tree/master/tornado"
Brian Granger
Work on the server side of the html notebook.
r4297 ioloop.IOLoop.instance().start()
Brian Granger
Initial draft of HTML5/JS/CSS3 notebook.
r4292
if __name__ == "__main__":
main()