simplify app mixin

get handler classes from instance attributes, rather than arguments

simplifies API
This commit is contained in:
Min RK
2020-07-31 10:27:32 +02:00
parent d0da677813
commit d2a42a69b0
4 changed files with 56 additions and 75 deletions

View File

@@ -4,3 +4,10 @@ Contains default notebook-app subclass and mixins
""" """
from .app import main from .app import main
from .app import SingleUserNotebookApp from .app import SingleUserNotebookApp
from .mixins import HubAuthenticatedHandler
from .mixins import make_singleuser_app
# backward-compatibility
JupyterHubLoginHandler = SingleUserNotebookApp.login_handler_class
JupyterHubLogoutHandler = SingleUserNotebookApp.logout_handler_class
OAuthCallbackHandler = SingleUserNotebookApp.oauth_callback_handler_class

View File

@@ -2,12 +2,6 @@
- $JUPYTERHUB_SINGLEUSER_APP, the base Application class, to be wrapped in JupyterHub authentication. - $JUPYTERHUB_SINGLEUSER_APP, the base Application class, to be wrapped in JupyterHub authentication.
default: notebook.notebookapp.NotebookApp default: notebook.notebookapp.NotebookApp
- $JUPYTERHUB_SINGLEUSER_PKG, e.g. notebook or jupyter_server.
Typically inferred from $JUPYTEHUB_SINGLEUSER_APP.
Package layout must include:
- base.handlers.JupyterHandler
- auth.login.LoginHandler
- auth.logout.LogoutHandler
""" """
import os import os
@@ -18,38 +12,9 @@ from .mixins import make_singleuser_app
JUPYTERHUB_SINGLEUSER_APP = ( JUPYTERHUB_SINGLEUSER_APP = (
os.environ.get("JUPYTERHUB_SINGLEUSER_APP") or "notebook.notebookapp.NotebookApp" os.environ.get("JUPYTERHUB_SINGLEUSER_APP") or "notebook.notebookapp.NotebookApp"
) )
JUPYTERHUB_SINGLEUSER_PKG = os.environ.get("JUPYTERHUB_SINGLEUSER_PKG") or "notebook"
App = import_item(JUPYTERHUB_SINGLEUSER_APP) App = import_item(JUPYTERHUB_SINGLEUSER_APP)
JUPYTERHUB_SINGLEUSER_PKG = os.environ.get("JUPYTERHUB_SINGLEUSER_PKG") SingleUserNotebookApp = make_singleuser_app(App)
if not JUPYTERHUB_SINGLEUSER_PKG:
# guess notebook or jupyter_server based on App class
for cls in App.mro():
pkg = cls.__module__.split(".", 1)[0]
if pkg in {"notebook", "jupyter_server"}:
JUPYTERHUB_SINGLEUSER_PKG = pkg
break
else:
raise RuntimeError(
"Failed to infer JUPYTERHUB_SINGLEUSER_PKG from {}, please set it directly".format(
JUPYTERHUB_SINGLEUSER_APP
)
)
LoginHandler = import_item(pkg + ".auth.login.LoginHandler")
LogoutHandler = import_item(pkg + ".auth.logout.LogoutHandler")
# BaseHandler could be called JupyterHandler or old IPythonHandler
try:
BaseHandler = import_item(pkg + ".base.handlers.JupyterHandler")
except ImportError:
BaseHandler = import_item(pkg + ".base.handlers.IPythonHandler")
SingleUserNotebookApp = make_singleuser_app(
App,
LoginHandler=LoginHandler,
LogoutHandler=LogoutHandler,
BaseHandler=BaseHandler,
)
main = SingleUserNotebookApp.launch_instance main = SingleUserNotebookApp.launch_instance

View File

