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 |
|
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 |
|
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 |
|
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