From c6325f3d852358aebd77b10c1e0ce0018f2b4731 Mon Sep 17 00:00:00 2001 From: David Brochart Date: Fri, 28 Jul 2023 22:22:26 +0200 Subject: [PATCH] Support Jupyverse as a single-user server --- jupyterhub/singleuser/__init__.py | 27 +++++++++----- jupyterhub/singleuser/app.py | 61 ++++++++++++++++++++++--------- 2 files changed, 61 insertions(+), 27 deletions(-) diff --git a/jupyterhub/singleuser/__init__.py b/jupyterhub/singleuser/__init__.py index 9c1c8269..a8d95762 100644 --- a/jupyterhub/singleuser/__init__.py +++ b/jupyterhub/singleuser/__init__.py @@ -28,15 +28,20 @@ if not _extension_env: # - extension, if jupyter server 2 # - older subclass app, otherwise try: - import jupyter_server + import jupyverse_api # noqa: F401 - _server_major = int(jupyter_server.__version__.split(".", 1)[0]) - except Exception: - # don't have jupyter-server, assume classic notebook _as_extension = False - else: - # default to extension if jupyter-server >=2 - _as_extension = _server_major >= 2 + except Exception: + try: + import jupyter_server + + _server_major = int(jupyter_server.__version__.split(".", 1)[0]) + except Exception: + # don't have jupyter-server, assume classic notebook + _as_extension = False + else: + # default to extension if jupyter-server >=2 + _as_extension = _server_major >= 2 elif _app_env == "extension": _as_extension = True @@ -67,9 +72,11 @@ else: from .app import SingleUserNotebookApp, main # backward-compatibility - JupyterHubLoginHandler = SingleUserNotebookApp.login_handler_class - JupyterHubLogoutHandler = SingleUserNotebookApp.logout_handler_class - OAuthCallbackHandler = SingleUserNotebookApp.oauth_callback_handler_class + if SingleUserNotebookApp is not None: + # not Jupyverse + JupyterHubLoginHandler = SingleUserNotebookApp.login_handler_class + JupyterHubLogoutHandler = SingleUserNotebookApp.logout_handler_class + OAuthCallbackHandler = SingleUserNotebookApp.oauth_callback_handler_class __all__ = [ diff --git a/jupyterhub/singleuser/app.py b/jupyterhub/singleuser/app.py index 818ff51a..ceed1bba 100644 --- a/jupyterhub/singleuser/app.py +++ b/jupyterhub/singleuser/app.py @@ -9,6 +9,7 @@ Use JUPYTERHUB_SINGLEUSER_APP='notebook' for the legacy 'classic' notebook server (requires notebook<7). """ import os +from urllib.parse import urlparse from traitlets import import_item @@ -27,6 +28,7 @@ JUPYTERHUB_SINGLEUSER_APP = _app_shortcuts.get( JUPYTERHUB_SINGLEUSER_APP.replace("_", "-"), JUPYTERHUB_SINGLEUSER_APP ) +jupyverse = None if JUPYTERHUB_SINGLEUSER_APP: if JUPYTERHUB_SINGLEUSER_APP in {"notebook", _app_shortcuts["notebook"]}: @@ -48,25 +50,33 @@ if JUPYTERHUB_SINGLEUSER_APP: ) App = import_item(JUPYTERHUB_SINGLEUSER_APP) else: - App = None - _import_error = None - for JUPYTERHUB_SINGLEUSER_APP in ( - "jupyter_server.serverapp.ServerApp", - "notebook.notebookapp.NotebookApp", - ): - try: - App = import_item(JUPYTERHUB_SINGLEUSER_APP) - except ImportError as e: - if _import_error is None: - _import_error = e - continue - else: - break - if App is None: - raise _import_error + try: + from jupyverse_api.cli import main as jupyverse + + App = None + except Exception: + App = None + _import_error = None + for JUPYTERHUB_SINGLEUSER_APP in ( + "jupyter_server.serverapp.ServerApp", + "notebook.notebookapp.NotebookApp", + ): + try: + App = import_item(JUPYTERHUB_SINGLEUSER_APP) + except ImportError as e: + if _import_error is None: + _import_error = e + continue + else: + break + if App is None: + raise _import_error -SingleUserNotebookApp = make_singleuser_app(App) +if App is None: + SingleUserNotebookApp = None +else: + SingleUserNotebookApp = make_singleuser_app(App) def main(): @@ -77,6 +87,23 @@ def main(): # This is a minimally extended ServerApp that does: # 1. ensure lab extension is enabled, and # 2. set default URL to `/lab` + if jupyverse: + service_url = os.environ.get("JUPYTERHUB_SERVICE_URL") + url = urlparse(service_url) + try: + return jupyverse.callback( + open_browser=True, + host=url.hostname, + port=url.port, + set_=[ + f"frontend.base_url={url.path}", + f"app.mount_path={url.path}", + ], + disable=[], + ) + except Exception: + return + import re _version_pat = re.compile(r"(\d+)\.(\d+)")