##// END OF EJS Templates
Merge pull request #4735 from minrk/better-errors...
Thomas Kluyver -
r14091:ae073a16 merge
parent child Browse files
Show More
@@ -0,0 +1,20 b''
1 div.error {
2 margin: 2em;
3 text-align: center;
4 }
5
6 div.error > h1 {
7 font-size: 500%;
8 line-height: normal;
9 }
10
11 div.error > p {
12 font-size: 200%;
13 line-height: normal;
14 }
15
16 div.traceback-wrapper {
17 text-align: left;
18 max-width: 800px;
19 margin: auto;
20 }
@@ -0,0 +1,5 b''
1 {% extends "error.html" %}
2 {% block error_detail %}
3 <p>You are requesting a page that does not exist!</p>
4 {% endblock %}
5
@@ -0,0 +1,31 b''
1 {% extends "page.html" %}
2
3 {% block login_widget %}
4 {% endblock %}
5
6 {% block stylesheet %}
7 {{super()}}
8 <style type="text/css">
9 /* disable initial hide */
10 div#header, div#site {
11 display: block;
12 }
13 </style>
14 {% endblock %}
15 {% block site %}
16
17 <div class="error">
18 {% block h1_error %}
19 <h1>{{status_code}} : {{status_message}}</h1>
20 {% endblock h1_error %}
21 {% block error_detail %}
22 {% if message %}
23 <p>The error was:</p>
24 <div class="traceback-wrapper">
25 <pre class="traceback">{{message}}</pre>
26 </div>
27 {% endif %}
28 {% endblock %}
29 </header>
30
31 {% endblock %}
@@ -25,7 +25,13 b' import re'
25 import stat
25 import stat
26 import sys
26 import sys
27 import traceback
27 import traceback
28 try:
29 # py3
30 from http.client import responses
31 except ImportError:
32 from httplib import responses
28
33
34 from jinja2 import TemplateNotFound
29 from tornado import web
35 from tornado import web
30
36
31 try:
37 try:
@@ -46,14 +52,7 b" UF_HIDDEN = getattr(stat, 'UF_HIDDEN', 32768)"
46 #-----------------------------------------------------------------------------
52 #-----------------------------------------------------------------------------
47 non_alphanum = re.compile(r'[^A-Za-z0-9]')
53 non_alphanum = re.compile(r'[^A-Za-z0-9]')
48
54
49 class RequestHandler(web.RequestHandler):
55 class AuthenticatedHandler(web.RequestHandler):
50 """RequestHandler with default variable setting."""
51
52 def render(*args, **kwargs):
53 kwargs.setdefault('message', '')
54 return web.RequestHandler.render(*args, **kwargs)
55
56 class AuthenticatedHandler(RequestHandler):
57 """A RequestHandler with an authenticated user."""
56 """A RequestHandler with an authenticated user."""
58
57
59 def clear_login_cookie(self):
58 def clear_login_cookie(self):
@@ -212,6 +211,45 b' class IPythonHandler(AuthenticatedHandler):'
212 raise web.HTTPError(400, u'Invalid JSON in body of request')
211 raise web.HTTPError(400, u'Invalid JSON in body of request')
213 return model
212 return model
214
213
214 def get_error_html(self, status_code, **kwargs):
215 """render custom error pages"""
216 exception = kwargs.get('exception')
217 message = ''
218 status_message = responses.get(status_code, 'Unknown HTTP Error')
219 if exception:
220 # get the custom message, if defined
221 try:
222 message = exception.log_message % exception.args
223 except Exception:
224 pass
225
226 # construct the custom reason, if defined
227 reason = getattr(exception, 'reason', '')
228 if reason:
229 status_message = reason
230
231 # build template namespace
232 ns = dict(
233 status_code=status_code,
234 status_message=status_message,
235 message=message,
236 exception=exception,
237 )
238
239 # render the template
240 try:
241 html = self.render_template('%s.html' % status_code, **ns)
242 except TemplateNotFound:
243 self.log.debug("No template for %d", status_code)
244 html = self.render_template('error.html', **ns)
245 return html
246
247
248 class Template404(IPythonHandler):
249 """Render our 404 template"""
250 def prepare(self):
251 raise web.HTTPError(404)
252
215
253
216 class AuthenticatedFileHandler(IPythonHandler, web.StaticFileHandler):
254 class AuthenticatedFileHandler(IPythonHandler, web.StaticFileHandler):
217 """static files should only be accessible when logged in"""
255 """static files should only be accessible when logged in"""
@@ -6,7 +6,7 b' from tornado import web'
6
6
7 from ..base.handlers import IPythonHandler, notebook_path_regex
7 from ..base.handlers import IPythonHandler, notebook_path_regex
8 from IPython.nbformat.current import to_notebook_json
8 from IPython.nbformat.current import to_notebook_json
9 from IPython.nbconvert.exporters.export import exporter_map
9
10 from IPython.utils import tz
10 from IPython.utils import tz
11 from IPython.utils.py3compat import cast_bytes
11 from IPython.utils.py3compat import cast_bytes
12
12
@@ -47,13 +47,33 b' def respond_zip(handler, name, output, resources):'
47 handler.finish(buffer.getvalue())
47 handler.finish(buffer.getvalue())
48 return True
48 return True
49
49
50 def get_exporter(format, **kwargs):
51 """get an exporter, raising appropriate errors"""
52 # if this fails, will raise 500
53 try:
54 from IPython.nbconvert.exporters.export import exporter_map
55 except ImportError as e:
56 raise web.HTTPError(500, "Could not import nbconvert: %s" % e)
57
58 try:
59 Exporter = exporter_map[format]
60 except KeyError:
61 # should this be 400?
62 raise web.HTTPError(404, u"No exporter for format: %s" % format)
63
64 try:
65 return Exporter(**kwargs)
66 except Exception as e:
67 raise web.HTTPError(500, "Could not construct Exporter: %s" % e)
68
50 class NbconvertFileHandler(IPythonHandler):
69 class NbconvertFileHandler(IPythonHandler):
51
70
52 SUPPORTED_METHODS = ('GET',)
71 SUPPORTED_METHODS = ('GET',)
53
72
54 @web.authenticated
73 @web.authenticated
55 def get(self, format, path='', name=None):
74 def get(self, format, path='', name=None):
56 exporter = exporter_map[format](config=self.config)
75
76 exporter = get_exporter(format, config=self.config)
57
77
58 path = path.strip('/')
78 path = path.strip('/')
59 os_path = self.notebook_manager.get_os_path(name, path)
79 os_path = self.notebook_manager.get_os_path(name, path)
@@ -62,8 +82,11 b' class NbconvertFileHandler(IPythonHandler):'
62
82
63 info = os.stat(os_path)
83 info = os.stat(os_path)
64 self.set_header('Last-Modified', tz.utcfromtimestamp(info.st_mtime))
84 self.set_header('Last-Modified', tz.utcfromtimestamp(info.st_mtime))
65
85
66 output, resources = exporter.from_filename(os_path)
86 try:
87 output, resources = exporter.from_filename(os_path)
88 except Exception as e:
89 raise web.HTTPError(500, "nbconvert failed: %s" % e)
67
90
68 if respond_zip(self, name, output, resources):
91 if respond_zip(self, name, output, resources):
69 return
92 return
@@ -86,12 +109,15 b' class NbconvertPostHandler(IPythonHandler):'
86
109
87 @web.authenticated
110 @web.authenticated
88 def post(self, format):
111 def post(self, format):
89 exporter = exporter_map[format](config=self.config)
112 exporter = get_exporter(format, config=self.config)
90
113
91 model = self.get_json_body()
114 model = self.get_json_body()
92 nbnode = to_notebook_json(model['content'])
115 nbnode = to_notebook_json(model['content'])
93
116
94 output, resources = exporter.from_notebook_node(nbnode)
117 try:
118 output, resources = exporter.from_notebook_node(nbnode)
119 except Exception as e:
120 raise web.HTTPError(500, "nbconvert failed: %s" % e)
95
121
96 if respond_zip(self, nbnode.metadata.name, output, resources):
122 if respond_zip(self, nbnode.metadata.name, output, resources):
97 return
123 return
@@ -61,6 +61,7 b' from tornado import web'
61
61
62 # Our own libraries
62 # Our own libraries
63 from IPython.html import DEFAULT_STATIC_FILES_PATH
63 from IPython.html import DEFAULT_STATIC_FILES_PATH
64 from .base.handlers import Template404
64
65
65 from .services.kernels.kernelmanager import MappingKernelManager
66 from .services.kernels.kernelmanager import MappingKernelManager
66 from .services.notebooks.nbmanager import NotebookManager
67 from .services.notebooks.nbmanager import NotebookManager
@@ -208,6 +209,8 b' class NotebookWebApplication(web.Application):'
208 pattern = url_path_join(settings['base_project_url'], handler[0])
209 pattern = url_path_join(settings['base_project_url'], handler[0])
209 new_handler = tuple([pattern] + list(handler[1:]))
210 new_handler = tuple([pattern] + list(handler[1:]))
210 new_handlers.append(new_handler)
211 new_handlers.append(new_handler)
212 # add 404 on the end, which will catch everything that falls through
213 new_handlers.append((r'(.*)', Template404))
211 return new_handlers
214 return new_handlers
212
215
213
216
@@ -3,7 +3,6 b' import json'
3 from tornado import web
3 from tornado import web
4
4
5 from ...base.handlers import IPythonHandler, json_errors
5 from ...base.handlers import IPythonHandler, json_errors
6 from IPython.nbconvert.exporters.export import exporter_map
7
6
8 class NbconvertRootHandler(IPythonHandler):
7 class NbconvertRootHandler(IPythonHandler):
9 SUPPORTED_METHODS = ('GET',)
8 SUPPORTED_METHODS = ('GET',)
@@ -11,6 +10,10 b' class NbconvertRootHandler(IPythonHandler):'
11 @web.authenticated
10 @web.authenticated
12 @json_errors
11 @json_errors
13 def get(self):
12 def get(self):
13 try:
14 from IPython.nbconvert.exporters.export import exporter_map
15 except ImportError as e:
16 raise web.HTTPError(500, "Could not import nbconvert: %s" % e)
14 res = {}
17 res = {}
15 for format, exporter in exporter_map.items():
18 for format, exporter in exporter_map.items():
16 res[format] = info = {}
19 res[format] = info = {}
@@ -1,4 +1,4 b''
1 @import "variables.less";
1 @import "variables.less";
2 @import "mixins.less";
2 @import "mixins.less";
3 @import "flexbox.less";
3 @import "flexbox.less";
4
4 @import "error.less";
@@ -18,6 +18,10 b''
18 .start{-webkit-box-pack:start;-moz-box-pack:start;box-pack:start;}
18 .start{-webkit-box-pack:start;-moz-box-pack:start;box-pack:start;}
19 .end{-webkit-box-pack:end;-moz-box-pack:end;box-pack:end;}
19 .end{-webkit-box-pack:end;-moz-box-pack:end;box-pack:end;}
20 .center{-webkit-box-pack:center;-moz-box-pack:center;box-pack:center;}
20 .center{-webkit-box-pack:center;-moz-box-pack:center;box-pack:center;}
21 div.error{margin:2em;text-align:center;}
22 div.error>h1{font-size:500%;line-height:normal;}
23 div.error>p{font-size:200%;line-height:normal;}
24 div.traceback-wrapper{text-align:left;max-width:800px;margin:auto;}
21 .center-nav{display:inline-block;margin-bottom:-4px;}
25 .center-nav{display:inline-block;margin-bottom:-4px;}
22 .alternate_upload{background-color:none;display:inline;}
26 .alternate_upload{background-color:none;display:inline;}
23 .alternate_upload.form{padding:0;margin:0;}
27 .alternate_upload.form{padding:0;margin:0;}
@@ -1385,6 +1385,10 b' ul.icons-ul{list-style-type:none;text-indent:-0.7142857142857143em;margin-left:2'
1385 .start{-webkit-box-pack:start;-moz-box-pack:start;box-pack:start;}
1385 .start{-webkit-box-pack:start;-moz-box-pack:start;box-pack:start;}
1386 .end{-webkit-box-pack:end;-moz-box-pack:end;box-pack:end;}
1386 .end{-webkit-box-pack:end;-moz-box-pack:end;box-pack:end;}
1387 .center{-webkit-box-pack:center;-moz-box-pack:center;box-pack:center;}
1387 .center{-webkit-box-pack:center;-moz-box-pack:center;box-pack:center;}
1388 div.error{margin:2em;text-align:center;}
1389 div.error>h1{font-size:500%;line-height:normal;}
1390 div.error>p{font-size:200%;line-height:normal;}
1391 div.traceback-wrapper{text-align:left;max-width:800px;margin:auto;}
1388 body{background-color:white;position:absolute;left:0px;right:0px;top:0px;bottom:0px;overflow:visible;}
1392 body{background-color:white;position:absolute;left:0px;right:0px;top:0px;bottom:0px;overflow:visible;}
1389 div#header{display:none;}
1393 div#header{display:none;}
1390 #ipython_notebook{padding-left:16px;}
1394 #ipython_notebook{padding-left:16px;}
@@ -19,8 +19,8 b' from __future__ import print_function, absolute_import'
19 # Stdlib imports
19 # Stdlib imports
20 import os
20 import os
21
21
22 # other libs/dependencies
22 # other libs/dependencies are imported at runtime
23 from jinja2 import Environment, FileSystemLoader, ChoiceLoader, TemplateNotFound
23 # to move ImportErrors to runtime when the requirement is actually needed
24
24
25 # IPython imports
25 # IPython imports
26 from IPython.utils.traitlets import MetaHasTraits, Unicode, List, Dict, Any
26 from IPython.utils.traitlets import MetaHasTraits, Unicode, List, Dict, Any
@@ -164,6 +164,8 b' class TemplateExporter(Exporter):'
164
164
165 This is triggered by various trait changes that would change the template.
165 This is triggered by various trait changes that would change the template.
166 """
166 """
167 from jinja2 import TemplateNotFound
168
167 if self.template is not None:
169 if self.template is not None:
168 return
170 return
169 # called too early, do nothing
171 # called too early, do nothing
@@ -274,6 +276,7 b' class TemplateExporter(Exporter):'
274 """
276 """
275 Create the Jinja templating environment.
277 Create the Jinja templating environment.
276 """
278 """
279 from jinja2 import Environment, ChoiceLoader, FileSystemLoader
277 here = os.path.dirname(os.path.realpath(__file__))
280 here = os.path.dirname(os.path.realpath(__file__))
278 loaders = []
281 loaders = []
279 if extra_loaders:
282 if extra_loaders:
@@ -14,14 +14,11 b' from within Jinja templates.'
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16
16
17 from pygments import highlight as pygements_highlight
17 # pygments must not be imported at the module level
18 from pygments.lexers import get_lexer_by_name
18 # because errors should be raised at runtime if it's actually needed,
19 from pygments.formatters import HtmlFormatter
19 # not import time, when it may not be needed.
20 from pygments.formatters import LatexFormatter
21
22
20
23 # Our own imports
21 # Our own imports
24 from IPython.nbconvert.utils.lexers import IPythonLexer
25 from IPython.nbconvert.utils.base import NbConvertBase
22 from IPython.nbconvert.utils.base import NbConvertBase
26
23
27 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
@@ -55,10 +52,11 b' class Highlight2Html(NbConvertBase):'
55 metadata : NotebookNode cell metadata
52 metadata : NotebookNode cell metadata
56 metadata of the cell to highlight
53 metadata of the cell to highlight
57 """
54 """
55 from pygments.formatters import HtmlFormatter
58 if not language:
56 if not language:
59 language=self.default_language
57 language=self.default_language
60
58
61 return _pygment_highlight(source, HtmlFormatter(), language, metadata)
59 return _pygments_highlight(source, HtmlFormatter(), language, metadata)
62
60
63
61
64 class Highlight2Latex(NbConvertBase):
62 class Highlight2Latex(NbConvertBase):
@@ -78,10 +76,11 b' class Highlight2Latex(NbConvertBase):'
78 strip_verbatim : bool
76 strip_verbatim : bool
79 remove the Verbatim environment that pygments provides by default
77 remove the Verbatim environment that pygments provides by default
80 """
78 """
79 from pygments.formatters import LatexFormatter
81 if not language:
80 if not language:
82 language=self.default_language
81 language=self.default_language
83
82
84 latex = _pygment_highlight(source, LatexFormatter(), language, metadata)
83 latex = _pygments_highlight(source, LatexFormatter(), language, metadata)
85 if strip_verbatim:
84 if strip_verbatim:
86 latex = latex.replace(r'\begin{Verbatim}[commandchars=\\\{\}]' + '\n', '')
85 latex = latex.replace(r'\begin{Verbatim}[commandchars=\\\{\}]' + '\n', '')
87 return latex.replace('\n\\end{Verbatim}\n', '')
86 return latex.replace('\n\\end{Verbatim}\n', '')
@@ -90,7 +89,7 b' class Highlight2Latex(NbConvertBase):'
90
89
91
90
92
91
93 def _pygment_highlight(source, output_formatter, language='ipython', metadata=None):
92 def _pygments_highlight(source, output_formatter, language='ipython', metadata=None):
94 """
93 """
95 Return a syntax-highlighted version of the input source
94 Return a syntax-highlighted version of the input source
96
95
@@ -104,6 +103,9 b" def _pygment_highlight(source, output_formatter, language='ipython', metadata=No"
104 metadata : NotebookNode cell metadata
103 metadata : NotebookNode cell metadata
105 metadata of the cell to highlight
104 metadata of the cell to highlight
106 """
105 """
106 from pygments import highlight
107 from pygments.lexers import get_lexer_by_name
108 from IPython.nbconvert.utils.lexers import IPythonLexer
107
109
108 # If the cell uses a magic extension language,
110 # If the cell uses a magic extension language,
109 # use the magic language instead.
111 # use the magic language instead.
@@ -118,4 +120,4 b" def _pygment_highlight(source, output_formatter, language='ipython', metadata=No"
118 else:
120 else:
119 lexer = get_lexer_by_name(language, stripall=True)
121 lexer = get_lexer_by_name(language, stripall=True)
120
122
121 return pygements_highlight(source, lexer, output_formatter)
123 return highlight(source, lexer, output_formatter)
@@ -15,8 +15,6 b''
15 import os
15 import os
16 import io
16 import io
17
17
18 from pygments.formatters import HtmlFormatter
19
20 from IPython.utils import path
18 from IPython.utils import path
21
19
22 from .base import Preprocessor
20 from .base import Preprocessor
@@ -83,6 +81,7 b' class CSSHTMLHeaderPreprocessor(Preprocessor):'
83 Fills self.header with lines of CSS extracted from IPython
81 Fills self.header with lines of CSS extracted from IPython
84 and Pygments.
82 and Pygments.
85 """
83 """
84 from pygments.formatters import HtmlFormatter
86
85
87 #Clear existing header.
86 #Clear existing header.
88 header = []
87 header = []
General Comments 0
You need to be logged in to leave comments. Login now