From e80fbf72043a447f2049026f2f716f59509d55e4 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Sat, 1 Jun 2019 04:30:28 -0500 Subject: [PATCH 01/26] wip hub integration --- jupyterlab/extension.py | 8 +++ jupyterlab/labhubapp.py | 18 +----- packages/hub-extension/README.md | 7 +++ packages/hub-extension/package.json | 38 ++++++++++++ packages/hub-extension/src/index.ts | 89 ++++++++++++++++++++++++++++ packages/hub-extension/tsconfig.json | 8 +++ packages/metapackage/package.json | 77 ++++++++++++++++++++++++ packages/metapackage/tsconfig.json | 3 + 8 files changed, 233 insertions(+), 15 deletions(-) create mode 100644 packages/hub-extension/README.md create mode 100644 packages/hub-extension/package.json create mode 100644 packages/hub-extension/src/index.ts create mode 100644 packages/hub-extension/tsconfig.json diff --git a/jupyterlab/extension.py b/jupyterlab/extension.py index 6044127f84d5..3cba00b3c9f9 100644 --- a/jupyterlab/extension.py +++ b/jupyterlab/extension.py @@ -204,6 +204,14 @@ def load_jupyter_server_extension(nbapp): # Must add before the root server handlers to avoid shadowing. web_app.add_handlers('.*$', handlers) + # If running under JupyterHub, add more metadata. + if hasattr(nbapp, 'hub_prefix'): + settings['page_config_data']['hub_prefix'] = nbapp.hub_prefix + settings['page_config_data']['hub_host'] = nbapp.hub_host + settings['page_config_data']['hub_user'] = nbapp.user + api_token = os.getenv('JUPYTERHUB_API_TOKEN', '') + settings['page_config_data']['token'] = api_token + # Add the root handlers if we have not errored. if not errored: add_handlers(web_app, config) diff --git a/jupyterlab/labhubapp.py b/jupyterlab/labhubapp.py index 7c039d1ced28..4aa6b892114f 100644 --- a/jupyterlab/labhubapp.py +++ b/jupyterlab/labhubapp.py @@ -18,22 +18,10 @@ def _default_url(self): return "/lab" def init_webapp(self, *args, **kwargs): + warnings.warn( + "SingleUserLabApp is deprecated, use SingleUserNotebookApp and set " + "c.Spawner.default_url = '/lab' in jupyterhub_config.py", DeprecationWarning + ) super().init_webapp(*args, **kwargs) - settings = self.web_app.settings - if 'page_config_data' not in settings: - settings['page_config_data'] = {} - settings['page_config_data']['hub_prefix'] = self.hub_prefix - settings['page_config_data']['hub_host'] = self.hub_host - settings['page_config_data']['hub_user'] = self.user - api_token = os.getenv('JUPYTERHUB_API_TOKEN') - if not api_token: - api_token = '' - if not self.token: - try: - self.token = api_token - except AttributeError: - self.log.error("Can't set self.token") - settings['page_config_data']['token'] = api_token def main(argv=None): diff --git a/packages/hub-extension/README.md b/packages/hub-extension/README.md new file mode 100644 index 000000000000..ccfef2468120 --- /dev/null +++ b/packages/hub-extension/README.md @@ -0,0 +1,7 @@ +# @jupyterlab/hub-extension + +JupyterLab](https://github.com/jupyterlab/jupyterlab) integration for +[JupyterHub](https://github.com/jupyterhub/jupyterhub). + +This adds a "Hub" menu to JupyterLab that allows a user to log out of JupyterHub +or access their JupyterHub control panel. diff --git a/packages/hub-extension/package.json b/packages/hub-extension/package.json new file mode 100644 index 000000000000..fbd56872cb6f --- /dev/null +++ b/packages/hub-extension/package.json @@ -0,0 +1,38 @@ +{ + "name": "@jupyterlab/hub-extension", + "version": "1.0.0-alpha.8", + "description": "JupyterLab integration for JupyterHub", + "homepage": "https://github.com/jupyterlab/jupyterlab", + "bugs": { + "url": "https://github.com/jupyterlab/jupyterlab/issues" + }, + "repository": { + "type": "git", + "url": "https://github.com/jupyterlab/jupyterlab.git" + }, + "license": "BSD-3-Clause", + "author": "Project Jupyter", + "files": [ + "lib/**/*.{d.ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}", + "schema/*.json", + "style/**/*.{css,eot,gif,html,jpg,json,png,svg,woff2,ttf}" + ], + "main": "lib/index.js", + "types": "lib/index.d.ts", + "directories": { + "lib": "lib/" + }, + "scripts": { + "build": "tsc", + "clean": "rimraf lib", + "prepublishOnly": "npm run build", + "watch": "tsc -w --listEmittedFiles" + }, + "devDependencies": { + "rimraf": "~2.6.2", + "typescript": "~3.5.1" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/packages/hub-extension/src/index.ts b/packages/hub-extension/src/index.ts new file mode 100644 index 000000000000..67358160dab3 --- /dev/null +++ b/packages/hub-extension/src/index.ts @@ -0,0 +1,89 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) Jupyter Development Team. +| Distributed under the terms of the Modified BSD License. +|----------------------------------------------------------------------------*/ + +import { Menu } from '@phosphor/widgets'; + +import { ICommandPalette } from '@jupyterlab/apputils'; + +import { + JupyterFrontEnd, + JupyterFrontEndPlugin +} from '@jupyterlab/application'; + +import { PageConfig, URLExt } from '@jupyterlab/coreutils'; + +import { IMainMenu } from '@jupyterlab/mainmenu'; + +/** + * The command IDs used by the plugin. + */ +export namespace CommandIDs { + export const controlPanel: string = 'hub:control-panel'; + + export const logout: string = 'hub:logout'; +} + +/** + * Activate the jupyterhub extension. + */ +function activateHubExtension( + app: JupyterFrontEnd, + palette: ICommandPalette, + mainMenu: IMainMenu +): void { + const hubHost = PageConfig.getOption('hub_host'); + const hubPrefix = PageConfig.getOption('hub_prefix'); + const baseUrl = PageConfig.getBaseUrl(); + + // Bail if not running on JupyterHub. + if (!hubPrefix) { + return; + } + + console.log('hub-extension: Found configuration ', { + hubHost: hubHost, + hubPrefix: hubPrefix + }); + + const category = 'Hub'; + const { commands } = app; + + commands.addCommand(CommandIDs.controlPanel, { + label: 'Control Panel', + caption: 'Open the Hub control panel in a new browser tab', + execute: () => { + window.open(hubHost + URLExt.join(hubPrefix, 'home'), '_blank'); + } + }); + + commands.addCommand(CommandIDs.logout, { + label: 'Logout', + caption: 'Log out of the Hub', + execute: () => { + window.location.href = hubHost + URLExt.join(baseUrl, 'logout'); + } + }); + + // Add commands and menu itmes. + let menu = new Menu({ commands }); + menu.title.label = category; + [CommandIDs.controlPanel, CommandIDs.logout].forEach(command => { + palette.addItem({ command, category }); + menu.addItem({ command }); + }); + mainMenu.addMenu(menu, { rank: 100 }); +} + +/** + * Initialization data for the hub-extension. + */ +const hubExtension: JupyterFrontEndPlugin = { + activate: activateHubExtension, + id: 'jupyter.extensions.hub-extension', + requires: [ICommandPalette, IMainMenu], + autoStart: true +}; + +export default hubExtension; diff --git a/packages/hub-extension/tsconfig.json b/packages/hub-extension/tsconfig.json new file mode 100644 index 000000000000..a1e4ba13c4bc --- /dev/null +++ b/packages/hub-extension/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfigbase", + "compilerOptions": { + "outDir": "lib", + "rootDir": "src" + }, + "include": ["src/*"] +} diff --git a/packages/metapackage/package.json b/packages/metapackage/package.json index 35648eea1cf1..ae445355d679 100644 --- a/packages/metapackage/package.json +++ b/packages/metapackage/package.json @@ -30,6 +30,7 @@ "watch": "tsc -b --watch" }, "dependencies": { +<<<<<<< HEAD "@jupyterlab/application": "^1.0.0-alpha.9", "@jupyterlab/application-extension": "^1.0.0-alpha.9", "@jupyterlab/apputils": "^1.0.0-alpha.9", @@ -103,6 +104,82 @@ "@jupyterlab/vdom": "^1.0.0-alpha.9", "@jupyterlab/vdom-extension": "^1.0.0-alpha.9", "@jupyterlab/vega5-extension": "^1.0.0-alpha.9" +======= + "@jupyterlab/application": "^1.0.0-alpha.8", + "@jupyterlab/application-extension": "^1.0.0-alpha.8", + "@jupyterlab/apputils": "^1.0.0-alpha.8", + "@jupyterlab/apputils-extension": "^1.0.0-alpha.8", + "@jupyterlab/attachments": "^1.0.0-alpha.8", + "@jupyterlab/cells": "^1.0.0-alpha.9", + "@jupyterlab/codeeditor": "^1.0.0-alpha.8", + "@jupyterlab/codemirror": "^1.0.0-alpha.8", + "@jupyterlab/codemirror-extension": "^1.0.0-alpha.8", + "@jupyterlab/completer": "^1.0.0-alpha.8", + "@jupyterlab/completer-extension": "^1.0.0-alpha.8", + "@jupyterlab/console": "^1.0.0-alpha.8", + "@jupyterlab/console-extension": "^1.0.0-alpha.8", + "@jupyterlab/coreutils": "^3.0.0-alpha.8", + "@jupyterlab/csvviewer": "^1.0.0-alpha.8", + "@jupyterlab/csvviewer-extension": "^1.0.0-alpha.9", + "@jupyterlab/docmanager": "^1.0.0-alpha.8", + "@jupyterlab/docmanager-extension": "^1.0.0-alpha.8", + "@jupyterlab/docregistry": "^1.0.0-alpha.8", + "@jupyterlab/documentsearch": "^1.0.0-alpha.9", + "@jupyterlab/documentsearch-extension": "^1.0.0-alpha.10", + "@jupyterlab/extensionmanager": "^1.0.0-alpha.8", + "@jupyterlab/extensionmanager-extension": "^1.0.0-alpha.8", + "@jupyterlab/faq-extension": "^1.0.0-alpha.8", + "@jupyterlab/filebrowser": "^1.0.0-alpha.8", + "@jupyterlab/filebrowser-extension": "^1.0.0-alpha.8", + "@jupyterlab/fileeditor": "^1.0.0-alpha.8", + "@jupyterlab/fileeditor-extension": "^1.0.0-alpha.8", + "@jupyterlab/help-extension": "^1.0.0-alpha.8", + "@jupyterlab/htmlviewer": "^1.0.0-alpha.9", + "@jupyterlab/htmlviewer-extension": "^1.0.0-alpha.9", + "@jupyterlab/hub-extension": "^1.0.0-alpha.8", + "@jupyterlab/imageviewer": "^1.0.0-alpha.8", + "@jupyterlab/imageviewer-extension": "^1.0.0-alpha.8", + "@jupyterlab/inspector": "^1.0.0-alpha.8", + "@jupyterlab/inspector-extension": "^1.0.0-alpha.8", + "@jupyterlab/javascript-extension": "^1.0.0-alpha.8", + "@jupyterlab/json-extension": "^1.0.0-alpha.8", + "@jupyterlab/launcher": "^1.0.0-alpha.8", + "@jupyterlab/launcher-extension": "^1.0.0-alpha.8", + "@jupyterlab/mainmenu": "^1.0.0-alpha.8", + "@jupyterlab/mainmenu-extension": "^1.0.0-alpha.8", + "@jupyterlab/markdownviewer": "^1.0.0-alpha.8", + "@jupyterlab/markdownviewer-extension": "^1.0.0-alpha.8", + "@jupyterlab/mathjax2": "^1.0.0-alpha.8", + "@jupyterlab/mathjax2-extension": "^1.0.0-alpha.8", + "@jupyterlab/nbconvert-css": "^0.1.1-alpha.1", + "@jupyterlab/notebook": "^1.0.0-alpha.9", + "@jupyterlab/notebook-extension": "^1.0.0-alpha.8", + "@jupyterlab/observables": "^2.2.0-alpha.8", + "@jupyterlab/outputarea": "^1.0.0-alpha.8", + "@jupyterlab/pdf-extension": "^1.0.0-alpha.8", + "@jupyterlab/rendermime": "^1.0.0-alpha.8", + "@jupyterlab/rendermime-extension": "^1.0.0-alpha.8", + "@jupyterlab/rendermime-interfaces": "^1.3.0-alpha.8", + "@jupyterlab/running": "^1.0.0-alpha.8", + "@jupyterlab/running-extension": "^1.0.0-alpha.8", + "@jupyterlab/services": "^4.0.0-alpha.8", + "@jupyterlab/settingeditor": "^1.0.0-alpha.8", + "@jupyterlab/settingeditor-extension": "^1.0.0-alpha.8", + "@jupyterlab/shortcuts-extension": "^1.0.0-alpha.8", + "@jupyterlab/statusbar": "^1.0.0-alpha.8", + "@jupyterlab/statusbar-extension": "^1.0.0-alpha.8", + "@jupyterlab/tabmanager-extension": "^1.0.0-alpha.8", + "@jupyterlab/terminal": "^1.0.0-alpha.8", + "@jupyterlab/terminal-extension": "^1.0.0-alpha.8", + "@jupyterlab/theme-dark-extension": "^1.0.0-alpha.9", + "@jupyterlab/theme-light-extension": "^1.0.0-alpha.9", + "@jupyterlab/tooltip": "^1.0.0-alpha.8", + "@jupyterlab/tooltip-extension": "^1.0.0-alpha.8", + "@jupyterlab/ui-components": "^1.0.0-alpha.8", + "@jupyterlab/vdom": "^1.0.0-alpha.8", + "@jupyterlab/vdom-extension": "^1.0.0-alpha.8", + "@jupyterlab/vega5-extension": "^1.0.0-alpha.8" +>>>>>>> 107ef3b30... wip hub integration }, "devDependencies": { "fs-extra": "^8.0.1", diff --git a/packages/metapackage/tsconfig.json b/packages/metapackage/tsconfig.json index d5c6ed4b3cbb..fe8d0e17a6fc 100644 --- a/packages/metapackage/tsconfig.json +++ b/packages/metapackage/tsconfig.json @@ -99,6 +99,9 @@ { "path": "../htmlviewer-extension" }, + { + "path": "../hub-extension" + }, { "path": "../imageviewer" }, From 09782b7d77196229d4addab5b0917cb2f1c8550b Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Sat, 1 Jun 2019 04:38:50 -0500 Subject: [PATCH 02/26] integrity --- packages/hub-extension/package.json | 7 +++++++ packages/hub-extension/tsconfig.json | 16 +++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/packages/hub-extension/package.json b/packages/hub-extension/package.json index fbd56872cb6f..7f70b4a860a4 100644 --- a/packages/hub-extension/package.json +++ b/packages/hub-extension/package.json @@ -28,6 +28,13 @@ "prepublishOnly": "npm run build", "watch": "tsc -w --listEmittedFiles" }, + "dependencies": { + "@jupyterlab/application": "^1.0.0-alpha.8", + "@jupyterlab/apputils": "^1.0.0-alpha.8", + "@jupyterlab/coreutils": "^3.0.0-alpha.8", + "@jupyterlab/mainmenu": "^1.0.0-alpha.8", + "@phosphor/widgets": "^1.6.0" + }, "devDependencies": { "rimraf": "~2.6.2", "typescript": "~3.5.1" diff --git a/packages/hub-extension/tsconfig.json b/packages/hub-extension/tsconfig.json index a1e4ba13c4bc..17ecbea70fb2 100644 --- a/packages/hub-extension/tsconfig.json +++ b/packages/hub-extension/tsconfig.json @@ -4,5 +4,19 @@ "outDir": "lib", "rootDir": "src" }, - "include": ["src/*"] + "include": ["src/*"], + "references": [ + { + "path": "../application" + }, + { + "path": "../apputils" + }, + { + "path": "../coreutils" + }, + { + "path": "../mainmenu" + } + ] } From 2ed5f9a6addce767ed34eaea649119030bf7cc89 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Mon, 3 Jun 2019 10:53:36 -0500 Subject: [PATCH 03/26] integry --- packages/hub-extension/package.json | 10 +++++----- packages/metapackage/package.json | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/hub-extension/package.json b/packages/hub-extension/package.json index 7f70b4a860a4..28bba4fe9731 100644 --- a/packages/hub-extension/package.json +++ b/packages/hub-extension/package.json @@ -29,11 +29,11 @@ "watch": "tsc -w --listEmittedFiles" }, "dependencies": { - "@jupyterlab/application": "^1.0.0-alpha.8", - "@jupyterlab/apputils": "^1.0.0-alpha.8", - "@jupyterlab/coreutils": "^3.0.0-alpha.8", - "@jupyterlab/mainmenu": "^1.0.0-alpha.8", - "@phosphor/widgets": "^1.6.0" + "@jupyterlab/application": "^1.0.0-alpha.9", + "@jupyterlab/apputils": "^1.0.0-alpha.9", + "@jupyterlab/coreutils": "^3.0.0-alpha.9", + "@jupyterlab/mainmenu": "^1.0.0-alpha.9", + "@phosphor/widgets": "^1.7.0" }, "devDependencies": { "rimraf": "~2.6.2", diff --git a/packages/metapackage/package.json b/packages/metapackage/package.json index ae445355d679..da65f5c01863 100644 --- a/packages/metapackage/package.json +++ b/packages/metapackage/package.json @@ -62,6 +62,7 @@ "@jupyterlab/help-extension": "^1.0.0-alpha.9", "@jupyterlab/htmlviewer": "^1.0.0-alpha.10", "@jupyterlab/htmlviewer-extension": "^1.0.0-alpha.10", + "@jupyterlab/hub-extension": "^1.0.0-alpha.8", "@jupyterlab/imageviewer": "^1.0.0-alpha.9", "@jupyterlab/imageviewer-extension": "^1.0.0-alpha.9", "@jupyterlab/inspector": "^1.0.0-alpha.9", From 2f0d850f7ec1dcc47744c72af08fe1472224e236 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Mon, 3 Jun 2019 10:55:00 -0500 Subject: [PATCH 04/26] integrity --- packages/metapackage/package.json | 77 ------------------------------- 1 file changed, 77 deletions(-) diff --git a/packages/metapackage/package.json b/packages/metapackage/package.json index da65f5c01863..7a2b2e1dda97 100644 --- a/packages/metapackage/package.json +++ b/packages/metapackage/package.json @@ -30,7 +30,6 @@ "watch": "tsc -b --watch" }, "dependencies": { -<<<<<<< HEAD "@jupyterlab/application": "^1.0.0-alpha.9", "@jupyterlab/application-extension": "^1.0.0-alpha.9", "@jupyterlab/apputils": "^1.0.0-alpha.9", @@ -105,82 +104,6 @@ "@jupyterlab/vdom": "^1.0.0-alpha.9", "@jupyterlab/vdom-extension": "^1.0.0-alpha.9", "@jupyterlab/vega5-extension": "^1.0.0-alpha.9" -======= - "@jupyterlab/application": "^1.0.0-alpha.8", - "@jupyterlab/application-extension": "^1.0.0-alpha.8", - "@jupyterlab/apputils": "^1.0.0-alpha.8", - "@jupyterlab/apputils-extension": "^1.0.0-alpha.8", - "@jupyterlab/attachments": "^1.0.0-alpha.8", - "@jupyterlab/cells": "^1.0.0-alpha.9", - "@jupyterlab/codeeditor": "^1.0.0-alpha.8", - "@jupyterlab/codemirror": "^1.0.0-alpha.8", - "@jupyterlab/codemirror-extension": "^1.0.0-alpha.8", - "@jupyterlab/completer": "^1.0.0-alpha.8", - "@jupyterlab/completer-extension": "^1.0.0-alpha.8", - "@jupyterlab/console": "^1.0.0-alpha.8", - "@jupyterlab/console-extension": "^1.0.0-alpha.8", - "@jupyterlab/coreutils": "^3.0.0-alpha.8", - "@jupyterlab/csvviewer": "^1.0.0-alpha.8", - "@jupyterlab/csvviewer-extension": "^1.0.0-alpha.9", - "@jupyterlab/docmanager": "^1.0.0-alpha.8", - "@jupyterlab/docmanager-extension": "^1.0.0-alpha.8", - "@jupyterlab/docregistry": "^1.0.0-alpha.8", - "@jupyterlab/documentsearch": "^1.0.0-alpha.9", - "@jupyterlab/documentsearch-extension": "^1.0.0-alpha.10", - "@jupyterlab/extensionmanager": "^1.0.0-alpha.8", - "@jupyterlab/extensionmanager-extension": "^1.0.0-alpha.8", - "@jupyterlab/faq-extension": "^1.0.0-alpha.8", - "@jupyterlab/filebrowser": "^1.0.0-alpha.8", - "@jupyterlab/filebrowser-extension": "^1.0.0-alpha.8", - "@jupyterlab/fileeditor": "^1.0.0-alpha.8", - "@jupyterlab/fileeditor-extension": "^1.0.0-alpha.8", - "@jupyterlab/help-extension": "^1.0.0-alpha.8", - "@jupyterlab/htmlviewer": "^1.0.0-alpha.9", - "@jupyterlab/htmlviewer-extension": "^1.0.0-alpha.9", - "@jupyterlab/hub-extension": "^1.0.0-alpha.8", - "@jupyterlab/imageviewer": "^1.0.0-alpha.8", - "@jupyterlab/imageviewer-extension": "^1.0.0-alpha.8", - "@jupyterlab/inspector": "^1.0.0-alpha.8", - "@jupyterlab/inspector-extension": "^1.0.0-alpha.8", - "@jupyterlab/javascript-extension": "^1.0.0-alpha.8", - "@jupyterlab/json-extension": "^1.0.0-alpha.8", - "@jupyterlab/launcher": "^1.0.0-alpha.8", - "@jupyterlab/launcher-extension": "^1.0.0-alpha.8", - "@jupyterlab/mainmenu": "^1.0.0-alpha.8", - "@jupyterlab/mainmenu-extension": "^1.0.0-alpha.8", - "@jupyterlab/markdownviewer": "^1.0.0-alpha.8", - "@jupyterlab/markdownviewer-extension": "^1.0.0-alpha.8", - "@jupyterlab/mathjax2": "^1.0.0-alpha.8", - "@jupyterlab/mathjax2-extension": "^1.0.0-alpha.8", - "@jupyterlab/nbconvert-css": "^0.1.1-alpha.1", - "@jupyterlab/notebook": "^1.0.0-alpha.9", - "@jupyterlab/notebook-extension": "^1.0.0-alpha.8", - "@jupyterlab/observables": "^2.2.0-alpha.8", - "@jupyterlab/outputarea": "^1.0.0-alpha.8", - "@jupyterlab/pdf-extension": "^1.0.0-alpha.8", - "@jupyterlab/rendermime": "^1.0.0-alpha.8", - "@jupyterlab/rendermime-extension": "^1.0.0-alpha.8", - "@jupyterlab/rendermime-interfaces": "^1.3.0-alpha.8", - "@jupyterlab/running": "^1.0.0-alpha.8", - "@jupyterlab/running-extension": "^1.0.0-alpha.8", - "@jupyterlab/services": "^4.0.0-alpha.8", - "@jupyterlab/settingeditor": "^1.0.0-alpha.8", - "@jupyterlab/settingeditor-extension": "^1.0.0-alpha.8", - "@jupyterlab/shortcuts-extension": "^1.0.0-alpha.8", - "@jupyterlab/statusbar": "^1.0.0-alpha.8", - "@jupyterlab/statusbar-extension": "^1.0.0-alpha.8", - "@jupyterlab/tabmanager-extension": "^1.0.0-alpha.8", - "@jupyterlab/terminal": "^1.0.0-alpha.8", - "@jupyterlab/terminal-extension": "^1.0.0-alpha.8", - "@jupyterlab/theme-dark-extension": "^1.0.0-alpha.9", - "@jupyterlab/theme-light-extension": "^1.0.0-alpha.9", - "@jupyterlab/tooltip": "^1.0.0-alpha.8", - "@jupyterlab/tooltip-extension": "^1.0.0-alpha.8", - "@jupyterlab/ui-components": "^1.0.0-alpha.8", - "@jupyterlab/vdom": "^1.0.0-alpha.8", - "@jupyterlab/vdom-extension": "^1.0.0-alpha.8", - "@jupyterlab/vega5-extension": "^1.0.0-alpha.8" ->>>>>>> 107ef3b30... wip hub integration }, "devDependencies": { "fs-extra": "^8.0.1", From 0f74dcb70cdeb47283e287472608006111ff5e6b Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Fri, 7 Jun 2019 06:02:13 -0500 Subject: [PATCH 05/26] clean up hub_prefix handling --- jupyterlab/extension.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/jupyterlab/extension.py b/jupyterlab/extension.py index 3cba00b3c9f9..648040d1a4f2 100644 --- a/jupyterlab/extension.py +++ b/jupyterlab/extension.py @@ -206,11 +206,14 @@ def load_jupyter_server_extension(nbapp): # If running under JupyterHub, add more metadata. if hasattr(nbapp, 'hub_prefix'): - settings['page_config_data']['hub_prefix'] = nbapp.hub_prefix - settings['page_config_data']['hub_host'] = nbapp.hub_host - settings['page_config_data']['hub_user'] = nbapp.user + logger.error('******\nhey, got a prefix\n\n') + page_config['hub_prefix'] = nbapp.hub_prefix + page_config['hub_host'] = nbapp.hub_host + page_config['hub_user'] = nbapp.user api_token = os.getenv('JUPYTERHUB_API_TOKEN', '') - settings['page_config_data']['token'] = api_token + page_config['token'] = api_token + else: + logger.error('*****\nhey, no prefix!\n\n') # Add the root handlers if we have not errored. if not errored: From 6de33106509decd0ec39d2b8004ee95252687590 Mon Sep 17 00:00:00 2001 From: Ian Rose Date: Thu, 6 Jun 2019 16:10:34 -0700 Subject: [PATCH 06/26] Explicitly type dummy promise as void. --- packages/application/src/frontend.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/application/src/frontend.ts b/packages/application/src/frontend.ts index 684711c2854d..7e80c7fc61ce 100644 --- a/packages/application/src/frontend.ts +++ b/packages/application/src/frontend.ts @@ -41,9 +41,9 @@ export abstract class JupyterFrontEnd< super(options); // The default restored promise if one does not exist in the options. - const restored = new Promise(resolve => { + const restored = new Promise(resolve => { requestAnimationFrame(() => { - resolve(); + resolve(void 0); }); }); From 610c1a1975305ab9f9bedfd64cd792f8e9c9aaae Mon Sep 17 00:00:00 2001 From: Ian Rose Date: Thu, 6 Jun 2019 16:18:56 -0700 Subject: [PATCH 07/26] Use JupyterFrontEnd.IPaths. --- jupyterlab/extension.py | 7 +++---- packages/application/src/frontend.ts | 2 ++ packages/application/src/lab.ts | 4 +++- packages/hub-extension/src/index.ts | 11 ++++++----- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/jupyterlab/extension.py b/jupyterlab/extension.py index 648040d1a4f2..dc010ca8c83a 100644 --- a/jupyterlab/extension.py +++ b/jupyterlab/extension.py @@ -206,10 +206,9 @@ def load_jupyter_server_extension(nbapp): # If running under JupyterHub, add more metadata. if hasattr(nbapp, 'hub_prefix'): - logger.error('******\nhey, got a prefix\n\n') - page_config['hub_prefix'] = nbapp.hub_prefix - page_config['hub_host'] = nbapp.hub_host - page_config['hub_user'] = nbapp.user + settings['page_config_data']['hubPrefix'] = nbapp.hub_prefix + settings['page_config_data']['hubHost'] = nbapp.hub_host + settings['page_config_data']['hubUser'] = nbapp.user api_token = os.getenv('JUPYTERHUB_API_TOKEN', '') page_config['token'] = api_token else: diff --git a/packages/application/src/frontend.ts b/packages/application/src/frontend.ts index 7e80c7fc61ce..8b14400f1da4 100644 --- a/packages/application/src/frontend.ts +++ b/packages/application/src/frontend.ts @@ -272,6 +272,8 @@ export namespace JupyterFrontEnd { readonly themes: string; readonly tree: string; readonly workspaces: string; + readonly hubPrefix?: string; + readonly hubHost?: string; }; /** diff --git a/packages/application/src/lab.ts b/packages/application/src/lab.ts index f92c5f0cf7ae..50f5423a0619 100644 --- a/packages/application/src/lab.ts +++ b/packages/application/src/lab.ts @@ -244,7 +244,9 @@ export namespace JupyterLab { settings: PageConfig.getOption('settingsUrl'), themes: PageConfig.getOption('themesUrl'), tree: PageConfig.getOption('treeUrl'), - workspaces: PageConfig.getOption('workspacesUrl') + workspaces: PageConfig.getOption('workspacesUrl'), + hubHost: PageConfig.getOption('hubHost'), + hubPrefix: PageConfig.getOption('hubPrefix') }, directories: { appSettings: PageConfig.getOption('appSettingsDir'), diff --git a/packages/hub-extension/src/index.ts b/packages/hub-extension/src/index.ts index 67358160dab3..f11a1242bc74 100644 --- a/packages/hub-extension/src/index.ts +++ b/packages/hub-extension/src/index.ts @@ -12,7 +12,7 @@ import { JupyterFrontEndPlugin } from '@jupyterlab/application'; -import { PageConfig, URLExt } from '@jupyterlab/coreutils'; +import { URLExt } from '@jupyterlab/coreutils'; import { IMainMenu } from '@jupyterlab/mainmenu'; @@ -30,12 +30,13 @@ export namespace CommandIDs { */ function activateHubExtension( app: JupyterFrontEnd, + paths: JupyterFrontEnd.IPaths, palette: ICommandPalette, mainMenu: IMainMenu ): void { - const hubHost = PageConfig.getOption('hub_host'); - const hubPrefix = PageConfig.getOption('hub_prefix'); - const baseUrl = PageConfig.getBaseUrl(); + const hubHost = paths.urls.hubHost; + const hubPrefix = paths.urls.hubPrefix; + const baseUrl = paths.urls.base; // Bail if not running on JupyterHub. if (!hubPrefix) { @@ -82,7 +83,7 @@ function activateHubExtension( const hubExtension: JupyterFrontEndPlugin = { activate: activateHubExtension, id: 'jupyter.extensions.hub-extension', - requires: [ICommandPalette, IMainMenu], + requires: [JupyterFrontEnd.IPaths, ICommandPalette, IMainMenu], autoStart: true }; From 570d6a99f356a4d69dacb7f717c29ca8e2c03308 Mon Sep 17 00:00:00 2001 From: Ian Rose Date: Thu, 6 Jun 2019 17:58:43 -0700 Subject: [PATCH 08/26] Use the right pageConfig. --- jupyterlab/extension.py | 8 +++----- packages/application/src/lab.ts | 4 ++-- packages/hub-extension/src/index.ts | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/jupyterlab/extension.py b/jupyterlab/extension.py index dc010ca8c83a..d6faf814dde8 100644 --- a/jupyterlab/extension.py +++ b/jupyterlab/extension.py @@ -206,13 +206,11 @@ def load_jupyter_server_extension(nbapp): # If running under JupyterHub, add more metadata. if hasattr(nbapp, 'hub_prefix'): - settings['page_config_data']['hubPrefix'] = nbapp.hub_prefix - settings['page_config_data']['hubHost'] = nbapp.hub_host - settings['page_config_data']['hubUser'] = nbapp.user + page_config['hubPrefix'] = nbapp.hub_prefix + page_config['hubHost'] = nbapp.hub_host + page_config['hubUser'] = nbapp.user api_token = os.getenv('JUPYTERHUB_API_TOKEN', '') page_config['token'] = api_token - else: - logger.error('*****\nhey, no prefix!\n\n') # Add the root handlers if we have not errored. if not errored: diff --git a/packages/application/src/lab.ts b/packages/application/src/lab.ts index 50f5423a0619..3530f2a36789 100644 --- a/packages/application/src/lab.ts +++ b/packages/application/src/lab.ts @@ -245,8 +245,8 @@ export namespace JupyterLab { themes: PageConfig.getOption('themesUrl'), tree: PageConfig.getOption('treeUrl'), workspaces: PageConfig.getOption('workspacesUrl'), - hubHost: PageConfig.getOption('hubHost'), - hubPrefix: PageConfig.getOption('hubPrefix') + hubHost: PageConfig.getOption('hubHost') || undefined, + hubPrefix: PageConfig.getOption('hubPrefix') || undefined }, directories: { appSettings: PageConfig.getOption('appSettingsDir'), diff --git a/packages/hub-extension/src/index.ts b/packages/hub-extension/src/index.ts index f11a1242bc74..65b25cb10141 100644 --- a/packages/hub-extension/src/index.ts +++ b/packages/hub-extension/src/index.ts @@ -39,7 +39,7 @@ function activateHubExtension( const baseUrl = paths.urls.base; // Bail if not running on JupyterHub. - if (!hubPrefix) { + if (!hubPrefix || !hubHost) { return; } From ed42fa81e1029633f6b6f426c437df0c55262922 Mon Sep 17 00:00:00 2001 From: Ian Rose Date: Thu, 6 Jun 2019 18:09:12 -0700 Subject: [PATCH 09/26] Fix LabHubApp. --- jupyterlab/labhubapp.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/jupyterlab/labhubapp.py b/jupyterlab/labhubapp.py index 4aa6b892114f..d2d28cff21d2 100644 --- a/jupyterlab/labhubapp.py +++ b/jupyterlab/labhubapp.py @@ -1,4 +1,5 @@ import os +import warnings from traitlets import default @@ -18,8 +19,9 @@ def _default_url(self): return "/lab" def init_webapp(self, *args, **kwargs): - warnings.warn( - "SingleUserLabApp is deprecated, use SingleUserNotebookApp and set " + "c.Spawner.default_url = '/lab' in jupyterhub_config.py", DeprecationWarning + warnings.warn( + "SingleUserLabApp is deprecated, use SingleUserNotebookApp and set " + \ + "c.Spawner.default_url = '/lab' in jupyterhub_config.py", DeprecationWarning ) super().init_webapp(*args, **kwargs) From 8f3c6e72f4bbd6471c572e0e18d6ef7599074443 Mon Sep 17 00:00:00 2001 From: Ian Rose Date: Thu, 6 Jun 2019 18:45:14 -0700 Subject: [PATCH 10/26] This is an extension. --- dev_mode/package.json | 3 +++ packages/hub-extension/package.json | 6 ++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/dev_mode/package.json b/dev_mode/package.json index 86b79eb815ba..f674a8dd87ab 100644 --- a/dev_mode/package.json +++ b/dev_mode/package.json @@ -34,6 +34,7 @@ "@jupyterlab/fileeditor-extension": "^1.0.0-alpha.9", "@jupyterlab/help-extension": "^1.0.0-alpha.9", "@jupyterlab/htmlviewer-extension": "^1.0.0-alpha.10", + "@jupyterlab/hub-extension": "^1.0.0-alpha.8", "@jupyterlab/imageviewer": "^1.0.0-alpha.9", "@jupyterlab/imageviewer-extension": "^1.0.0-alpha.9", "@jupyterlab/inspector-extension": "^1.0.0-alpha.9", @@ -130,6 +131,7 @@ "@jupyterlab/fileeditor-extension": "", "@jupyterlab/help-extension": "", "@jupyterlab/htmlviewer-extension": "", + "@jupyterlab/hub-extension": "", "@jupyterlab/imageviewer-extension": "", "@jupyterlab/inspector-extension": "", "@jupyterlab/launcher-extension": "", @@ -225,6 +227,7 @@ "@jupyterlab/fileeditor-extension": "../packages/fileeditor-extension", "@jupyterlab/help-extension": "../packages/help-extension", "@jupyterlab/htmlviewer-extension": "../packages/htmlviewer-extension", + "@jupyterlab/hub-extension": "../packages/hub-extension", "@jupyterlab/imageviewer-extension": "../packages/imageviewer-extension", "@jupyterlab/inspector-extension": "../packages/inspector-extension", "@jupyterlab/javascript-extension": "../packages/javascript-extension", diff --git a/packages/hub-extension/package.json b/packages/hub-extension/package.json index 28bba4fe9731..37de72515bfa 100644 --- a/packages/hub-extension/package.json +++ b/packages/hub-extension/package.json @@ -32,8 +32,7 @@ "@jupyterlab/application": "^1.0.0-alpha.9", "@jupyterlab/apputils": "^1.0.0-alpha.9", "@jupyterlab/coreutils": "^3.0.0-alpha.9", - "@jupyterlab/mainmenu": "^1.0.0-alpha.9", - "@phosphor/widgets": "^1.7.0" + "@jupyterlab/mainmenu": "^1.0.0-alpha.9" }, "devDependencies": { "rimraf": "~2.6.2", @@ -41,5 +40,8 @@ }, "publishConfig": { "access": "public" + }, + "jupyterlab": { + "extension": true } } From d99a4e3b0af66e17b5386bc8f9424ccc9cc899c3 Mon Sep 17 00:00:00 2001 From: Ian Rose Date: Thu, 6 Jun 2019 19:00:52 -0700 Subject: [PATCH 11/26] Add commands to file menu, command palette. --- packages/application/src/frontend.ts | 2 +- packages/hub-extension/src/index.ts | 31 ++++++++++++++-------------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/packages/application/src/frontend.ts b/packages/application/src/frontend.ts index 8b14400f1da4..eebf857be248 100644 --- a/packages/application/src/frontend.ts +++ b/packages/application/src/frontend.ts @@ -43,7 +43,7 @@ export abstract class JupyterFrontEnd< // The default restored promise if one does not exist in the options. const restored = new Promise(resolve => { requestAnimationFrame(() => { - resolve(void 0); + resolve(); }); }); diff --git a/packages/hub-extension/src/index.ts b/packages/hub-extension/src/index.ts index 65b25cb10141..2d48c8ee60c9 100644 --- a/packages/hub-extension/src/index.ts +++ b/packages/hub-extension/src/index.ts @@ -3,11 +3,10 @@ | Distributed under the terms of the Modified BSD License. |----------------------------------------------------------------------------*/ -import { Menu } from '@phosphor/widgets'; - import { ICommandPalette } from '@jupyterlab/apputils'; import { + IRouter, JupyterFrontEnd, JupyterFrontEndPlugin } from '@jupyterlab/application'; @@ -30,16 +29,17 @@ export namespace CommandIDs { */ function activateHubExtension( app: JupyterFrontEnd, + router: IRouter, paths: JupyterFrontEnd.IPaths, palette: ICommandPalette, mainMenu: IMainMenu ): void { - const hubHost = paths.urls.hubHost; - const hubPrefix = paths.urls.hubPrefix; + const hubHost = paths.urls.hubHost || ''; + const hubPrefix = paths.urls.hubPrefix || ''; const baseUrl = paths.urls.base; // Bail if not running on JupyterHub. - if (!hubPrefix || !hubHost) { + if (!hubPrefix) { return; } @@ -48,11 +48,10 @@ function activateHubExtension( hubPrefix: hubPrefix }); - const category = 'Hub'; const { commands } = app; commands.addCommand(CommandIDs.controlPanel, { - label: 'Control Panel', + label: 'Hub Control Panel', caption: 'Open the Hub control panel in a new browser tab', execute: () => { window.open(hubHost + URLExt.join(hubPrefix, 'home'), '_blank'); @@ -60,7 +59,7 @@ function activateHubExtension( }); commands.addCommand(CommandIDs.logout, { - label: 'Logout', + label: 'Log Out', caption: 'Log out of the Hub', execute: () => { window.location.href = hubHost + URLExt.join(baseUrl, 'logout'); @@ -68,13 +67,13 @@ function activateHubExtension( }); // Add commands and menu itmes. - let menu = new Menu({ commands }); - menu.title.label = category; - [CommandIDs.controlPanel, CommandIDs.logout].forEach(command => { - palette.addItem({ command, category }); - menu.addItem({ command }); - }); - mainMenu.addMenu(menu, { rank: 100 }); + mainMenu.fileMenu.addGroup( + [{ command: CommandIDs.controlPanel }, { command: CommandIDs.logout }], + 100 + ); + const category = 'Hub'; + palette.addItem({ category, command: CommandIDs.controlPanel }); + palette.addItem({ category, command: CommandIDs.logout }); } /** @@ -83,7 +82,7 @@ function activateHubExtension( const hubExtension: JupyterFrontEndPlugin = { activate: activateHubExtension, id: 'jupyter.extensions.hub-extension', - requires: [JupyterFrontEnd.IPaths, ICommandPalette, IMainMenu], + requires: [IRouter, JupyterFrontEnd.IPaths, ICommandPalette, IMainMenu], autoStart: true }; From a33f18dd324941e4cf5106838d2adf309750d2e1 Mon Sep 17 00:00:00 2001 From: Ian Rose Date: Thu, 6 Jun 2019 19:55:26 -0700 Subject: [PATCH 12/26] If running im JupyterHub, provide a dialog that prompts the user to restart the server. --- packages/application-extension/src/index.tsx | 17 +---- packages/hub-extension/package.json | 3 +- packages/hub-extension/src/index.ts | 78 +++++++++++++++++++- packages/hub-extension/tsconfig.json | 3 + packages/services/src/kernel/manager.ts | 13 ++-- packages/services/src/manager.ts | 16 +--- packages/services/src/session/manager.ts | 13 ++-- packages/services/src/terminal/manager.ts | 13 ++-- 8 files changed, 110 insertions(+), 46 deletions(-) diff --git a/packages/application-extension/src/index.tsx b/packages/application-extension/src/index.tsx index d6377ccf79aa..e30dbda92962 100644 --- a/packages/application-extension/src/index.tsx +++ b/packages/application-extension/src/index.tsx @@ -2,7 +2,6 @@ // Distributed under the terms of the Modified BSD License. import { - ConnectionLost, IConnectionLost, ILabShell, ILabStatus, @@ -746,19 +745,6 @@ const paths: JupyterFrontEndPlugin = { provides: JupyterFrontEnd.IPaths }; -/** - * The default JupyterLab connection lost provider. This may be overridden - * to provide custom behavior when a connection to the server is lost. - */ -const connectionlost: JupyterFrontEndPlugin = { - id: '@jupyterlab/apputils-extension:connectionlost', - activate: (app: JupyterFrontEnd): IConnectionLost => { - return ConnectionLost; - }, - autoStart: true, - provides: IConnectionLost -}; - /** * Export the plugins as default. */ @@ -773,8 +759,7 @@ const plugins: JupyterFrontEndPlugin[] = [ shell, status, info, - paths, - connectionlost + paths ]; export default plugins; diff --git a/packages/hub-extension/package.json b/packages/hub-extension/package.json index 37de72515bfa..c2d80b8fddb9 100644 --- a/packages/hub-extension/package.json +++ b/packages/hub-extension/package.json @@ -32,7 +32,8 @@ "@jupyterlab/application": "^1.0.0-alpha.9", "@jupyterlab/apputils": "^1.0.0-alpha.9", "@jupyterlab/coreutils": "^3.0.0-alpha.9", - "@jupyterlab/mainmenu": "^1.0.0-alpha.9" + "@jupyterlab/mainmenu": "^1.0.0-alpha.9", + "@jupyterlab/services": "^4.0.0-alpha.9" }, "devDependencies": { "rimraf": "~2.6.2", diff --git a/packages/hub-extension/src/index.ts b/packages/hub-extension/src/index.ts index 2d48c8ee60c9..97354c4b6238 100644 --- a/packages/hub-extension/src/index.ts +++ b/packages/hub-extension/src/index.ts @@ -3,9 +3,11 @@ | Distributed under the terms of the Modified BSD License. |----------------------------------------------------------------------------*/ -import { ICommandPalette } from '@jupyterlab/apputils'; +import { Dialog, ICommandPalette, showDialog } from '@jupyterlab/apputils'; import { + ConnectionLost, + IConnectionLost, IRouter, JupyterFrontEnd, JupyterFrontEndPlugin @@ -15,6 +17,8 @@ import { URLExt } from '@jupyterlab/coreutils'; import { IMainMenu } from '@jupyterlab/mainmenu'; +import { ServerConnection, ServiceManager } from '@jupyterlab/services'; + /** * The command IDs used by the plugin. */ @@ -22,6 +26,8 @@ export namespace CommandIDs { export const controlPanel: string = 'hub:control-panel'; export const logout: string = 'hub:logout'; + + export const restart: string = 'hub:restart'; } /** @@ -50,6 +56,20 @@ function activateHubExtension( const { commands } = app; + // TODO: use /spawn/:user/:name + // but that requires jupyterhub 1.0 + // and jupyterlab to pass username, servername to PageConfig + const restartUrl = + hubHost + URLExt.join(hubPrefix, `spawn?next=${hubPrefix}home`); + + commands.addCommand(CommandIDs.restart, { + label: 'Restart Server', + caption: 'Request that the Hub restart this server', + execute: () => { + window.open(restartUrl, '_blank'); + } + }); + commands.addCommand(CommandIDs.controlPanel, { label: 'Hub Control Panel', caption: 'Open the Hub control panel in a new browser tab', @@ -86,4 +106,58 @@ const hubExtension: JupyterFrontEndPlugin = { autoStart: true }; -export default hubExtension; +/** + * The default JupyterLab connection lost provider. This may be overridden + * to provide custom behavior when a connection to the server is lost. + * + * If the application is being deployed within a JupyterHub context, + * this will provide a dialog that prompts the user to restart the server. + * Otherwise, it shows an error dialog. + */ +const connectionlost: JupyterFrontEndPlugin = { + id: '@jupyterlab/apputils-extension:connectionlost', + requires: [JupyterFrontEnd.IPaths], + activate: ( + app: JupyterFrontEnd, + paths: JupyterFrontEnd.IPaths + ): IConnectionLost => { + const hubPrefix = paths.urls.hubPrefix || ''; + const baseUrl = paths.urls.base; + + // Return the default error message if not running on JupyterHub. + if (!hubPrefix) { + return ConnectionLost; + } + + // If we are running on JupyterHub, return a dialog + // that prompts the user to restart their server. + let showingError = false; + const onConnectionLost: IConnectionLost = async ( + manager: ServiceManager.IManager, + err: ServerConnection.NetworkError + ): Promise => { + if (showingError) { + return; + } + showingError = true; + const result = await showDialog({ + title: 'Server Not Running', + body: `Your server at ${baseUrl} is not running. +Would you like to restart it?`, + buttons: [ + Dialog.okButton({ label: 'Restart' }), + Dialog.cancelButton({ label: 'Dismiss' }) + ] + }); + showingError = false; + if (result.button.accept) { + await app.commands.execute(CommandIDs.restart); + } + }; + return onConnectionLost; + }, + autoStart: true, + provides: IConnectionLost +}; + +export default [hubExtension, connectionlost] as JupyterFrontEndPlugin[]; diff --git a/packages/hub-extension/tsconfig.json b/packages/hub-extension/tsconfig.json index 17ecbea70fb2..df557cb832f7 100644 --- a/packages/hub-extension/tsconfig.json +++ b/packages/hub-extension/tsconfig.json @@ -17,6 +17,9 @@ }, { "path": "../mainmenu" + }, + { + "path": "../services" } ] } diff --git a/packages/services/src/kernel/manager.ts b/packages/services/src/kernel/manager.ts index 73230ea4c737..ec2c407a3b66 100644 --- a/packages/services/src/kernel/manager.ts +++ b/packages/services/src/kernel/manager.ts @@ -116,7 +116,7 @@ export class KernelManager implements Kernel.IManager { /** * A signal emitted when there is a connection failure. */ - get connectionFailure(): ISignal { + get connectionFailure(): ISignal { return this._connectionFailure; } @@ -280,7 +280,12 @@ export class KernelManager implements Kernel.IManager { */ protected async requestRunning(): Promise { const models = await Kernel.listRunning(this.serverSettings).catch(err => { - if (err instanceof ServerConnection.NetworkError) { + // Check for a network error, or a 503 error, which is returned + // by a JupyterHub when a server is shut down. + if ( + err instanceof ServerConnection.NetworkError || + (err.response && err.response.status === 503) + ) { this._connectionFailure.emit(err); return [] as Kernel.IModel[]; } @@ -354,9 +359,7 @@ export class KernelManager implements Kernel.IManager { private _runningChanged = new Signal(this); private _specs: Kernel.ISpecModels | null = null; private _specsChanged = new Signal(this); - private _connectionFailure = new Signal( - this - ); + private _connectionFailure = new Signal(this); } /** diff --git a/packages/services/src/manager.ts b/packages/services/src/manager.ts index 9a01362276cc..111e092313f6 100644 --- a/packages/services/src/manager.ts +++ b/packages/services/src/manager.ts @@ -78,7 +78,7 @@ export class ServiceManager implements ServiceManager.IManager { /** * A signal emitted when there is a connection failure with the kernel. */ - get connectionFailure(): ISignal { + get connectionFailure(): ISignal { return this._connectionFailure; } @@ -166,19 +166,14 @@ export class ServiceManager implements ServiceManager.IManager { return this._readyPromise; } - private _onConnectionFailure( - sender: any, - err: ServerConnection.NetworkError - ): void { + private _onConnectionFailure(sender: any, err: Error): void { this._connectionFailure.emit(err); } private _isDisposed = false; private _readyPromise: Promise; private _specsChanged = new Signal(this); - private _connectionFailure = new Signal( - this - ); + private _connectionFailure = new Signal(this); private _isReady = false; } @@ -253,10 +248,7 @@ export namespace ServiceManager { /** * A signal emitted when there is a connection failure with the server. */ - readonly connectionFailure: ISignal< - IManager, - ServerConnection.NetworkError - >; + readonly connectionFailure: ISignal; } /** diff --git a/packages/services/src/session/manager.ts b/packages/services/src/session/manager.ts index 450f9d1dbfb3..9b7436c395c6 100644 --- a/packages/services/src/session/manager.ts +++ b/packages/services/src/session/manager.ts @@ -85,7 +85,7 @@ export class SessionManager implements Session.IManager { /** * A signal emitted when there is a connection failure. */ - get connectionFailure(): ISignal { + get connectionFailure(): ISignal { return this._connectionFailure; } @@ -289,7 +289,12 @@ export class SessionManager implements Session.IManager { */ protected async requestRunning(): Promise { const models = await Session.listRunning(this.serverSettings).catch(err => { - if (err instanceof ServerConnection.NetworkError) { + // Check for a network error, or a 503 error, which is returned + // by a JupyterHub when a server is shut down. + if ( + err instanceof ServerConnection.NetworkError || + (err.response && err.response.status === 503) + ) { this._connectionFailure.emit(err); return [] as Session.IModel[]; } @@ -380,9 +385,7 @@ export class SessionManager implements Session.IManager { private _pollSpecs: Poll; private _ready: Promise; private _runningChanged = new Signal(this); - private _connectionFailure = new Signal( - this - ); + private _connectionFailure = new Signal(this); private _sessions = new Set(); private _specs: Kernel.ISpecModels | null = null; private _specsChanged = new Signal(this); diff --git a/packages/services/src/terminal/manager.ts b/packages/services/src/terminal/manager.ts index 84b9df779428..f04cace1ef45 100644 --- a/packages/services/src/terminal/manager.ts +++ b/packages/services/src/terminal/manager.ts @@ -69,7 +69,7 @@ export class TerminalManager implements TerminalSession.IManager { /** * A signal emitted when there is a connection failure. */ - get connectionFailure(): ISignal { + get connectionFailure(): ISignal { return this._connectionFailure; } @@ -246,7 +246,12 @@ export class TerminalManager implements TerminalSession.IManager { protected async requestRunning(): Promise { const models = await TerminalSession.listRunning(this.serverSettings).catch( err => { - if (err instanceof ServerConnection.NetworkError) { + // Check for a network error, or a 503 error, which is returned + // by a JupyterHub when a server is shut down. + if ( + err instanceof ServerConnection.NetworkError || + (err.response && err.response.status === 503) + ) { this._connectionFailure.emit(err); return [] as TerminalSession.IModel[]; } @@ -325,9 +330,7 @@ export class TerminalManager implements TerminalSession.IManager { private _sessions = new Set(); private _ready: Promise; private _runningChanged = new Signal(this); - private _connectionFailure = new Signal( - this - ); + private _connectionFailure = new Signal(this); } /** From fbdaeff6f01ffaf0ac4f9a0d0d962a19c2865b32 Mon Sep 17 00:00:00 2001 From: Ian Rose Date: Sat, 8 Jun 2019 09:11:46 -0700 Subject: [PATCH 13/26] Add docstring documenting the intended use of LabHubApp. --- jupyterlab/labhubapp.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/jupyterlab/labhubapp.py b/jupyterlab/labhubapp.py index d2d28cff21d2..32db626628ea 100644 --- a/jupyterlab/labhubapp.py +++ b/jupyterlab/labhubapp.py @@ -1,5 +1,4 @@ import os -import warnings from traitlets import default @@ -12,17 +11,25 @@ raise ImportError('You must have jupyterhub installed for this to work.') else: class SingleUserLabApp(SingleUserNotebookApp, LabApp): - + """ + A sublcass of JupyterHub's SingleUserNotebookApp which includes LabApp + as a mixin. This makes the LabApp configurables available to the spawned + jupyter server. + + If you don't need to change any of the configurables from their default + values, then this class is not necessary, and you can deploy JupyterLab + by ensuring that its server extension is enabled and setting the + `Spawner.default_url` to '/lab'. + + If you do need to configure JupyterLab, then use this application by + setting `Spawner.cmd = ['jupyter-labhub']`. + """ @default("default_url") def _default_url(self): """when using jupyter-labhub, jupyterlab is default ui""" return "/lab" def init_webapp(self, *args, **kwargs): - warnings.warn( - "SingleUserLabApp is deprecated, use SingleUserNotebookApp and set " + \ - "c.Spawner.default_url = '/lab' in jupyterhub_config.py", DeprecationWarning - ) super().init_webapp(*args, **kwargs) From a9cc9d2ee5037a23c85c7731a78227bc54b73124 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Sat, 1 Jun 2019 04:30:28 -0500 Subject: [PATCH 14/26] wip hub integration --- jupyterlab/extension.py | 8 +++ jupyterlab/labhubapp.py | 18 +----- packages/hub-extension/README.md | 7 +++ packages/hub-extension/package.json | 38 ++++++++++++ packages/hub-extension/src/index.ts | 89 ++++++++++++++++++++++++++++ packages/hub-extension/tsconfig.json | 8 +++ packages/metapackage/package.json | 77 ++++++++++++++++++++++++ packages/metapackage/tsconfig.json | 3 + 8 files changed, 233 insertions(+), 15 deletions(-) create mode 100644 packages/hub-extension/README.md create mode 100644 packages/hub-extension/package.json create mode 100644 packages/hub-extension/src/index.ts create mode 100644 packages/hub-extension/tsconfig.json diff --git a/jupyterlab/extension.py b/jupyterlab/extension.py index 6044127f84d5..3cba00b3c9f9 100644 --- a/jupyterlab/extension.py +++ b/jupyterlab/extension.py @@ -204,6 +204,14 @@ def load_jupyter_server_extension(nbapp): # Must add before the root server handlers to avoid shadowing. web_app.add_handlers('.*$', handlers) + # If running under JupyterHub, add more metadata. + if hasattr(nbapp, 'hub_prefix'): + settings['page_config_data']['hub_prefix'] = nbapp.hub_prefix + settings['page_config_data']['hub_host'] = nbapp.hub_host + settings['page_config_data']['hub_user'] = nbapp.user + api_token = os.getenv('JUPYTERHUB_API_TOKEN', '') + settings['page_config_data']['token'] = api_token + # Add the root handlers if we have not errored. if not errored: add_handlers(web_app, config) diff --git a/jupyterlab/labhubapp.py b/jupyterlab/labhubapp.py index 7c039d1ced28..4aa6b892114f 100644 --- a/jupyterlab/labhubapp.py +++ b/jupyterlab/labhubapp.py @@ -18,22 +18,10 @@ def _default_url(self): return "/lab" def init_webapp(self, *args, **kwargs): + warnings.warn( + "SingleUserLabApp is deprecated, use SingleUserNotebookApp and set " + "c.Spawner.default_url = '/lab' in jupyterhub_config.py", DeprecationWarning + ) super().init_webapp(*args, **kwargs) - settings = self.web_app.settings - if 'page_config_data' not in settings: - settings['page_config_data'] = {} - settings['page_config_data']['hub_prefix'] = self.hub_prefix - settings['page_config_data']['hub_host'] = self.hub_host - settings['page_config_data']['hub_user'] = self.user - api_token = os.getenv('JUPYTERHUB_API_TOKEN') - if not api_token: - api_token = '' - if not self.token: - try: - self.token = api_token - except AttributeError: - self.log.error("Can't set self.token") - settings['page_config_data']['token'] = api_token def main(argv=None): diff --git a/packages/hub-extension/README.md b/packages/hub-extension/README.md new file mode 100644 index 000000000000..ccfef2468120 --- /dev/null +++ b/packages/hub-extension/README.md @@ -0,0 +1,7 @@ +# @jupyterlab/hub-extension + +JupyterLab](https://github.com/jupyterlab/jupyterlab) integration for +[JupyterHub](https://github.com/jupyterhub/jupyterhub). + +This adds a "Hub" menu to JupyterLab that allows a user to log out of JupyterHub +or access their JupyterHub control panel. diff --git a/packages/hub-extension/package.json b/packages/hub-extension/package.json new file mode 100644 index 000000000000..fbd56872cb6f --- /dev/null +++ b/packages/hub-extension/package.json @@ -0,0 +1,38 @@ +{ + "name": "@jupyterlab/hub-extension", + "version": "1.0.0-alpha.8", + "description": "JupyterLab integration for JupyterHub", + "homepage": "https://github.com/jupyterlab/jupyterlab", + "bugs": { + "url": "https://github.com/jupyterlab/jupyterlab/issues" + }, + "repository": { + "type": "git", + "url": "https://github.com/jupyterlab/jupyterlab.git" + }, + "license": "BSD-3-Clause", + "author": "Project Jupyter", + "files": [ + "lib/**/*.{d.ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}", + "schema/*.json", + "style/**/*.{css,eot,gif,html,jpg,json,png,svg,woff2,ttf}" + ], + "main": "lib/index.js", + "types": "lib/index.d.ts", + "directories": { + "lib": "lib/" + }, + "scripts": { + "build": "tsc", + "clean": "rimraf lib", + "prepublishOnly": "npm run build", + "watch": "tsc -w --listEmittedFiles" + }, + "devDependencies": { + "rimraf": "~2.6.2", + "typescript": "~3.5.1" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/packages/hub-extension/src/index.ts b/packages/hub-extension/src/index.ts new file mode 100644 index 000000000000..67358160dab3 --- /dev/null +++ b/packages/hub-extension/src/index.ts @@ -0,0 +1,89 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) Jupyter Development Team. +| Distributed under the terms of the Modified BSD License. +|----------------------------------------------------------------------------*/ + +import { Menu } from '@phosphor/widgets'; + +import { ICommandPalette } from '@jupyterlab/apputils'; + +import { + JupyterFrontEnd, + JupyterFrontEndPlugin +} from '@jupyterlab/application'; + +import { PageConfig, URLExt } from '@jupyterlab/coreutils'; + +import { IMainMenu } from '@jupyterlab/mainmenu'; + +/** + * The command IDs used by the plugin. + */ +export namespace CommandIDs { + export const controlPanel: string = 'hub:control-panel'; + + export const logout: string = 'hub:logout'; +} + +/** + * Activate the jupyterhub extension. + */ +function activateHubExtension( + app: JupyterFrontEnd, + palette: ICommandPalette, + mainMenu: IMainMenu +): void { + const hubHost = PageConfig.getOption('hub_host'); + const hubPrefix = PageConfig.getOption('hub_prefix'); + const baseUrl = PageConfig.getBaseUrl(); + + // Bail if not running on JupyterHub. + if (!hubPrefix) { + return; + } + + console.log('hub-extension: Found configuration ', { + hubHost: hubHost, + hubPrefix: hubPrefix + }); + + const category = 'Hub'; + const { commands } = app; + + commands.addCommand(CommandIDs.controlPanel, { + label: 'Control Panel', + caption: 'Open the Hub control panel in a new browser tab', + execute: () => { + window.open(hubHost + URLExt.join(hubPrefix, 'home'), '_blank'); + } + }); + + commands.addCommand(CommandIDs.logout, { + label: 'Logout', + caption: 'Log out of the Hub', + execute: () => { + window.location.href = hubHost + URLExt.join(baseUrl, 'logout'); + } + }); + + // Add commands and menu itmes. + let menu = new Menu({ commands }); + menu.title.label = category; + [CommandIDs.controlPanel, CommandIDs.logout].forEach(command => { + palette.addItem({ command, category }); + menu.addItem({ command }); + }); + mainMenu.addMenu(menu, { rank: 100 }); +} + +/** + * Initialization data for the hub-extension. + */ +const hubExtension: JupyterFrontEndPlugin = { + activate: activateHubExtension, + id: 'jupyter.extensions.hub-extension', + requires: [ICommandPalette, IMainMenu], + autoStart: true +}; + +export default hubExtension; diff --git a/packages/hub-extension/tsconfig.json b/packages/hub-extension/tsconfig.json new file mode 100644 index 000000000000..a1e4ba13c4bc --- /dev/null +++ b/packages/hub-extension/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfigbase", + "compilerOptions": { + "outDir": "lib", + "rootDir": "src" + }, + "include": ["src/*"] +} diff --git a/packages/metapackage/package.json b/packages/metapackage/package.json index 35648eea1cf1..ae445355d679 100644 --- a/packages/metapackage/package.json +++ b/packages/metapackage/package.json @@ -30,6 +30,7 @@ "watch": "tsc -b --watch" }, "dependencies": { +<<<<<<< HEAD "@jupyterlab/application": "^1.0.0-alpha.9", "@jupyterlab/application-extension": "^1.0.0-alpha.9", "@jupyterlab/apputils": "^1.0.0-alpha.9", @@ -103,6 +104,82 @@ "@jupyterlab/vdom": "^1.0.0-alpha.9", "@jupyterlab/vdom-extension": "^1.0.0-alpha.9", "@jupyterlab/vega5-extension": "^1.0.0-alpha.9" +======= + "@jupyterlab/application": "^1.0.0-alpha.8", + "@jupyterlab/application-extension": "^1.0.0-alpha.8", + "@jupyterlab/apputils": "^1.0.0-alpha.8", + "@jupyterlab/apputils-extension": "^1.0.0-alpha.8", + "@jupyterlab/attachments": "^1.0.0-alpha.8", + "@jupyterlab/cells": "^1.0.0-alpha.9", + "@jupyterlab/codeeditor": "^1.0.0-alpha.8", + "@jupyterlab/codemirror": "^1.0.0-alpha.8", + "@jupyterlab/codemirror-extension": "^1.0.0-alpha.8", + "@jupyterlab/completer": "^1.0.0-alpha.8", + "@jupyterlab/completer-extension": "^1.0.0-alpha.8", + "@jupyterlab/console": "^1.0.0-alpha.8", + "@jupyterlab/console-extension": "^1.0.0-alpha.8", + "@jupyterlab/coreutils": "^3.0.0-alpha.8", + "@jupyterlab/csvviewer": "^1.0.0-alpha.8", + "@jupyterlab/csvviewer-extension": "^1.0.0-alpha.9", + "@jupyterlab/docmanager": "^1.0.0-alpha.8", + "@jupyterlab/docmanager-extension": "^1.0.0-alpha.8", + "@jupyterlab/docregistry": "^1.0.0-alpha.8", + "@jupyterlab/documentsearch": "^1.0.0-alpha.9", + "@jupyterlab/documentsearch-extension": "^1.0.0-alpha.10", + "@jupyterlab/extensionmanager": "^1.0.0-alpha.8", + "@jupyterlab/extensionmanager-extension": "^1.0.0-alpha.8", + "@jupyterlab/faq-extension": "^1.0.0-alpha.8", + "@jupyterlab/filebrowser": "^1.0.0-alpha.8", + "@jupyterlab/filebrowser-extension": "^1.0.0-alpha.8", + "@jupyterlab/fileeditor": "^1.0.0-alpha.8", + "@jupyterlab/fileeditor-extension": "^1.0.0-alpha.8", + "@jupyterlab/help-extension": "^1.0.0-alpha.8", + "@jupyterlab/htmlviewer": "^1.0.0-alpha.9", + "@jupyterlab/htmlviewer-extension": "^1.0.0-alpha.9", + "@jupyterlab/hub-extension": "^1.0.0-alpha.8", + "@jupyterlab/imageviewer": "^1.0.0-alpha.8", + "@jupyterlab/imageviewer-extension": "^1.0.0-alpha.8", + "@jupyterlab/inspector": "^1.0.0-alpha.8", + "@jupyterlab/inspector-extension": "^1.0.0-alpha.8", + "@jupyterlab/javascript-extension": "^1.0.0-alpha.8", + "@jupyterlab/json-extension": "^1.0.0-alpha.8", + "@jupyterlab/launcher": "^1.0.0-alpha.8", + "@jupyterlab/launcher-extension": "^1.0.0-alpha.8", + "@jupyterlab/mainmenu": "^1.0.0-alpha.8", + "@jupyterlab/mainmenu-extension": "^1.0.0-alpha.8", + "@jupyterlab/markdownviewer": "^1.0.0-alpha.8", + "@jupyterlab/markdownviewer-extension": "^1.0.0-alpha.8", + "@jupyterlab/mathjax2": "^1.0.0-alpha.8", + "@jupyterlab/mathjax2-extension": "^1.0.0-alpha.8", + "@jupyterlab/nbconvert-css": "^0.1.1-alpha.1", + "@jupyterlab/notebook": "^1.0.0-alpha.9", + "@jupyterlab/notebook-extension": "^1.0.0-alpha.8", + "@jupyterlab/observables": "^2.2.0-alpha.8", + "@jupyterlab/outputarea": "^1.0.0-alpha.8", + "@jupyterlab/pdf-extension": "^1.0.0-alpha.8", + "@jupyterlab/rendermime": "^1.0.0-alpha.8", + "@jupyterlab/rendermime-extension": "^1.0.0-alpha.8", + "@jupyterlab/rendermime-interfaces": "^1.3.0-alpha.8", + "@jupyterlab/running": "^1.0.0-alpha.8", + "@jupyterlab/running-extension": "^1.0.0-alpha.8", + "@jupyterlab/services": "^4.0.0-alpha.8", + "@jupyterlab/settingeditor": "^1.0.0-alpha.8", + "@jupyterlab/settingeditor-extension": "^1.0.0-alpha.8", + "@jupyterlab/shortcuts-extension": "^1.0.0-alpha.8", + "@jupyterlab/statusbar": "^1.0.0-alpha.8", + "@jupyterlab/statusbar-extension": "^1.0.0-alpha.8", + "@jupyterlab/tabmanager-extension": "^1.0.0-alpha.8", + "@jupyterlab/terminal": "^1.0.0-alpha.8", + "@jupyterlab/terminal-extension": "^1.0.0-alpha.8", + "@jupyterlab/theme-dark-extension": "^1.0.0-alpha.9", + "@jupyterlab/theme-light-extension": "^1.0.0-alpha.9", + "@jupyterlab/tooltip": "^1.0.0-alpha.8", + "@jupyterlab/tooltip-extension": "^1.0.0-alpha.8", + "@jupyterlab/ui-components": "^1.0.0-alpha.8", + "@jupyterlab/vdom": "^1.0.0-alpha.8", + "@jupyterlab/vdom-extension": "^1.0.0-alpha.8", + "@jupyterlab/vega5-extension": "^1.0.0-alpha.8" +>>>>>>> 107ef3b30... wip hub integration }, "devDependencies": { "fs-extra": "^8.0.1", diff --git a/packages/metapackage/tsconfig.json b/packages/metapackage/tsconfig.json index d5c6ed4b3cbb..fe8d0e17a6fc 100644 --- a/packages/metapackage/tsconfig.json +++ b/packages/metapackage/tsconfig.json @@ -99,6 +99,9 @@ { "path": "../htmlviewer-extension" }, + { + "path": "../hub-extension" + }, { "path": "../imageviewer" }, From f03468359a4072a445b0c0eca4ceff81fb3c7041 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Sat, 1 Jun 2019 04:38:50 -0500 Subject: [PATCH 15/26] integrity --- packages/hub-extension/package.json | 7 +++++++ packages/hub-extension/tsconfig.json | 16 +++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/packages/hub-extension/package.json b/packages/hub-extension/package.json index fbd56872cb6f..7f70b4a860a4 100644 --- a/packages/hub-extension/package.json +++ b/packages/hub-extension/package.json @@ -28,6 +28,13 @@ "prepublishOnly": "npm run build", "watch": "tsc -w --listEmittedFiles" }, + "dependencies": { + "@jupyterlab/application": "^1.0.0-alpha.8", + "@jupyterlab/apputils": "^1.0.0-alpha.8", + "@jupyterlab/coreutils": "^3.0.0-alpha.8", + "@jupyterlab/mainmenu": "^1.0.0-alpha.8", + "@phosphor/widgets": "^1.6.0" + }, "devDependencies": { "rimraf": "~2.6.2", "typescript": "~3.5.1" diff --git a/packages/hub-extension/tsconfig.json b/packages/hub-extension/tsconfig.json index a1e4ba13c4bc..17ecbea70fb2 100644 --- a/packages/hub-extension/tsconfig.json +++ b/packages/hub-extension/tsconfig.json @@ -4,5 +4,19 @@ "outDir": "lib", "rootDir": "src" }, - "include": ["src/*"] + "include": ["src/*"], + "references": [ + { + "path": "../application" + }, + { + "path": "../apputils" + }, + { + "path": "../coreutils" + }, + { + "path": "../mainmenu" + } + ] } From b08ea7e7b2c920b2d88e2c196c23cdf40f5eff29 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Mon, 3 Jun 2019 10:53:36 -0500 Subject: [PATCH 16/26] integry --- packages/hub-extension/package.json | 10 +++++----- packages/metapackage/package.json | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/hub-extension/package.json b/packages/hub-extension/package.json index 7f70b4a860a4..28bba4fe9731 100644 --- a/packages/hub-extension/package.json +++ b/packages/hub-extension/package.json @@ -29,11 +29,11 @@ "watch": "tsc -w --listEmittedFiles" }, "dependencies": { - "@jupyterlab/application": "^1.0.0-alpha.8", - "@jupyterlab/apputils": "^1.0.0-alpha.8", - "@jupyterlab/coreutils": "^3.0.0-alpha.8", - "@jupyterlab/mainmenu": "^1.0.0-alpha.8", - "@phosphor/widgets": "^1.6.0" + "@jupyterlab/application": "^1.0.0-alpha.9", + "@jupyterlab/apputils": "^1.0.0-alpha.9", + "@jupyterlab/coreutils": "^3.0.0-alpha.9", + "@jupyterlab/mainmenu": "^1.0.0-alpha.9", + "@phosphor/widgets": "^1.7.0" }, "devDependencies": { "rimraf": "~2.6.2", diff --git a/packages/metapackage/package.json b/packages/metapackage/package.json index ae445355d679..da65f5c01863 100644 --- a/packages/metapackage/package.json +++ b/packages/metapackage/package.json @@ -62,6 +62,7 @@ "@jupyterlab/help-extension": "^1.0.0-alpha.9", "@jupyterlab/htmlviewer": "^1.0.0-alpha.10", "@jupyterlab/htmlviewer-extension": "^1.0.0-alpha.10", + "@jupyterlab/hub-extension": "^1.0.0-alpha.8", "@jupyterlab/imageviewer": "^1.0.0-alpha.9", "@jupyterlab/imageviewer-extension": "^1.0.0-alpha.9", "@jupyterlab/inspector": "^1.0.0-alpha.9", From 654a4f8aa2bd9c6e9e1398481b3c5bbd021b677d Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Mon, 3 Jun 2019 10:55:00 -0500 Subject: [PATCH 17/26] integrity --- packages/metapackage/package.json | 77 ------------------------------- 1 file changed, 77 deletions(-) diff --git a/packages/metapackage/package.json b/packages/metapackage/package.json index da65f5c01863..7a2b2e1dda97 100644 --- a/packages/metapackage/package.json +++ b/packages/metapackage/package.json @@ -30,7 +30,6 @@ "watch": "tsc -b --watch" }, "dependencies": { -<<<<<<< HEAD "@jupyterlab/application": "^1.0.0-alpha.9", "@jupyterlab/application-extension": "^1.0.0-alpha.9", "@jupyterlab/apputils": "^1.0.0-alpha.9", @@ -105,82 +104,6 @@ "@jupyterlab/vdom": "^1.0.0-alpha.9", "@jupyterlab/vdom-extension": "^1.0.0-alpha.9", "@jupyterlab/vega5-extension": "^1.0.0-alpha.9" -======= - "@jupyterlab/application": "^1.0.0-alpha.8", - "@jupyterlab/application-extension": "^1.0.0-alpha.8", - "@jupyterlab/apputils": "^1.0.0-alpha.8", - "@jupyterlab/apputils-extension": "^1.0.0-alpha.8", - "@jupyterlab/attachments": "^1.0.0-alpha.8", - "@jupyterlab/cells": "^1.0.0-alpha.9", - "@jupyterlab/codeeditor": "^1.0.0-alpha.8", - "@jupyterlab/codemirror": "^1.0.0-alpha.8", - "@jupyterlab/codemirror-extension": "^1.0.0-alpha.8", - "@jupyterlab/completer": "^1.0.0-alpha.8", - "@jupyterlab/completer-extension": "^1.0.0-alpha.8", - "@jupyterlab/console": "^1.0.0-alpha.8", - "@jupyterlab/console-extension": "^1.0.0-alpha.8", - "@jupyterlab/coreutils": "^3.0.0-alpha.8", - "@jupyterlab/csvviewer": "^1.0.0-alpha.8", - "@jupyterlab/csvviewer-extension": "^1.0.0-alpha.9", - "@jupyterlab/docmanager": "^1.0.0-alpha.8", - "@jupyterlab/docmanager-extension": "^1.0.0-alpha.8", - "@jupyterlab/docregistry": "^1.0.0-alpha.8", - "@jupyterlab/documentsearch": "^1.0.0-alpha.9", - "@jupyterlab/documentsearch-extension": "^1.0.0-alpha.10", - "@jupyterlab/extensionmanager": "^1.0.0-alpha.8", - "@jupyterlab/extensionmanager-extension": "^1.0.0-alpha.8", - "@jupyterlab/faq-extension": "^1.0.0-alpha.8", - "@jupyterlab/filebrowser": "^1.0.0-alpha.8", - "@jupyterlab/filebrowser-extension": "^1.0.0-alpha.8", - "@jupyterlab/fileeditor": "^1.0.0-alpha.8", - "@jupyterlab/fileeditor-extension": "^1.0.0-alpha.8", - "@jupyterlab/help-extension": "^1.0.0-alpha.8", - "@jupyterlab/htmlviewer": "^1.0.0-alpha.9", - "@jupyterlab/htmlviewer-extension": "^1.0.0-alpha.9", - "@jupyterlab/hub-extension": "^1.0.0-alpha.8", - "@jupyterlab/imageviewer": "^1.0.0-alpha.8", - "@jupyterlab/imageviewer-extension": "^1.0.0-alpha.8", - "@jupyterlab/inspector": "^1.0.0-alpha.8", - "@jupyterlab/inspector-extension": "^1.0.0-alpha.8", - "@jupyterlab/javascript-extension": "^1.0.0-alpha.8", - "@jupyterlab/json-extension": "^1.0.0-alpha.8", - "@jupyterlab/launcher": "^1.0.0-alpha.8", - "@jupyterlab/launcher-extension": "^1.0.0-alpha.8", - "@jupyterlab/mainmenu": "^1.0.0-alpha.8", - "@jupyterlab/mainmenu-extension": "^1.0.0-alpha.8", - "@jupyterlab/markdownviewer": "^1.0.0-alpha.8", - "@jupyterlab/markdownviewer-extension": "^1.0.0-alpha.8", - "@jupyterlab/mathjax2": "^1.0.0-alpha.8", - "@jupyterlab/mathjax2-extension": "^1.0.0-alpha.8", - "@jupyterlab/nbconvert-css": "^0.1.1-alpha.1", - "@jupyterlab/notebook": "^1.0.0-alpha.9", - "@jupyterlab/notebook-extension": "^1.0.0-alpha.8", - "@jupyterlab/observables": "^2.2.0-alpha.8", - "@jupyterlab/outputarea": "^1.0.0-alpha.8", - "@jupyterlab/pdf-extension": "^1.0.0-alpha.8", - "@jupyterlab/rendermime": "^1.0.0-alpha.8", - "@jupyterlab/rendermime-extension": "^1.0.0-alpha.8", - "@jupyterlab/rendermime-interfaces": "^1.3.0-alpha.8", - "@jupyterlab/running": "^1.0.0-alpha.8", - "@jupyterlab/running-extension": "^1.0.0-alpha.8", - "@jupyterlab/services": "^4.0.0-alpha.8", - "@jupyterlab/settingeditor": "^1.0.0-alpha.8", - "@jupyterlab/settingeditor-extension": "^1.0.0-alpha.8", - "@jupyterlab/shortcuts-extension": "^1.0.0-alpha.8", - "@jupyterlab/statusbar": "^1.0.0-alpha.8", - "@jupyterlab/statusbar-extension": "^1.0.0-alpha.8", - "@jupyterlab/tabmanager-extension": "^1.0.0-alpha.8", - "@jupyterlab/terminal": "^1.0.0-alpha.8", - "@jupyterlab/terminal-extension": "^1.0.0-alpha.8", - "@jupyterlab/theme-dark-extension": "^1.0.0-alpha.9", - "@jupyterlab/theme-light-extension": "^1.0.0-alpha.9", - "@jupyterlab/tooltip": "^1.0.0-alpha.8", - "@jupyterlab/tooltip-extension": "^1.0.0-alpha.8", - "@jupyterlab/ui-components": "^1.0.0-alpha.8", - "@jupyterlab/vdom": "^1.0.0-alpha.8", - "@jupyterlab/vdom-extension": "^1.0.0-alpha.8", - "@jupyterlab/vega5-extension": "^1.0.0-alpha.8" ->>>>>>> 107ef3b30... wip hub integration }, "devDependencies": { "fs-extra": "^8.0.1", From a699f5139a0f3991550dbd56f72225d1e2f28583 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Fri, 7 Jun 2019 06:02:13 -0500 Subject: [PATCH 18/26] clean up hub_prefix handling --- jupyterlab/extension.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/jupyterlab/extension.py b/jupyterlab/extension.py index 3cba00b3c9f9..648040d1a4f2 100644 --- a/jupyterlab/extension.py +++ b/jupyterlab/extension.py @@ -206,11 +206,14 @@ def load_jupyter_server_extension(nbapp): # If running under JupyterHub, add more metadata. if hasattr(nbapp, 'hub_prefix'): - settings['page_config_data']['hub_prefix'] = nbapp.hub_prefix - settings['page_config_data']['hub_host'] = nbapp.hub_host - settings['page_config_data']['hub_user'] = nbapp.user + logger.error('******\nhey, got a prefix\n\n') + page_config['hub_prefix'] = nbapp.hub_prefix + page_config['hub_host'] = nbapp.hub_host + page_config['hub_user'] = nbapp.user api_token = os.getenv('JUPYTERHUB_API_TOKEN', '') - settings['page_config_data']['token'] = api_token + page_config['token'] = api_token + else: + logger.error('*****\nhey, no prefix!\n\n') # Add the root handlers if we have not errored. if not errored: From 43028a1d7caabf94850d7d09a5e4645fab3fe472 Mon Sep 17 00:00:00 2001 From: Ian Rose Date: Thu, 6 Jun 2019 16:10:34 -0700 Subject: [PATCH 19/26] Explicitly type dummy promise as void. --- packages/application/src/frontend.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/application/src/frontend.ts b/packages/application/src/frontend.ts index c11a193b86df..7e80c7fc61ce 100644 --- a/packages/application/src/frontend.ts +++ b/packages/application/src/frontend.ts @@ -43,7 +43,7 @@ export abstract class JupyterFrontEnd< // The default restored promise if one does not exist in the options. const restored = new Promise(resolve => { requestAnimationFrame(() => { - resolve(); + resolve(void 0); }); }); From 3da56c735b2482f21d2ab0438e91e3afff3733ae Mon Sep 17 00:00:00 2001 From: Ian Rose Date: Thu, 6 Jun 2019 16:18:56 -0700 Subject: [PATCH 20/26] Use JupyterFrontEnd.IPaths. --- jupyterlab/extension.py | 7 +++---- packages/application/src/frontend.ts | 2 ++ packages/application/src/lab.ts | 4 +++- packages/hub-extension/src/index.ts | 11 ++++++----- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/jupyterlab/extension.py b/jupyterlab/extension.py index 648040d1a4f2..dc010ca8c83a 100644 --- a/jupyterlab/extension.py +++ b/jupyterlab/extension.py @@ -206,10 +206,9 @@ def load_jupyter_server_extension(nbapp): # If running under JupyterHub, add more metadata. if hasattr(nbapp, 'hub_prefix'): - logger.error('******\nhey, got a prefix\n\n') - page_config['hub_prefix'] = nbapp.hub_prefix - page_config['hub_host'] = nbapp.hub_host - page_config['hub_user'] = nbapp.user + settings['page_config_data']['hubPrefix'] = nbapp.hub_prefix + settings['page_config_data']['hubHost'] = nbapp.hub_host + settings['page_config_data']['hubUser'] = nbapp.user api_token = os.getenv('JUPYTERHUB_API_TOKEN', '') page_config['token'] = api_token else: diff --git a/packages/application/src/frontend.ts b/packages/application/src/frontend.ts index 7e80c7fc61ce..8b14400f1da4 100644 --- a/packages/application/src/frontend.ts +++ b/packages/application/src/frontend.ts @@ -272,6 +272,8 @@ export namespace JupyterFrontEnd { readonly themes: string; readonly tree: string; readonly workspaces: string; + readonly hubPrefix?: string; + readonly hubHost?: string; }; /** diff --git a/packages/application/src/lab.ts b/packages/application/src/lab.ts index f92c5f0cf7ae..50f5423a0619 100644 --- a/packages/application/src/lab.ts +++ b/packages/application/src/lab.ts @@ -244,7 +244,9 @@ export namespace JupyterLab { settings: PageConfig.getOption('settingsUrl'), themes: PageConfig.getOption('themesUrl'), tree: PageConfig.getOption('treeUrl'), - workspaces: PageConfig.getOption('workspacesUrl') + workspaces: PageConfig.getOption('workspacesUrl'), + hubHost: PageConfig.getOption('hubHost'), + hubPrefix: PageConfig.getOption('hubPrefix') }, directories: { appSettings: PageConfig.getOption('appSettingsDir'), diff --git a/packages/hub-extension/src/index.ts b/packages/hub-extension/src/index.ts index 67358160dab3..f11a1242bc74 100644 --- a/packages/hub-extension/src/index.ts +++ b/packages/hub-extension/src/index.ts @@ -12,7 +12,7 @@ import { JupyterFrontEndPlugin } from '@jupyterlab/application'; -import { PageConfig, URLExt } from '@jupyterlab/coreutils'; +import { URLExt } from '@jupyterlab/coreutils'; import { IMainMenu } from '@jupyterlab/mainmenu'; @@ -30,12 +30,13 @@ export namespace CommandIDs { */ function activateHubExtension( app: JupyterFrontEnd, + paths: JupyterFrontEnd.IPaths, palette: ICommandPalette, mainMenu: IMainMenu ): void { - const hubHost = PageConfig.getOption('hub_host'); - const hubPrefix = PageConfig.getOption('hub_prefix'); - const baseUrl = PageConfig.getBaseUrl(); + const hubHost = paths.urls.hubHost; + const hubPrefix = paths.urls.hubPrefix; + const baseUrl = paths.urls.base; // Bail if not running on JupyterHub. if (!hubPrefix) { @@ -82,7 +83,7 @@ function activateHubExtension( const hubExtension: JupyterFrontEndPlugin = { activate: activateHubExtension, id: 'jupyter.extensions.hub-extension', - requires: [ICommandPalette, IMainMenu], + requires: [JupyterFrontEnd.IPaths, ICommandPalette, IMainMenu], autoStart: true }; From 9f14551478b288c7c30ce79105c41fc726bf0c28 Mon Sep 17 00:00:00 2001 From: Ian Rose Date: Thu, 6 Jun 2019 17:58:43 -0700 Subject: [PATCH 21/26] Use the right pageConfig. --- jupyterlab/extension.py | 8 +++----- packages/application/src/lab.ts | 4 ++-- packages/hub-extension/src/index.ts | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/jupyterlab/extension.py b/jupyterlab/extension.py index dc010ca8c83a..d6faf814dde8 100644 --- a/jupyterlab/extension.py +++ b/jupyterlab/extension.py @@ -206,13 +206,11 @@ def load_jupyter_server_extension(nbapp): # If running under JupyterHub, add more metadata. if hasattr(nbapp, 'hub_prefix'): - settings['page_config_data']['hubPrefix'] = nbapp.hub_prefix - settings['page_config_data']['hubHost'] = nbapp.hub_host - settings['page_config_data']['hubUser'] = nbapp.user + page_config['hubPrefix'] = nbapp.hub_prefix + page_config['hubHost'] = nbapp.hub_host + page_config['hubUser'] = nbapp.user api_token = os.getenv('JUPYTERHUB_API_TOKEN', '') page_config['token'] = api_token - else: - logger.error('*****\nhey, no prefix!\n\n') # Add the root handlers if we have not errored. if not errored: diff --git a/packages/application/src/lab.ts b/packages/application/src/lab.ts index 50f5423a0619..3530f2a36789 100644 --- a/packages/application/src/lab.ts +++ b/packages/application/src/lab.ts @@ -245,8 +245,8 @@ export namespace JupyterLab { themes: PageConfig.getOption('themesUrl'), tree: PageConfig.getOption('treeUrl'), workspaces: PageConfig.getOption('workspacesUrl'), - hubHost: PageConfig.getOption('hubHost'), - hubPrefix: PageConfig.getOption('hubPrefix') + hubHost: PageConfig.getOption('hubHost') || undefined, + hubPrefix: PageConfig.getOption('hubPrefix') || undefined }, directories: { appSettings: PageConfig.getOption('appSettingsDir'), diff --git a/packages/hub-extension/src/index.ts b/packages/hub-extension/src/index.ts index f11a1242bc74..65b25cb10141 100644 --- a/packages/hub-extension/src/index.ts +++ b/packages/hub-extension/src/index.ts @@ -39,7 +39,7 @@ function activateHubExtension( const baseUrl = paths.urls.base; // Bail if not running on JupyterHub. - if (!hubPrefix) { + if (!hubPrefix || !hubHost) { return; } From 908c2a51dc0350ea472ee63076b186d28b9d6b3e Mon Sep 17 00:00:00 2001 From: Ian Rose Date: Thu, 6 Jun 2019 18:09:12 -0700 Subject: [PATCH 22/26] Fix LabHubApp. --- jupyterlab/labhubapp.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/jupyterlab/labhubapp.py b/jupyterlab/labhubapp.py index 4aa6b892114f..d2d28cff21d2 100644 --- a/jupyterlab/labhubapp.py +++ b/jupyterlab/labhubapp.py @@ -1,4 +1,5 @@ import os +import warnings from traitlets import default @@ -18,8 +19,9 @@ def _default_url(self): return "/lab" def init_webapp(self, *args, **kwargs): - warnings.warn( - "SingleUserLabApp is deprecated, use SingleUserNotebookApp and set " + "c.Spawner.default_url = '/lab' in jupyterhub_config.py", DeprecationWarning + warnings.warn( + "SingleUserLabApp is deprecated, use SingleUserNotebookApp and set " + \ + "c.Spawner.default_url = '/lab' in jupyterhub_config.py", DeprecationWarning ) super().init_webapp(*args, **kwargs) From 83ac052e0fd565e250bf5698da7b3fd367630989 Mon Sep 17 00:00:00 2001 From: Ian Rose Date: Thu, 6 Jun 2019 18:45:14 -0700 Subject: [PATCH 23/26] This is an extension. --- dev_mode/package.json | 3 +++ packages/hub-extension/package.json | 6 ++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/dev_mode/package.json b/dev_mode/package.json index 86b79eb815ba..f674a8dd87ab 100644 --- a/dev_mode/package.json +++ b/dev_mode/package.json @@ -34,6 +34,7 @@ "@jupyterlab/fileeditor-extension": "^1.0.0-alpha.9", "@jupyterlab/help-extension": "^1.0.0-alpha.9", "@jupyterlab/htmlviewer-extension": "^1.0.0-alpha.10", + "@jupyterlab/hub-extension": "^1.0.0-alpha.8", "@jupyterlab/imageviewer": "^1.0.0-alpha.9", "@jupyterlab/imageviewer-extension": "^1.0.0-alpha.9", "@jupyterlab/inspector-extension": "^1.0.0-alpha.9", @@ -130,6 +131,7 @@ "@jupyterlab/fileeditor-extension": "", "@jupyterlab/help-extension": "", "@jupyterlab/htmlviewer-extension": "", + "@jupyterlab/hub-extension": "", "@jupyterlab/imageviewer-extension": "", "@jupyterlab/inspector-extension": "", "@jupyterlab/launcher-extension": "", @@ -225,6 +227,7 @@ "@jupyterlab/fileeditor-extension": "../packages/fileeditor-extension", "@jupyterlab/help-extension": "../packages/help-extension", "@jupyterlab/htmlviewer-extension": "../packages/htmlviewer-extension", + "@jupyterlab/hub-extension": "../packages/hub-extension", "@jupyterlab/imageviewer-extension": "../packages/imageviewer-extension", "@jupyterlab/inspector-extension": "../packages/inspector-extension", "@jupyterlab/javascript-extension": "../packages/javascript-extension", diff --git a/packages/hub-extension/package.json b/packages/hub-extension/package.json index 28bba4fe9731..37de72515bfa 100644 --- a/packages/hub-extension/package.json +++ b/packages/hub-extension/package.json @@ -32,8 +32,7 @@ "@jupyterlab/application": "^1.0.0-alpha.9", "@jupyterlab/apputils": "^1.0.0-alpha.9", "@jupyterlab/coreutils": "^3.0.0-alpha.9", - "@jupyterlab/mainmenu": "^1.0.0-alpha.9", - "@phosphor/widgets": "^1.7.0" + "@jupyterlab/mainmenu": "^1.0.0-alpha.9" }, "devDependencies": { "rimraf": "~2.6.2", @@ -41,5 +40,8 @@ }, "publishConfig": { "access": "public" + }, + "jupyterlab": { + "extension": true } } From 16f274a82161fc40ab07b0494393a92cc703bf1c Mon Sep 17 00:00:00 2001 From: Ian Rose Date: Thu, 6 Jun 2019 19:00:52 -0700 Subject: [PATCH 24/26] Add commands to file menu, command palette. --- packages/application/src/frontend.ts | 2 +- packages/hub-extension/src/index.ts | 31 ++++++++++++++-------------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/packages/application/src/frontend.ts b/packages/application/src/frontend.ts index 8b14400f1da4..eebf857be248 100644 --- a/packages/application/src/frontend.ts +++ b/packages/application/src/frontend.ts @@ -43,7 +43,7 @@ export abstract class JupyterFrontEnd< // The default restored promise if one does not exist in the options. const restored = new Promise(resolve => { requestAnimationFrame(() => { - resolve(void 0); + resolve(); }); }); diff --git a/packages/hub-extension/src/index.ts b/packages/hub-extension/src/index.ts index 65b25cb10141..2d48c8ee60c9 100644 --- a/packages/hub-extension/src/index.ts +++ b/packages/hub-extension/src/index.ts @@ -3,11 +3,10 @@ | Distributed under the terms of the Modified BSD License. |----------------------------------------------------------------------------*/ -import { Menu } from '@phosphor/widgets'; - import { ICommandPalette } from '@jupyterlab/apputils'; import { + IRouter, JupyterFrontEnd, JupyterFrontEndPlugin } from '@jupyterlab/application'; @@ -30,16 +29,17 @@ export namespace CommandIDs { */ function activateHubExtension( app: JupyterFrontEnd, + router: IRouter, paths: JupyterFrontEnd.IPaths, palette: ICommandPalette, mainMenu: IMainMenu ): void { - const hubHost = paths.urls.hubHost; - const hubPrefix = paths.urls.hubPrefix; + const hubHost = paths.urls.hubHost || ''; + const hubPrefix = paths.urls.hubPrefix || ''; const baseUrl = paths.urls.base; // Bail if not running on JupyterHub. - if (!hubPrefix || !hubHost) { + if (!hubPrefix) { return; } @@ -48,11 +48,10 @@ function activateHubExtension( hubPrefix: hubPrefix }); - const category = 'Hub'; const { commands } = app; commands.addCommand(CommandIDs.controlPanel, { - label: 'Control Panel', + label: 'Hub Control Panel', caption: 'Open the Hub control panel in a new browser tab', execute: () => { window.open(hubHost + URLExt.join(hubPrefix, 'home'), '_blank'); @@ -60,7 +59,7 @@ function activateHubExtension( }); commands.addCommand(CommandIDs.logout, { - label: 'Logout', + label: 'Log Out', caption: 'Log out of the Hub', execute: () => { window.location.href = hubHost + URLExt.join(baseUrl, 'logout'); @@ -68,13 +67,13 @@ function activateHubExtension( }); // Add commands and menu itmes. - let menu = new Menu({ commands }); - menu.title.label = category; - [CommandIDs.controlPanel, CommandIDs.logout].forEach(command => { - palette.addItem({ command, category }); - menu.addItem({ command }); - }); - mainMenu.addMenu(menu, { rank: 100 }); + mainMenu.fileMenu.addGroup( + [{ command: CommandIDs.controlPanel }, { command: CommandIDs.logout }], + 100 + ); + const category = 'Hub'; + palette.addItem({ category, command: CommandIDs.controlPanel }); + palette.addItem({ category, command: CommandIDs.logout }); } /** @@ -83,7 +82,7 @@ function activateHubExtension( const hubExtension: JupyterFrontEndPlugin = { activate: activateHubExtension, id: 'jupyter.extensions.hub-extension', - requires: [JupyterFrontEnd.IPaths, ICommandPalette, IMainMenu], + requires: [IRouter, JupyterFrontEnd.IPaths, ICommandPalette, IMainMenu], autoStart: true }; From d5e1c617adf0fc168c9903f6ec161cd76b6314ec Mon Sep 17 00:00:00 2001 From: Ian Rose Date: Thu, 6 Jun 2019 19:55:26 -0700 Subject: [PATCH 25/26] If running im JupyterHub, provide a dialog that prompts the user to restart the server. --- packages/application-extension/src/index.tsx | 17 +---- packages/hub-extension/package.json | 3 +- packages/hub-extension/src/index.ts | 78 +++++++++++++++++++- packages/hub-extension/tsconfig.json | 3 + packages/services/src/kernel/manager.ts | 13 ++-- packages/services/src/manager.ts | 16 +--- packages/services/src/session/manager.ts | 13 ++-- packages/services/src/terminal/manager.ts | 13 ++-- 8 files changed, 110 insertions(+), 46 deletions(-) diff --git a/packages/application-extension/src/index.tsx b/packages/application-extension/src/index.tsx index d6377ccf79aa..e30dbda92962 100644 --- a/packages/application-extension/src/index.tsx +++ b/packages/application-extension/src/index.tsx @@ -2,7 +2,6 @@ // Distributed under the terms of the Modified BSD License. import { - ConnectionLost, IConnectionLost, ILabShell, ILabStatus, @@ -746,19 +745,6 @@ const paths: JupyterFrontEndPlugin = { provides: JupyterFrontEnd.IPaths }; -/** - * The default JupyterLab connection lost provider. This may be overridden - * to provide custom behavior when a connection to the server is lost. - */ -const connectionlost: JupyterFrontEndPlugin = { - id: '@jupyterlab/apputils-extension:connectionlost', - activate: (app: JupyterFrontEnd): IConnectionLost => { - return ConnectionLost; - }, - autoStart: true, - provides: IConnectionLost -}; - /** * Export the plugins as default. */ @@ -773,8 +759,7 @@ const plugins: JupyterFrontEndPlugin[] = [ shell, status, info, - paths, - connectionlost + paths ]; export default plugins; diff --git a/packages/hub-extension/package.json b/packages/hub-extension/package.json index 37de72515bfa..c2d80b8fddb9 100644 --- a/packages/hub-extension/package.json +++ b/packages/hub-extension/package.json @@ -32,7 +32,8 @@ "@jupyterlab/application": "^1.0.0-alpha.9", "@jupyterlab/apputils": "^1.0.0-alpha.9", "@jupyterlab/coreutils": "^3.0.0-alpha.9", - "@jupyterlab/mainmenu": "^1.0.0-alpha.9" + "@jupyterlab/mainmenu": "^1.0.0-alpha.9", + "@jupyterlab/services": "^4.0.0-alpha.9" }, "devDependencies": { "rimraf": "~2.6.2", diff --git a/packages/hub-extension/src/index.ts b/packages/hub-extension/src/index.ts index 2d48c8ee60c9..97354c4b6238 100644 --- a/packages/hub-extension/src/index.ts +++ b/packages/hub-extension/src/index.ts @@ -3,9 +3,11 @@ | Distributed under the terms of the Modified BSD License. |----------------------------------------------------------------------------*/ -import { ICommandPalette } from '@jupyterlab/apputils'; +import { Dialog, ICommandPalette, showDialog } from '@jupyterlab/apputils'; import { + ConnectionLost, + IConnectionLost, IRouter, JupyterFrontEnd, JupyterFrontEndPlugin @@ -15,6 +17,8 @@ import { URLExt } from '@jupyterlab/coreutils'; import { IMainMenu } from '@jupyterlab/mainmenu'; +import { ServerConnection, ServiceManager } from '@jupyterlab/services'; + /** * The command IDs used by the plugin. */ @@ -22,6 +26,8 @@ export namespace CommandIDs { export const controlPanel: string = 'hub:control-panel'; export const logout: string = 'hub:logout'; + + export const restart: string = 'hub:restart'; } /** @@ -50,6 +56,20 @@ function activateHubExtension( const { commands } = app; + // TODO: use /spawn/:user/:name + // but that requires jupyterhub 1.0 + // and jupyterlab to pass username, servername to PageConfig + const restartUrl = + hubHost + URLExt.join(hubPrefix, `spawn?next=${hubPrefix}home`); + + commands.addCommand(CommandIDs.restart, { + label: 'Restart Server', + caption: 'Request that the Hub restart this server', + execute: () => { + window.open(restartUrl, '_blank'); + } + }); + commands.addCommand(CommandIDs.controlPanel, { label: 'Hub Control Panel', caption: 'Open the Hub control panel in a new browser tab', @@ -86,4 +106,58 @@ const hubExtension: JupyterFrontEndPlugin = { autoStart: true }; -export default hubExtension; +/** + * The default JupyterLab connection lost provider. This may be overridden + * to provide custom behavior when a connection to the server is lost. + * + * If the application is being deployed within a JupyterHub context, + * this will provide a dialog that prompts the user to restart the server. + * Otherwise, it shows an error dialog. + */ +const connectionlost: JupyterFrontEndPlugin = { + id: '@jupyterlab/apputils-extension:connectionlost', + requires: [JupyterFrontEnd.IPaths], + activate: ( + app: JupyterFrontEnd, + paths: JupyterFrontEnd.IPaths + ): IConnectionLost => { + const hubPrefix = paths.urls.hubPrefix || ''; + const baseUrl = paths.urls.base; + + // Return the default error message if not running on JupyterHub. + if (!hubPrefix) { + return ConnectionLost; + } + + // If we are running on JupyterHub, return a dialog + // that prompts the user to restart their server. + let showingError = false; + const onConnectionLost: IConnectionLost = async ( + manager: ServiceManager.IManager, + err: ServerConnection.NetworkError + ): Promise => { + if (showingError) { + return; + } + showingError = true; + const result = await showDialog({ + title: 'Server Not Running', + body: `Your server at ${baseUrl} is not running. +Would you like to restart it?`, + buttons: [ + Dialog.okButton({ label: 'Restart' }), + Dialog.cancelButton({ label: 'Dismiss' }) + ] + }); + showingError = false; + if (result.button.accept) { + await app.commands.execute(CommandIDs.restart); + } + }; + return onConnectionLost; + }, + autoStart: true, + provides: IConnectionLost +}; + +export default [hubExtension, connectionlost] as JupyterFrontEndPlugin[]; diff --git a/packages/hub-extension/tsconfig.json b/packages/hub-extension/tsconfig.json index 17ecbea70fb2..df557cb832f7 100644 --- a/packages/hub-extension/tsconfig.json +++ b/packages/hub-extension/tsconfig.json @@ -17,6 +17,9 @@ }, { "path": "../mainmenu" + }, + { + "path": "../services" } ] } diff --git a/packages/services/src/kernel/manager.ts b/packages/services/src/kernel/manager.ts index 73230ea4c737..ec2c407a3b66 100644 --- a/packages/services/src/kernel/manager.ts +++ b/packages/services/src/kernel/manager.ts @@ -116,7 +116,7 @@ export class KernelManager implements Kernel.IManager { /** * A signal emitted when there is a connection failure. */ - get connectionFailure(): ISignal { + get connectionFailure(): ISignal { return this._connectionFailure; } @@ -280,7 +280,12 @@ export class KernelManager implements Kernel.IManager { */ protected async requestRunning(): Promise { const models = await Kernel.listRunning(this.serverSettings).catch(err => { - if (err instanceof ServerConnection.NetworkError) { + // Check for a network error, or a 503 error, which is returned + // by a JupyterHub when a server is shut down. + if ( + err instanceof ServerConnection.NetworkError || + (err.response && err.response.status === 503) + ) { this._connectionFailure.emit(err); return [] as Kernel.IModel[]; } @@ -354,9 +359,7 @@ export class KernelManager implements Kernel.IManager { private _runningChanged = new Signal(this); private _specs: Kernel.ISpecModels | null = null; private _specsChanged = new Signal(this); - private _connectionFailure = new Signal( - this - ); + private _connectionFailure = new Signal(this); } /** diff --git a/packages/services/src/manager.ts b/packages/services/src/manager.ts index 9a01362276cc..111e092313f6 100644 --- a/packages/services/src/manager.ts +++ b/packages/services/src/manager.ts @@ -78,7 +78,7 @@ export class ServiceManager implements ServiceManager.IManager { /** * A signal emitted when there is a connection failure with the kernel. */ - get connectionFailure(): ISignal { + get connectionFailure(): ISignal { return this._connectionFailure; } @@ -166,19 +166,14 @@ export class ServiceManager implements ServiceManager.IManager { return this._readyPromise; } - private _onConnectionFailure( - sender: any, - err: ServerConnection.NetworkError - ): void { + private _onConnectionFailure(sender: any, err: Error): void { this._connectionFailure.emit(err); } private _isDisposed = false; private _readyPromise: Promise; private _specsChanged = new Signal(this); - private _connectionFailure = new Signal( - this - ); + private _connectionFailure = new Signal(this); private _isReady = false; } @@ -253,10 +248,7 @@ export namespace ServiceManager { /** * A signal emitted when there is a connection failure with the server. */ - readonly connectionFailure: ISignal< - IManager, - ServerConnection.NetworkError - >; + readonly connectionFailure: ISignal; } /** diff --git a/packages/services/src/session/manager.ts b/packages/services/src/session/manager.ts index 450f9d1dbfb3..9b7436c395c6 100644 --- a/packages/services/src/session/manager.ts +++ b/packages/services/src/session/manager.ts @@ -85,7 +85,7 @@ export class SessionManager implements Session.IManager { /** * A signal emitted when there is a connection failure. */ - get connectionFailure(): ISignal { + get connectionFailure(): ISignal { return this._connectionFailure; } @@ -289,7 +289,12 @@ export class SessionManager implements Session.IManager { */ protected async requestRunning(): Promise { const models = await Session.listRunning(this.serverSettings).catch(err => { - if (err instanceof ServerConnection.NetworkError) { + // Check for a network error, or a 503 error, which is returned + // by a JupyterHub when a server is shut down. + if ( + err instanceof ServerConnection.NetworkError || + (err.response && err.response.status === 503) + ) { this._connectionFailure.emit(err); return [] as Session.IModel[]; } @@ -380,9 +385,7 @@ export class SessionManager implements Session.IManager { private _pollSpecs: Poll; private _ready: Promise; private _runningChanged = new Signal(this); - private _connectionFailure = new Signal( - this - ); + private _connectionFailure = new Signal(this); private _sessions = new Set(); private _specs: Kernel.ISpecModels | null = null; private _specsChanged = new Signal(this); diff --git a/packages/services/src/terminal/manager.ts b/packages/services/src/terminal/manager.ts index 84b9df779428..f04cace1ef45 100644 --- a/packages/services/src/terminal/manager.ts +++ b/packages/services/src/terminal/manager.ts @@ -69,7 +69,7 @@ export class TerminalManager implements TerminalSession.IManager { /** * A signal emitted when there is a connection failure. */ - get connectionFailure(): ISignal { + get connectionFailure(): ISignal { return this._connectionFailure; } @@ -246,7 +246,12 @@ export class TerminalManager implements TerminalSession.IManager { protected async requestRunning(): Promise { const models = await TerminalSession.listRunning(this.serverSettings).catch( err => { - if (err instanceof ServerConnection.NetworkError) { + // Check for a network error, or a 503 error, which is returned + // by a JupyterHub when a server is shut down. + if ( + err instanceof ServerConnection.NetworkError || + (err.response && err.response.status === 503) + ) { this._connectionFailure.emit(err); return [] as TerminalSession.IModel[]; } @@ -325,9 +330,7 @@ export class TerminalManager implements TerminalSession.IManager { private _sessions = new Set(); private _ready: Promise; private _runningChanged = new Signal(this); - private _connectionFailure = new Signal( - this - ); + private _connectionFailure = new Signal(this); } /** From 3293670973096a8a04aff69d60395f4b4a32b48b Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Sun, 9 Jun 2019 04:03:45 -0500 Subject: [PATCH 26/26] remove dependency on hub extension --- packages/application-extension/src/index.tsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/application-extension/src/index.tsx b/packages/application-extension/src/index.tsx index e30dbda92962..a0fbf64230e1 100644 --- a/packages/application-extension/src/index.tsx +++ b/packages/application-extension/src/index.tsx @@ -7,6 +7,7 @@ import { ILabStatus, ILayoutRestorer, IRouter, + ConnectionLost, JupyterFrontEnd, JupyterFrontEndPlugin, JupyterLab, @@ -74,13 +75,14 @@ namespace CommandIDs { */ const main: JupyterFrontEndPlugin = { id: '@jupyterlab/application-extension:main', - requires: [ICommandPalette, IConnectionLost, IRouter, IWindowResolver], + requires: [ICommandPalette, IRouter, IWindowResolver], + optional: [IConnectionLost], activate: ( app: JupyterFrontEnd, palette: ICommandPalette, - connectionLost: IConnectionLost, router: IRouter, - resolver: IWindowResolver + resolver: IWindowResolver, + connectionLost: IConnectionLost | undefined ) => { if (!(app instanceof JupyterLab)) { throw new Error(`${main.id} must be activated in JupyterLab.`); @@ -111,7 +113,8 @@ const main: JupyterFrontEndPlugin = { }); // If the connection to the server is lost, handle it with the - // connection lost token. + // connection lost handler. + connectionLost = connectionLost || ConnectionLost; app.serviceManager.connectionFailure.connect(connectionLost); const builder = app.serviceManager.builder;