##// END OF EJS Templates
Date is properly removed from JSON reply before WebSocket forward....
Date is properly removed from JSON reply before WebSocket forward. * Both the header and parent_header have a date field that cannot be json serialized. This field is just removed for now, but in the future, we will covert the date to a ISO8601 field. * Better error handling around this code has also been added to prevent the server from crashing due to malformed messages.

File last commit:

r4561:b796f247
r4561:b796f247
Show More
handlers.py
296 lines | 9.5 KiB | text/x-python | PythonLexer
Brian E. Granger
Work to adapt routers to new Session message protocol.
r4346 """Tornado handlers for the notebook."""
#-----------------------------------------------------------------------------
# Imports
#-----------------------------------------------------------------------------
Brian Granger
Work on the server side of the html notebook.
r4297
from tornado import web
from tornado import websocket
Brian E. Granger
Major refactor of kernel connection management in the notebook....
r4545 from zmq.eventloop import ioloop
from zmq.utils import jsonapi
from IPython.zmq.session import Session
Brian E. Granger
Starting work on a Markdown cell.
r4507 try:
from docutils.core import publish_string
except ImportError:
publish_string = None
Brian E. Granger
Massive work on the notebook document format....
r4484
Brian E. Granger
Major refactor of kernel connection management in the notebook....
r4545
Brian E. Granger
Work to adapt routers to new Session message protocol.
r4346 #-----------------------------------------------------------------------------
Brian E. Granger
Adding kernel/notebook associations.
r4494 # Top-level handlers
Brian E. Granger
Work to adapt routers to new Session message protocol.
r4346 #-----------------------------------------------------------------------------
Brian Granger
Initial draft of HTML5/JS/CSS3 notebook.
r4292
Brian Granger
Work on the server side of the html notebook.
r4297
Brian E. Granger
Implemented basic notebook browser and fixed numerous bugs.
r4488 class NBBrowserHandler(web.RequestHandler):
def get(self):
nbm = self.application.notebook_manager
project = nbm.notebook_dir
self.render('nbbrowser.html', project=project)
class NewHandler(web.RequestHandler):
Brian Granger
Initial draft of HTML5/JS/CSS3 notebook.
r4292 def get(self):
Brian E. Granger
Massive work on the notebook document format....
r4484 notebook_id = self.application.notebook_manager.new_notebook()
self.render('notebook.html', notebook_id=notebook_id)
class NamedNotebookHandler(web.RequestHandler):
def get(self, notebook_id):
nbm = self.application.notebook_manager
if not nbm.notebook_exists(notebook_id):
raise web.HTTPError(404)
self.render('notebook.html', notebook_id=notebook_id)
Brian Granger
Initial draft of HTML5/JS/CSS3 notebook.
r4292
Brian E. Granger
Adding kernel/notebook associations.
r4494 #-----------------------------------------------------------------------------
# Kernel handlers
#-----------------------------------------------------------------------------
class MainKernelHandler(web.RequestHandler):
Brian Granger
Work on the server side of the html notebook.
r4297
def get(self):
Brian E. Granger
Major refactor of kernel connection management in the notebook....
r4545 km = self.application.kernel_manager
self.finish(jsonapi.dumps(km.kernel_ids))
Brian Granger
Basic server for htmlnotebook working.
r4298
Brian Granger
Different clients now share a single zmq session....
r4306 def post(self):
Brian E. Granger
Major refactor of kernel connection management in the notebook....
r4545 km = self.application.kernel_manager
Brian E. Granger
Adding kernel/notebook associations.
r4494 notebook_id = self.get_argument('notebook', default=None)
Brian E. Granger
Major refactor of kernel connection management in the notebook....
r4545 kernel_id = km.start_kernel(notebook_id)
Brian E. Granger
Massive work on the notebook document format....
r4484 self.set_header('Location', '/'+kernel_id)
Brian E. Granger
Major refactor of kernel connection management in the notebook....
r4545 self.finish(jsonapi.dumps(kernel_id))
Brian Granger
Work on the server side of the html notebook.
r4297
Brian E. Granger
Adding kernel/notebook associations.
r4494 class KernelHandler(web.RequestHandler):
SUPPORTED_METHODS = ('DELETE')
def delete(self, kernel_id):
Brian E. Granger
Major refactor of kernel connection management in the notebook....
r4545 km = self.application.kernel_manager
km.kill_kernel(kernel_id)
Brian E. Granger
Adding kernel/notebook associations.
r4494 self.set_status(204)
self.finish()
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 E. Granger
Major refactor of kernel connection management in the notebook....
r4545 km = self.application.kernel_manager
Brian Granger
Cleaned up kernel action interface....
r4309 if action == 'interrupt':
Brian E. Granger
Major refactor of kernel connection management in the notebook....
r4545 km.interrupt_kernel(kernel_id)
Brian E. Granger
Adding kernel/notebook associations.
r4494 self.set_status(204)
Brian Granger
Cleaned up kernel action interface....
r4309 if action == 'restart':
Brian E. Granger
Major refactor of kernel connection management in the notebook....
r4545 new_kernel_id = km.restart_kernel(kernel_id)
self.write(jsonapi.dumps(new_kernel_id))
Brian E. Granger
Improvements to file uploaded, mime types and .py reader....
r4493 self.finish()
Brian Granger
Interrupt and restart work for kernels.
r4308
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 E. Granger
Major refactor of kernel connection management in the notebook....
r4545 def _reserialize_reply(self, msg_list):
"""Reserialize a reply message using JSON.
This takes the msg list from the ZMQ socket, unserializes it using
self.session and then serializes the result using JSON. This method
should be used by self._on_zmq_reply to build messages that can
be sent back to the browser.
"""
idents, msg_list = self.session.feed_identities(msg_list)
msg = self.session.unserialize(msg_list)
Brian E. Granger
Date is properly removed from JSON reply before WebSocket forward....
r4561 try:
msg['header'].pop('date')
except KeyError:
pass
try:
msg['parent_header'].pop('date')
except KeyError:
pass
Brian E. Granger
Major refactor of kernel connection management in the notebook....
r4545 msg.pop('buffers')
return jsonapi.dumps(msg)
Brian E. Granger
Date is properly removed from JSON reply before WebSocket forward....
r4561 def _on_zmq_reply(self, msg_list):
try:
msg = self._reserialize_reply(msg_list)
except:
self.application.kernel_manager.log.critical("Malformed message: %r" % msg_list)
else:
self.write_message(msg)
Brian E. Granger
Major refactor of kernel connection management in the notebook....
r4545
class IOPubHandler(ZMQStreamHandler):
def initialize(self, *args, **kwargs):
self._kernel_alive = True
self._beating = False
def open(self, kernel_id):
km = self.application.kernel_manager
self.kernel_id = kernel_id
self.session = Session()
self.time_to_dead = km.time_to_dead
self.iopub_stream = km.create_iopub_stream(kernel_id)
self.hb_stream = km.create_hb_stream(kernel_id)
self.iopub_stream.on_recv(self._on_zmq_reply)
self.start_hb(self.kernel_died)
def on_close(self):
self.stop_hb()
self.iopub_stream.close()
self.hb_stream.close()
def start_hb(self, callback):
"""Start the heartbeating and call the callback if the kernel dies."""
if not self._beating:
self._kernel_alive = True
def ping_or_dead():
if self._kernel_alive:
self._kernel_alive = False
self.hb_stream.send(b'ping')
else:
try:
callback()
except:
pass
finally:
self._hb_periodic_callback.stop()
def beat_received(msg):
self._kernel_alive = True
self.hb_stream.on_recv(beat_received)
self._hb_periodic_callback = ioloop.PeriodicCallback(ping_or_dead, self.time_to_dead*1000)
self._hb_periodic_callback.start()
self._beating= True
def stop_hb(self):
"""Stop the heartbeating and cancel all related callbacks."""
if self._beating:
self._hb_periodic_callback.stop()
if not self.hb_stream.closed():
self.hb_stream.on_recv(None)
def kernel_died(self):
self.write_message(
{'header': {'msg_type': 'status'},
'parent_header': {},
'content': {'execution_state':'dead'}
}
)
self.on_close()
class ShellHandler(ZMQStreamHandler):
def initialize(self, *args, **kwargs):
pass
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):
Brian E. Granger
Major refactor of kernel connection management in the notebook....
r4545 km = self.application.kernel_manager
self.max_msg_size = km.max_msg_size
self.kernel_id = kernel_id
self.session = Session()
self.shell_stream = self.application.kernel_manager.create_shell_stream(kernel_id)
self.shell_stream.on_recv(self._on_zmq_reply)
Brian Granger
Different clients now share a single zmq session....
r4306 def on_message(self, msg):
Brian E. Granger
Major refactor of kernel connection management in the notebook....
r4545 if len(msg) < self.max_msg_size:
msg = jsonapi.loads(msg)
self.session.send(self.shell_stream, 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):
Brian E. Granger
Major refactor of kernel connection management in the notebook....
r4545 self.shell_stream.close()
Brian Granger
Work on the server side of the html notebook.
r4297
Brian E. Granger
Adding kernel/notebook associations.
r4494 #-----------------------------------------------------------------------------
# Notebook web service handlers
#-----------------------------------------------------------------------------
Brian Granger
Server side of file based notebook store implemented.
r4301 class NotebookRootHandler(web.RequestHandler):
def get(self):
Brian E. Granger
Massive work on the notebook document format....
r4484 nbm = self.application.notebook_manager
files = nbm.list_notebooks()
Brian E. Granger
Major refactor of kernel connection management in the notebook....
r4545 self.finish(jsonapi.dumps(files))
Brian Granger
Server side of file based notebook store implemented.
r4301
Brian E. Granger
Massive work on the notebook document format....
r4484 def post(self):
nbm = self.application.notebook_manager
body = self.request.body.strip()
format = self.get_argument('format', default='json')
Brian E. Granger
File upload/import working from notebook browser.
r4491 name = self.get_argument('name', default=None)
Brian E. Granger
Massive work on the notebook document format....
r4484 if body:
Brian E. Granger
File upload/import working from notebook browser.
r4491 notebook_id = nbm.save_new_notebook(body, name=name, format=format)
Brian E. Granger
Massive work on the notebook document format....
r4484 else:
notebook_id = nbm.new_notebook()
self.set_header('Location', '/'+notebook_id)
Brian E. Granger
Major refactor of kernel connection management in the notebook....
r4545 self.finish(jsonapi.dumps(notebook_id))
Brian Granger
Server side of file based notebook store implemented.
r4301
Brian E. Granger
Massive work on the notebook document format....
r4484 class NotebookHandler(web.RequestHandler):
Brian Granger
Server side of file based notebook store implemented.
r4301
Brian E. Granger
Massive work on the notebook document format....
r4484 SUPPORTED_METHODS = ('GET', 'PUT', 'DELETE')
def get(self, notebook_id):
nbm = self.application.notebook_manager
format = self.get_argument('format', default='json')
last_mod, name, data = nbm.get_notebook(notebook_id, format)
if format == u'json':
self.set_header('Content-Type', 'application/json')
Brian E. Granger
Export works with filenames having spaces....
r4558 self.set_header('Content-Disposition','attachment; filename="%s.json"' % name)
Brian E. Granger
Massive work on the notebook document format....
r4484 elif format == u'xml':
Brian E. Granger
Improvements to file uploaded, mime types and .py reader....
r4493 self.set_header('Content-Type', 'application/xml')
Brian E. Granger
Export works with filenames having spaces....
r4558 self.set_header('Content-Disposition','attachment; filename="%s.ipynb"' % name)
Brian E. Granger
Massive work on the notebook document format....
r4484 elif format == u'py':
Brian E. Granger
Improvements to file uploaded, mime types and .py reader....
r4493 self.set_header('Content-Type', 'application/x-python')
Brian E. Granger
Export works with filenames having spaces....
r4558 self.set_header('Content-Disposition','attachment; filename="%s.py"' % name)
Brian E. Granger
Massive work on the notebook document format....
r4484 self.set_header('Last-Modified', last_mod)
self.finish(data)
def put(self, notebook_id):
nbm = self.application.notebook_manager
format = self.get_argument('format', default='json')
Brian E. Granger
File upload/import working from notebook browser.
r4491 name = self.get_argument('name', default=None)
nbm.save_notebook(notebook_id, self.request.body, name=name, format=format)
Brian E. Granger
Massive work on the notebook document format....
r4484 self.set_status(204)
Brian Granger
Server side of file based notebook store implemented.
r4301 self.finish()
Brian E. Granger
Massive work on the notebook document format....
r4484 def delete(self, notebook_id):
nbm = self.application.notebook_manager
nbm.delete_notebook(notebook_id)
Brian Granger
Server side of file based notebook store implemented.
r4301 self.set_status(204)
self.finish()
Brian E. Granger
Starting work on a Markdown cell.
r4507 #-----------------------------------------------------------------------------
# RST web service handlers
#-----------------------------------------------------------------------------
class RSTHandler(web.RequestHandler):
def post(self):
if publish_string is None:
raise web.HTTPError(503)
body = self.request.body.strip()
Brian E. Granger
Adding tracebacks, evalue and etype to the nbformat and notebook.
r4540 source = body
# template_path=os.path.join(os.path.dirname(__file__), u'templates', u'rst_template.html')
Brian E. Granger
Starting work on a Markdown cell.
r4507 defaults = {'file_insertion_enabled': 0,
'raw_enabled': 0,
'_disable_config': 1,
Brian E. Granger
Major refactor of kernel connection management in the notebook....
r4545 'stylesheet_path': 0
Brian E. Granger
Adding tracebacks, evalue and etype to the nbformat and notebook.
r4540 # 'template': template_path
Brian E. Granger
Starting work on a Markdown cell.
r4507 }
try:
html = publish_string(source, writer_name='html',
settings_overrides=defaults
)
except:
raise web.HTTPError(400)
print html
self.set_header('Content-Type', 'text/html')
self.finish(html)