diff --git a/IPython/html/base/handlers.py b/IPython/html/base/handlers.py
index f63438a7f..c9333804f 100644
--- a/IPython/html/base/handlers.py
+++ b/IPython/html/base/handlers.py
@@ -24,7 +24,13 @@ import os
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:
@@ -44,14 +50,7 @@ UF_HIDDEN = getattr(stat, 'UF_HIDDEN', 32768)
# Top-level handlers
#-----------------------------------------------------------------------------
-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):
@@ -209,6 +208,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')
+ 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"""
diff --git a/IPython/html/notebookapp.py b/IPython/html/notebookapp.py
index 0dc0dc258..85b3fc26f 100644
--- a/IPython/html/notebookapp.py
+++ b/IPython/html/notebookapp.py
@@ -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