@@ -31,6 +31,7 @@ from traitlets import Bool
from traitlets import Bytes from traitlets import Bytes
from traitlets import CUnicode from traitlets import CUnicode
from traitlets import default from traitlets import default
from traitlets import import_item
from traitlets import Integer from traitlets import Integer
from traitlets import observe from traitlets import observe
from traitlets import TraitError from traitlets import TraitError
@@ -208,9 +209,6 @@ def _exclude_home(path_list):
yield p yield p
from traitlets import HasTraits
class SingleUserNotebookAppMixin(Configurable): class SingleUserNotebookAppMixin(Configurable):
"""A Subclass of the regular NotebookApp that is aware of the parent multiuser context.""" """A Subclass of the regular NotebookApp that is aware of the parent multiuser context."""
@@ -660,23 +658,57 @@ class SingleUserNotebookAppMixin(Configurable):
env.loader = ChoiceLoader([FunctionLoader(get_page), orig_loader]) env.loader = ChoiceLoader([FunctionLoader(get_page), orig_loader])
def make_singleuser_app(App, LoginHandler, LogoutHandler, BaseHandler): def detect_base_package(App):
"""Detect the base package for an App class
Will return 'notebook' or 'jupyter_server'
based on which package App subclasses from.
Will return None if neither is identified (e.g. fork package, or duck-typing).
"""
# guess notebook or jupyter_server based on App class inheritance
for cls in App.mro():
pkg = cls.__module__.split(".", 1)[0]
if pkg in {"notebook", "jupyter_server"}:
return pkg
return None
def make_singleuser_app(App):
"""Make and return a singleuser notebook app """Make and return a singleuser notebook app
given existing notebook or jupyter_server classes, given existing notebook or jupyter_server Application classes,
mix-in jupyterhub auth. mix-in jupyterhub auth.
Instances of App must have the following attributes defining classes:
- .login_handler_class
- .logout_handler_class
- .base_handler_class (only required if not a subclass of the default app
in jupyter_server or notebook)
App should be a subclass of `notebook.notebookapp.NotebookApp` App should be a subclass of `notebook.notebookapp.NotebookApp`
or `jupyter_server.serverapp.ServerApp` or `jupyter_server.serverapp.ServerApp`.
Must be passed base classes for:
- App
- LoginHandler
- LogoutHandler
- BaseHandler
""" """
# create handler classes from mixins + bases
empty_parent_app = App()
# detect base classes
LoginHandler = empty_parent_app.login_handler_class
LogoutHandler = empty_parent_app.logout_handler_class
BaseHandler = getattr(empty_parent_app, "base_handler_class", None)
if BaseHandler is None:
pkg = detect_base_package(App)
if pkg == "jupyter_server":
BaseHandler = import_item("jupyter_server.base.handlers.JupyterHandler")
elif pkg == "notebook":
BaseHandler = import_item("notebook.base.handlers.IPythonHandler")
else:
raise ValueError(
"{}.base_handler_class must be defined".format(App.__name__)
)
# create Handler classes from mixins + bases
class JupyterHubLoginHandler(JupyterHubLoginHandlerMixin, LoginHandler): class JupyterHubLoginHandler(JupyterHubLoginHandlerMixin, LoginHandler):
pass pass
@@ -687,13 +719,12 @@ def make_singleuser_app(App, LoginHandler, LogoutHandler, BaseHandler):
pass pass
# create merged aliases & flags # create merged aliases & flags
empty_parent_app = App()
merged_aliases = {} merged_aliases = {}
merged_aliases.update(empty_parent_app.aliases) merged_aliases.update(empty_parent_app.aliases or {})
merged_aliases.update(aliases) merged_aliases.update(aliases)
merged_flags = {} merged_flags = {}
merged_flags.update(empty_parent_app.flags) merged_flags.update(empty_parent_app.flags or {})
merged_flags.update(flags) merged_flags.update(flags)
# create mixed-in App class, bringing it all together # create mixed-in App class, bringing it all together
class SingleUserNotebookApp(SingleUserNotebookAppMixin, App): class SingleUserNotebookApp(SingleUserNotebookAppMixin, App):

View File

@@ -1,22 +0,0 @@
#!/usr/bin/env python
"""Extend regular notebook server to be aware of multiuser things."""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
from notebook.auth.login import LoginHandler
from notebook.auth.logout import LogoutHandler
from notebook.base.handlers import IPythonHandler
from notebook.notebookapp import NotebookApp
from .mixins import make_singleuser_app
SingleUserNotebookApp = make_singleuser_app(
NotebookApp=NotebookApp,
LoginHandler=LoginHandler,
LogoutHandler=LogoutHandler,
BaseHandler=IPythonHandler,
)
main = SingleUserNotebookApp.launch_instance
if __name__ == '__main__':
main()