mirror of
https://github.com/jupyter/notebook.git
synced 2025-01-24 12:05:22 +08:00
Merge pull request #4735 from minrk/better-errors
add some HTML error pages
This commit is contained in:
commit
c0f346c4e3
@ -25,7 +25,13 @@ import re
|
||||
import stat
|
||||
import sys
|
||||
import traceback
|
||||
try:
|
||||
# py3
|
||||
from http.client import responses
|
||||
except ImportError:
|
||||
from httplib import responses
|
||||
|
||||
from jinja2 import TemplateNotFound
|
||||
from tornado import web
|
||||
|
||||
try:
|
||||
@ -46,14 +52,7 @@ UF_HIDDEN = getattr(stat, 'UF_HIDDEN', 32768)
|
||||
#-----------------------------------------------------------------------------
|
||||
non_alphanum = re.compile(r'[^A-Za-z0-9]')
|
||||
|
||||
class RequestHandler(web.RequestHandler):
|
||||
"""RequestHandler with default variable setting."""
|
||||
|
||||
def render(*args, **kwargs):
|
||||
kwargs.setdefault('message', '')
|
||||
return web.RequestHandler.render(*args, **kwargs)
|
||||
|
||||
class AuthenticatedHandler(RequestHandler):
|
||||
class AuthenticatedHandler(web.RequestHandler):
|
||||
"""A RequestHandler with an authenticated user."""
|
||||
|
||||
def clear_login_cookie(self):
|
||||
@ -212,6 +211,45 @@ class IPythonHandler(AuthenticatedHandler):
|
||||
raise web.HTTPError(400, u'Invalid JSON in body of request')
|
||||
return model
|
||||
|
||||
def get_error_html(self, status_code, **kwargs):
|
||||
"""render custom error pages"""
|
||||
exception = kwargs.get('exception')
|
||||
message = ''
|
||||
status_message = responses.get(status_code, 'Unknown HTTP Error')
|
||||
if exception:
|
||||
# get the custom message, if defined
|
||||
try:
|
||||
message = exception.log_message % exception.args
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# construct the custom reason, if defined
|
||||
reason = getattr(exception, 'reason', '')
|
||||
if reason:
|
||||
status_message = reason
|
||||
|
||||
# build template namespace
|
||||
ns = dict(
|
||||
status_code=status_code,
|
||||
status_message=status_message,
|
||||
message=message,
|
||||
exception=exception,
|
||||
)
|
||||
|
||||
# render the template
|
||||
try:
|
||||
html = self.render_template('%s.html' % status_code, **ns)
|
||||
except TemplateNotFound:
|
||||
self.log.debug("No template for %d", status_code)
|
||||
html = self.render_template('error.html', **ns)
|
||||
return html
|
||||
|
||||
|
||||
class Template404(IPythonHandler):
|
||||
"""Render our 404 template"""
|
||||
def prepare(self):
|
||||
raise web.HTTPError(404)
|
||||
|
||||
|
||||
class AuthenticatedFileHandler(IPythonHandler, web.StaticFileHandler):
|
||||
"""static files should only be accessible when logged in"""
|
||||
|
@ -6,7 +6,7 @@ from tornado import web
|
||||
|
||||
from ..base.handlers import IPythonHandler, notebook_path_regex
|
||||
from IPython.nbformat.current import to_notebook_json
|
||||
from IPython.nbconvert.exporters.export import exporter_map
|
||||
|
||||
from IPython.utils import tz
|
||||
from IPython.utils.py3compat import cast_bytes
|
||||
|
||||
@ -47,13 +47,33 @@ def respond_zip(handler, name, output, resources):
|
||||
handler.finish(buffer.getvalue())
|
||||
return True
|
||||
|
||||
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)
|
||||
|
||||
try:
|
||||
return Exporter(**kwargs)
|
||||
except Exception as e:
|
||||
raise web.HTTPError(500, "Could not construct Exporter: %s" % e)
|
||||
|
||||
class NbconvertFileHandler(IPythonHandler):
|
||||
|
||||
SUPPORTED_METHODS = ('GET',)
|
||||
|
||||
@web.authenticated
|
||||
def get(self, format, path='', name=None):
|
||||
exporter = exporter_map[format](config=self.config)
|
||||
|
||||
exporter = get_exporter(format, config=self.config)
|
||||
|
||||
path = path.strip('/')
|
||||
os_path = self.notebook_manager.get_os_path(name, path)
|
||||
@ -62,8 +82,11 @@ class NbconvertFileHandler(IPythonHandler):
|
||||
|
||||
info = os.stat(os_path)
|
||||
self.set_header('Last-Modified', tz.utcfromtimestamp(info.st_mtime))
|
||||
|
||||
output, resources = exporter.from_filename(os_path)
|
||||
|
||||
try:
|
||||
output, resources = exporter.from_filename(os_path)
|
||||
except Exception as e:
|
||||
raise web.HTTPError(500, "nbconvert failed: %s" % e)
|
||||
|
||||
if respond_zip(self, name, output, resources):
|
||||
return
|
||||
@ -86,12 +109,15 @@ class NbconvertPostHandler(IPythonHandler):
|
||||
|
||||
@web.authenticated
|
||||
def post(self, format):
|
||||
exporter = exporter_map[format](config=self.config)
|
||||
exporter = get_exporter(format, config=self.config)
|
||||
|
||||
model = self.get_json_body()
|
||||
nbnode = to_notebook_json(model['content'])
|
||||
|
||||
output, resources = exporter.from_notebook_node(nbnode)
|
||||
|
||||
try:
|
||||
output, resources = exporter.from_notebook_node(nbnode)
|
||||
except Exception as e:
|
||||
raise web.HTTPError(500, "nbconvert failed: %s" % e)
|
||||
|
||||
if respond_zip(self, nbnode.metadata.name, output, resources):
|
||||
return
|
||||
|
@ -61,6 +61,7 @@ from tornado import web
|
||||
|
||||
# Our own libraries
|
||||
from IPython.html import DEFAULT_STATIC_FILES_PATH
|
||||
from .base.handlers import Template404
|
||||
|
||||
from .services.kernels.kernelmanager import MappingKernelManager
|
||||
from .services.notebooks.nbmanager import NotebookManager
|
||||
@ -208,6 +209,8 @@ class NotebookWebApplication(web.Application):
|
||||
pattern = url_path_join(settings['base_project_url'], handler[0])
|
||||
new_handler = tuple([pattern] + list(handler[1:]))
|
||||
new_handlers.append(new_handler)
|
||||
# add 404 on the end, which will catch everything that falls through
|
||||
new_handlers.append((r'(.*)', Template404))
|
||||
return new_handlers
|
||||
|
||||
|
||||
|
@ -3,7 +3,6 @@ import json
|
||||
from tornado import web
|
||||
|
||||
from ...base.handlers import IPythonHandler, json_errors
|
||||
from IPython.nbconvert.exporters.export import exporter_map
|
||||
|
||||
class NbconvertRootHandler(IPythonHandler):
|
||||
SUPPORTED_METHODS = ('GET',)
|
||||
@ -11,6 +10,10 @@ class NbconvertRootHandler(IPythonHandler):
|
||||
@web.authenticated
|
||||
@json_errors
|
||||
def get(self):
|
||||
try:
|
||||
from IPython.nbconvert.exporters.export import exporter_map
|
||||
except ImportError as e:
|
||||
raise web.HTTPError(500, "Could not import nbconvert: %s" % e)
|
||||
res = {}
|
||||
for format, exporter in exporter_map.items():
|
||||
res[format] = info = {}
|
||||
|
20
IPython/html/static/base/less/error.less
Normal file
20
IPython/html/static/base/less/error.less
Normal file
@ -0,0 +1,20 @@
|
||||
div.error {
|
||||
margin: 2em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
div.error > h1 {
|
||||
font-size: 500%;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
div.error > p {
|
||||
font-size: 200%;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
div.traceback-wrapper {
|
||||
text-align: left;
|
||||
max-width: 800px;
|
||||
margin: auto;
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
@import "variables.less";
|
||||
@import "mixins.less";
|
||||
@import "flexbox.less";
|
||||
|
||||
@import "error.less";
|
||||
|
4
IPython/html/static/style/ipython.min.css
vendored
4
IPython/html/static/style/ipython.min.css
vendored
@ -18,6 +18,10 @@
|
||||
.start{-webkit-box-pack:start;-moz-box-pack:start;box-pack:start;}
|
||||
.end{-webkit-box-pack:end;-moz-box-pack:end;box-pack:end;}
|
||||
.center{-webkit-box-pack:center;-moz-box-pack:center;box-pack:center;}
|
||||
div.error{margin:2em;text-align:center;}
|
||||
div.error>h1{font-size:500%;line-height:normal;}
|
||||
div.error>p{font-size:200%;line-height:normal;}
|
||||
div.traceback-wrapper{text-align:left;max-width:800px;margin:auto;}
|
||||
.center-nav{display:inline-block;margin-bottom:-4px;}
|
||||
.alternate_upload{background-color:none;display:inline;}
|
||||
.alternate_upload.form{padding:0;margin:0;}
|
||||
|
4
IPython/html/static/style/style.min.css
vendored
4
IPython/html/static/style/style.min.css
vendored
@ -1385,6 +1385,10 @@ ul.icons-ul{list-style-type:none;text-indent:-0.7142857142857143em;margin-left:2
|
||||
.start{-webkit-box-pack:start;-moz-box-pack:start;box-pack:start;}
|
||||
.end{-webkit-box-pack:end;-moz-box-pack:end;box-pack:end;}
|
||||
.center{-webkit-box-pack:center;-moz-box-pack:center;box-pack:center;}
|
||||
div.error{margin:2em;text-align:center;}
|
||||
div.error>h1{font-size:500%;line-height:normal;}
|
||||
div.error>p{font-size:200%;line-height:normal;}
|
||||
div.traceback-wrapper{text-align:left;max-width:800px;margin:auto;}
|
||||
body{background-color:white;position:absolute;left:0px;right:0px;top:0px;bottom:0px;overflow:visible;}
|
||||
div#header{display:none;}
|
||||
#ipython_notebook{padding-left:16px;}
|
||||
|
5
IPython/html/templates/404.html
Normal file
5
IPython/html/templates/404.html
Normal file
@ -0,0 +1,5 @@
|
||||
{% extends "error.html" %}
|
||||
{% block error_detail %}
|
||||
<p>You are requesting a page that does not exist!</p>
|
||||
{% endblock %}
|
||||
|
31
IPython/html/templates/error.html
Normal file
31
IPython/html/templates/error.html
Normal file
@ -0,0 +1,31 @@
|
||||
{% extends "page.html" %}
|
||||
|
||||
{% block login_widget %}
|
||||
{% endblock %}
|
||||
|
||||
{% block stylesheet %}
|
||||
{{super()}}
|
||||
<style type="text/css">
|
||||
/* disable initial hide */
|
||||
div#header, div#site {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
{% block site %}
|
||||
|
||||
<div class="error">
|
||||
{% block h1_error %}
|
||||
<h1>{{status_code}} : {{status_message}}</h1>
|
||||
{% endblock h1_error %}
|
||||
{% block error_detail %}
|
||||
{% if message %}
|
||||
<p>The error was:</p>
|
||||
<div class="traceback-wrapper">
|
||||
<pre class="traceback">{{message}}</pre>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
</header>
|
||||
|
||||
{% endblock %}
|
Loading…
Reference in New Issue
Block a user