handlers.py
142 lines
| 4.6 KiB
| text/x-python
|
PythonLexer
Thomas Kluyver
|
r13919 | import io | |
Thomas Kluyver
|
r13827 | import os | |
Thomas Kluyver
|
r13919 | import zipfile | |
Thomas Kluyver
|
r13827 | ||
from tornado import web | |||
Thomas Kluyver
|
r13916 | from ..base.handlers import IPythonHandler, notebook_path_regex | |
Thomas Kluyver
|
r13827 | from IPython.nbformat.current import to_notebook_json | |
MinRK
|
r13946 | ||
Thomas Kluyver
|
r13827 | from IPython.utils import tz | |
Thomas Kluyver
|
r13919 | from IPython.utils.py3compat import cast_bytes | |
import sys | |||
def find_resource_files(output_files_dir): | |||
files = [] | |||
for dirpath, dirnames, filenames in os.walk(output_files_dir): | |||
files.extend([os.path.join(dirpath, f) for f in filenames]) | |||
return files | |||
def respond_zip(handler, name, output, resources): | |||
"""Zip up the output and resource files and respond with the zip file. | |||
Returns True if it has served a zip file, False if there are no resource | |||
files, in which case we serve the plain output file. | |||
""" | |||
# Check if we have resource files we need to zip | |||
output_files = resources.get('outputs', None) | |||
if not output_files: | |||
return False | |||
# Headers | |||
zip_filename = os.path.splitext(name)[0] + '.zip' | |||
handler.set_header('Content-Disposition', | |||
'attachment; filename="%s"' % zip_filename) | |||
handler.set_header('Content-Type', 'application/zip') | |||
# Prepare the zip file | |||
buffer = io.BytesIO() | |||
zipf = zipfile.ZipFile(buffer, mode='w', compression=zipfile.ZIP_DEFLATED) | |||
output_filename = os.path.splitext(name)[0] + '.' + resources['output_extension'] | |||
zipf.writestr(output_filename, cast_bytes(output, 'utf-8')) | |||
for filename, data in output_files.items(): | |||
zipf.writestr(os.path.basename(filename), data) | |||
zipf.close() | |||
handler.finish(buffer.getvalue()) | |||
return True | |||
Thomas Kluyver
|
r13827 | ||
MinRK
|
r13946 | def get_exporter(format, **kwargs): | |
"""get an exporter, raising appropriate errors""" | |||
# if this fails, will raise 500 | |||
try: | |||
from IPython.nbconvert.exporters.export import exporter_map | |||
except ImportError as e: | |||
raise web.HTTPError(500, "Could not import nbconvert: %s" % e) | |||
try: | |||
Exporter = exporter_map[format] | |||
except KeyError: | |||
# should this be 400? | |||
raise web.HTTPError(404, u"No exporter for format: %s" % format) | |||
MinRK
|
r14044 | try: | |
return Exporter(**kwargs) | |||
except Exception as e: | |||
raise web.HTTPError(500, "Could not construct Exporter: %s" % e) | |||
MinRK
|
r13946 | ||
Thomas Kluyver
|
r13827 | class NbconvertFileHandler(IPythonHandler): | |
SUPPORTED_METHODS = ('GET',) | |||
@web.authenticated | |||
def get(self, format, path='', name=None): | |||
MinRK
|
r13946 | ||
exporter = get_exporter(format, config=self.config) | |||
Thomas Kluyver
|
r13827 | ||
path = path.strip('/') | |||
os_path = self.notebook_manager.get_os_path(name, path) | |||
if not os.path.isfile(os_path): | |||
raise web.HTTPError(404, u'Notebook does not exist: %s' % name) | |||
info = os.stat(os_path) | |||
self.set_header('Last-Modified', tz.utcfromtimestamp(info.st_mtime)) | |||
MinRK
|
r13940 | ||
try: | |||
output, resources = exporter.from_filename(os_path) | |||
except Exception as e: | |||
raise web.HTTPError(500, "nbconvert failed: %s" % e) | |||
Thomas Kluyver
|
r13919 | ||
if respond_zip(self, name, output, resources): | |||
return | |||
Thomas Kluyver
|
r13830 | # Force download if requested | |
Thomas Kluyver
|
r13829 | if self.get_argument('download', 'false').lower() == 'true': | |
Thomas Kluyver
|
r13919 | filename = os.path.splitext(name)[0] + '.' + resources['output_extension'] | |
Thomas Kluyver
|
r13829 | self.set_header('Content-Disposition', | |
Thomas Kluyver
|
r13919 | 'attachment; filename="%s"' % filename) | |
Thomas Kluyver
|
r13830 | # MIME type | |
Thomas Kluyver
|
r13832 | if exporter.output_mimetype: | |
self.set_header('Content-Type', | |||
'%s; charset=utf-8' % exporter.output_mimetype) | |||
Thomas Kluyver
|
r13830 | ||
Thomas Kluyver
|
r13827 | self.finish(output) | |
class NbconvertPostHandler(IPythonHandler): | |||
SUPPORTED_METHODS = ('POST',) | |||
@web.authenticated | |||
def post(self, format): | |||
MinRK
|
r13946 | exporter = get_exporter(format, config=self.config) | |
Thomas Kluyver
|
r13827 | ||
model = self.get_json_body() | |||
nbnode = to_notebook_json(model['content']) | |||
MinRK
|
r13940 | ||
try: | |||
output, resources = exporter.from_notebook_node(nbnode) | |||
except Exception as e: | |||
raise web.HTTPError(500, "nbconvert failed: %s" % e) | |||
Thomas Kluyver
|
r13919 | ||
if respond_zip(self, nbnode.metadata.name, output, resources): | |||
return | |||
Thomas Kluyver
|
r13830 | # MIME type | |
Thomas Kluyver
|
r13832 | if exporter.output_mimetype: | |
self.set_header('Content-Type', | |||
Thomas Kluyver
|
r13919 | '%s; charset=utf-8' % exporter.output_mimetype) | |
Thomas Kluyver
|
r13830 | ||
Thomas Kluyver
|
r13827 | self.finish(output) | |
#----------------------------------------------------------------------------- | |||
# URL to handler mappings | |||
#----------------------------------------------------------------------------- | |||
_format_regex = r"(?P<format>\w+)" | |||
Thomas Kluyver
|
r13916 | ||
Thomas Kluyver
|
r13827 | ||
default_handlers = [ | |||
Thomas Kluyver
|
r13916 | (r"/nbconvert/%s%s" % (_format_regex, notebook_path_regex), | |
Thomas Kluyver
|
r13827 | NbconvertFileHandler), | |
(r"/nbconvert/%s" % _format_regex, NbconvertPostHandler), | |||
] |