diff --git a/IPython/frontend/html/notebook/handlers/base.py b/IPython/frontend/html/notebook/handlers/base.py
index 5d42ea2..adf1e3c 100644
--- a/IPython/frontend/html/notebook/handlers/base.py
+++ b/IPython/frontend/html/notebook/handlers/base.py
@@ -274,3 +274,11 @@ class AuthenticatedFileHandler(IPythonHandler, web.StaticFileHandler):
@authenticate_unless_readonly
def get(self, path):
return web.StaticFileHandler.get(self, path)
+
+
+#-----------------------------------------------------------------------------
+# URL to handler mappings
+#-----------------------------------------------------------------------------
+
+
+default_handlers = []
diff --git a/IPython/frontend/html/notebook/handlers/clustersapi.py b/IPython/frontend/html/notebook/handlers/clustersapi.py
index 25b35d7..486ada3 100644
--- a/IPython/frontend/html/notebook/handlers/clustersapi.py
+++ b/IPython/frontend/html/notebook/handlers/clustersapi.py
@@ -55,3 +55,18 @@ class ClusterActionHandler(IPythonHandler):
if action == 'stop':
data = cm.stop_cluster(profile)
self.finish(jsonapi.dumps(data))
+
+
+#-----------------------------------------------------------------------------
+# URL to handler mappings
+#-----------------------------------------------------------------------------
+
+
+_cluster_action_regex = r"(?Pstart|stop)"
+_profile_regex = r"(?P[^\/]+)" # there is almost no text that is invalid
+
+default_handlers = [
+ (r"/clusters", MainClusterHandler),
+ (r"/clusters/%s/%s" % (_profile_regex, _cluster_action_regex), ClusterActionHandler),
+ (r"/clusters/%s" % _profile_regex, ClusterProfileHandler),
+]
diff --git a/IPython/frontend/html/notebook/handlers/kernelsapi.py b/IPython/frontend/html/notebook/handlers/kernelsapi.py
index c1519d3..5c59015 100644
--- a/IPython/frontend/html/notebook/handlers/kernelsapi.py
+++ b/IPython/frontend/html/notebook/handlers/kernelsapi.py
@@ -244,8 +244,28 @@ class IOPubHandler(ZMQChannelHandler):
"""IOPub messages make no sense"""
pass
+
class ShellHandler(ZMQChannelHandler):
channel = 'shell'
+
class StdinHandler(ZMQChannelHandler):
channel = 'stdin'
+
+
+#-----------------------------------------------------------------------------
+# URL to handler mappings
+#-----------------------------------------------------------------------------
+
+
+_kernel_id_regex = r"(?P\w+-\w+-\w+-\w+-\w+)"
+_kernel_action_regex = r"(?Prestart|interrupt)"
+
+default_handlers = [
+ (r"/kernels", MainKernelHandler),
+ (r"/kernels/%s" % _kernel_id_regex, KernelHandler),
+ (r"/kernels/%s/%s" % (_kernel_id_regex, _kernel_action_regex), KernelActionHandler),
+ (r"/kernels/%s/iopub" % _kernel_id_regex, IOPubHandler),
+ (r"/kernels/%s/shell" % _kernel_id_regex, ShellHandler),
+ (r"/kernels/%s/stdin" % _kernel_id_regex, StdinHandler)
+]
diff --git a/IPython/frontend/html/notebook/handlers/login.py b/IPython/frontend/html/notebook/handlers/login.py
index 27d953c..f63cc8b 100644
--- a/IPython/frontend/html/notebook/handlers/login.py
+++ b/IPython/frontend/html/notebook/handlers/login.py
@@ -28,7 +28,6 @@ from .base import IPythonHandler
# Handler
#-----------------------------------------------------------------------------
-
class LoginHandler(IPythonHandler):
def _render(self, message=None):
@@ -55,3 +54,9 @@ class LoginHandler(IPythonHandler):
self.redirect(self.get_argument('next', default=self.base_project_url))
+#-----------------------------------------------------------------------------
+# URL to handler mappings
+#-----------------------------------------------------------------------------
+
+
+default_handlers = [(r"/login", LoginHandler)]
diff --git a/IPython/frontend/html/notebook/handlers/logout.py b/IPython/frontend/html/notebook/handlers/logout.py
index 7474eba..43cefc3 100644
--- a/IPython/frontend/html/notebook/handlers/logout.py
+++ b/IPython/frontend/html/notebook/handlers/logout.py
@@ -34,3 +34,11 @@ class LogoutHandler(IPythonHandler):
'is disabled.'}
self.write(self.render_template('logout.html',
message=message))
+
+
+#-----------------------------------------------------------------------------
+# URL to handler mappings
+#-----------------------------------------------------------------------------
+
+
+default_handlers = [(r"/logout", LogoutHandler)]
\ No newline at end of file
diff --git a/IPython/frontend/html/notebook/handlers/notebooks.py b/IPython/frontend/html/notebook/handlers/notebooks.py
index 3d14799..1a51843 100644
--- a/IPython/frontend/html/notebook/handlers/notebooks.py
+++ b/IPython/frontend/html/notebook/handlers/notebooks.py
@@ -73,3 +73,19 @@ class NotebookCopyHandler(IPythonHandler):
notebook_id = self.notebook_manager.copy_notebook(notebook_id)
self.redirect(url_path_join(self.base_project_url, notebook_id))
+
+#-----------------------------------------------------------------------------
+# URL to handler mappings
+#-----------------------------------------------------------------------------
+
+
+_notebook_id_regex = r"(?P\w+-\w+-\w+-\w+-\w+)"
+_notebook_name_regex = r"(?P.+\.ipynb)"
+
+default_handlers = [
+ (r"/new", NewHandler),
+ (r"/%s" % _notebook_id_regex, NamedNotebookHandler),
+ (r"/%s" % _notebook_name_regex, NotebookRedirectHandler),
+ (r"/%s/copy" % _notebook_id_regex, NotebookCopyHandler),
+
+]
diff --git a/IPython/frontend/html/notebook/handlers/notebooksapi.py b/IPython/frontend/html/notebook/handlers/notebooksapi.py
index c0f4db7..a0aa477 100644
--- a/IPython/frontend/html/notebook/handlers/notebooksapi.py
+++ b/IPython/frontend/html/notebook/handlers/notebooksapi.py
@@ -134,5 +134,23 @@ class ModifyNotebookCheckpointsHandler(IPythonHandler):
self.finish()
+#-----------------------------------------------------------------------------
+# URL to handler mappings
+#-----------------------------------------------------------------------------
+
+
+_notebook_id_regex = r"(?P\w+-\w+-\w+-\w+-\w+)"
+_checkpoint_id_regex = r"(?P[\w-]+)"
+
+default_handlers = [
+ (r"/notebooks", NotebookRootHandler),
+ (r"/notebooks/%s" % _notebook_id_regex, NotebookHandler),
+ (r"/notebooks/%s/checkpoints" % _notebook_id_regex, NotebookCheckpointsHandler),
+ (r"/notebooks/%s/checkpoints/%s" % (_notebook_id_regex, _checkpoint_id_regex),
+ ModifyNotebookCheckpointsHandler
+ ),
+]
+
+
diff --git a/IPython/frontend/html/notebook/handlers/tree.py b/IPython/frontend/html/notebook/handlers/tree.py
index ffcfce6..3133f95 100644
--- a/IPython/frontend/html/notebook/handlers/tree.py
+++ b/IPython/frontend/html/notebook/handlers/tree.py
@@ -31,3 +31,11 @@ class ProjectDashboardHandler(IPythonHandler):
project=self.project,
project_component=self.project.split('/'),
))
+
+
+#-----------------------------------------------------------------------------
+# URL to handler mappings
+#-----------------------------------------------------------------------------
+
+
+default_handlers = [(r"/", ProjectDashboardHandler)]
\ No newline at end of file
diff --git a/IPython/frontend/html/notebook/notebookapp.py b/IPython/frontend/html/notebook/notebookapp.py
index cf6b7e2..75ae14b 100644
--- a/IPython/frontend/html/notebook/notebookapp.py
+++ b/IPython/frontend/html/notebook/notebookapp.py
@@ -115,14 +115,6 @@ from .utils import url_path_join
# Module globals
#-----------------------------------------------------------------------------
-_kernel_id_regex = r"(?P\w+-\w+-\w+-\w+-\w+)"
-_kernel_action_regex = r"(?Prestart|interrupt)"
-_notebook_id_regex = r"(?P\w+-\w+-\w+-\w+-\w+)"
-_notebook_name_regex = r"(?P.+\.ipynb)"
-_checkpoint_id_regex = r"(?P[\w-]+)"
-_profile_regex = r"(?P[^\/]+)" # there is almost no text that is invalid
-_cluster_action_regex = r"(?Pstart|stop)"
-
_examples = """
ipython notebook # start the notebook
ipython notebook --profile=sympy # use the sympy profile
@@ -146,6 +138,12 @@ def random_ports(port, n):
for i in range(n-5):
yield port + random.randint(-2*n, 2*n)
+def load_handlers(name):
+ """Load the (URL pattern, handler) tuples for each component."""
+ name = 'IPython.frontend.html.notebook.handlers.' + name
+ mod = __import__(name, fromlist=['default_handlers'])
+ return mod.default_handlers
+
#-----------------------------------------------------------------------------
# The Tornado web application
#-----------------------------------------------------------------------------
@@ -155,31 +153,20 @@ class NotebookWebApplication(web.Application):
def __init__(self, ipython_app, kernel_manager, notebook_manager,
cluster_manager, log,
base_project_url, settings_overrides):
- handlers = [
- (r"/", ProjectDashboardHandler),
- (r"/login", LoginHandler),
- (r"/logout", LogoutHandler),
- (r"/new", NewHandler),
- (r"/%s" % _notebook_id_regex, NamedNotebookHandler),
- (r"/%s" % _notebook_name_regex, NotebookRedirectHandler),
- (r"/%s/copy" % _notebook_id_regex, NotebookCopyHandler),
- (r"/kernels", MainKernelHandler),
- (r"/kernels/%s" % _kernel_id_regex, KernelHandler),
- (r"/kernels/%s/%s" % (_kernel_id_regex, _kernel_action_regex), KernelActionHandler),
- (r"/kernels/%s/iopub" % _kernel_id_regex, IOPubHandler),
- (r"/kernels/%s/shell" % _kernel_id_regex, ShellHandler),
- (r"/kernels/%s/stdin" % _kernel_id_regex, StdinHandler),
- (r"/notebooks", NotebookRootHandler),
- (r"/notebooks/%s" % _notebook_id_regex, NotebookHandler),
- (r"/notebooks/%s/checkpoints" % _notebook_id_regex, NotebookCheckpointsHandler),
- (r"/notebooks/%s/checkpoints/%s" % (_notebook_id_regex, _checkpoint_id_regex),
- ModifyNotebookCheckpointsHandler
- ),
+
+ # Load the (URL pattern, handler) tuples for each component.
+ handlers = []
+ handlers.extend(load_handlers('base'))
+ handlers.extend(load_handlers('tree'))
+ handlers.extend(load_handlers('login'))
+ handlers.extend(load_handlers('logout'))
+ handlers.extend(load_handlers('notebooks'))
+ handlers.extend(load_handlers('kernelsapi'))
+ handlers.extend(load_handlers('notebooksapi'))
+ handlers.extend(load_handlers('clustersapi'))
+ handlers.extend([
(r"/files/(.*)", AuthenticatedFileHandler, {'path' : notebook_manager.notebook_dir}),
- (r"/clusters", MainClusterHandler),
- (r"/clusters/%s/%s" % (_profile_regex, _cluster_action_regex), ClusterActionHandler),
- (r"/clusters/%s" % _profile_regex, ClusterProfileHandler),
- ]
+ ])
# Python < 2.6.5 doesn't accept unicode keys in f(**kwargs), and
# base_project_url will always be unicode, which will in turn