From dc8d4c9a900b14cd94e149dccfc205bd97dd1976 Mon Sep 17 00:00:00 2001 From: mahendrapaipuri Date: Wed, 12 Apr 2023 11:35:59 +0200 Subject: [PATCH 01/25] Add server proxy manager It keeps simpervisor proc objects in internal state to be able to teminate a process by calling terminate method on proc object. It also checks the status of active processes by running a periodic callback. If a process is killed outside of jsp, it removes it from running apps list. --- jupyter_server_proxy/manager.py | 125 ++++++++++++++++++++++++++++++++ jupyter_server_proxy/utils.py | 11 +++ 2 files changed, 136 insertions(+) create mode 100644 jupyter_server_proxy/manager.py diff --git a/jupyter_server_proxy/manager.py b/jupyter_server_proxy/manager.py new file mode 100644 index 00000000..af4d10ce --- /dev/null +++ b/jupyter_server_proxy/manager.py @@ -0,0 +1,125 @@ +"""Manager for jupyter server proxy""" + +from collections import namedtuple +from tornado.ioloop import PeriodicCallback +from jupyter_server.utils import url_path_join as ujoin + +from .utils import check_pid + + +ServerProxy = namedtuple('ServerProxy', [ + 'name', 'url', 'cmd', 'port', 'managed' +]) +ServerProxyProc = namedtuple('ServerProxyProc', [ + 'name', 'proc' +]) + + +async def monitor_server_proxy_procs(): + """Perodically monitor the server proxy processes. If user terminates + the process outside of jupyter-server-proxy, we should be able to + capture that and remove proxy app from manager""" + # Get current active apps + procs = manager._list_server_proxy_procs() + + # Check if all pids are alive + for proc in procs: + exists = check_pid(proc.proc.pid) + if not exists: + await manager.del_server_proxy_app(proc.name) + + +class ServerProxyAppManager: + """ + A class for listing and stopping server proxies that are started + by jupyter server proxy. + """ + + def __init__(self): + """Initialize the server proxy manager""" + # List of server proxy apps + self.server_proxy_apps = [] + + # List of server proxy app proc objects. For internal use only + self._server_proxy_procs = [] + + # Total number of currently running proxy apps + self.num_active_server_proxy_apps = 0 + + async def add_server_proxy_app(self, name, base_url, cmd, port, proc): + """Add a launched proxy server to list""" + self.num_active_server_proxy_apps += 1 + + # Add proxy server metadata + self.server_proxy_apps.append( + ServerProxy( + name=name, + url=ujoin(base_url, name), + cmd=' '.join(cmd), + port=port, + managed=True if proc else False, + )) + + # Add proxy server proc object so that we can send SIGTERM + # when user chooses to shut it down + self._server_proxy_procs.append( + ServerProxyProc( + name=name, + proc=proc, + )) + + async def del_server_proxy_app(self, name): + """Remove a launched proxy server from list""" + self.server_proxy_apps = [app for app in self.server_proxy_apps if app.name != name] + self._server_proxy_procs = [app for app in self._server_proxy_procs if app.name != name] + self.num_active_server_proxy_apps -= 1 + + def get_server_proxy_app(self, name): + """Get a given server proxy app""" + return next((app for app in self.server_proxy_apps if app.name == name), {}) + + def _get_server_proxy_proc(self, name): + """Get a given server proxy app""" + return next((app for app in self._server_proxy_procs if app.name == name), {}) + + def list_server_proxy_apps(self): + """List all active server proxy apps""" + return self.server_proxy_apps + + def _list_server_proxy_procs(self): + """List all active server proxy proc objs""" + return self._server_proxy_procs + + async def terminate_server_proxy_app(self, name): + """Terminate a server proxy by sending SIGTERM""" + app = self._get_server_proxy_proc(name) + try: + # Here we send SIGTERM signal to terminate proxy app + # graciously so we can restart it if needed. Note that + # some servers may not get stopped by sending SIGTERM + # signal (example is mlflow server). In this case, it is + # user's responsibility to write wrapper scripts around + # proxy app's executable to terminate them cleanly using + # TERM signal. It is also important to set exit code to 0 + # when using such wrappers when proxy apps shutdown. + await app.proc.terminate() + + # Remove proxy app from list + await self.del_server_proxy_app(name) + + return True + except (KeyError, AttributeError): + return None + + async def terminate_all(self): + """Close all server proxy and cleanup""" + for app in self.server_proxy_apps: + await self.terminate_server_proxy_app(app) + + +# Create a default manager to keep track of server proxy apps. +manager = ServerProxyAppManager() + +# Create a Periodic call back function to check the status of processes +pc = PeriodicCallback(monitor_server_proxy_procs, 1e4) +pc.start() diff --git a/jupyter_server_proxy/utils.py b/jupyter_server_proxy/utils.py index e3ea7bea..133fb513 100644 --- a/jupyter_server_proxy/utils.py +++ b/jupyter_server_proxy/utils.py @@ -1,3 +1,4 @@ +import os from traitlets import TraitType @@ -49,3 +50,13 @@ def validate(self, obj, value): return value else: self.error(obj, value) + + +def check_pid(pid): + """ Check For the existence of a unix pid""" + try: + os.kill(pid, 0) + except OSError: + return False + else: + return True From 8351275c9d12a180123ea115b1783f0f0b704999 Mon Sep 17 00:00:00 2001 From: mahendrapaipuri Date: Wed, 12 Apr 2023 11:42:03 +0200 Subject: [PATCH 02/25] Add API handlers to list&del servers from manager Existing servers-info endpoint is moved with api sub directory to have a consistent end point scheme. --- jupyter_server_proxy/__init__.py | 24 +++++++++++++++---- jupyter_server_proxy/api.py | 41 ++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 4 deletions(-) diff --git a/jupyter_server_proxy/__init__.py b/jupyter_server_proxy/__init__.py index bb3d0f00..9a3dd89f 100644 --- a/jupyter_server_proxy/__init__.py +++ b/jupyter_server_proxy/__init__.py @@ -1,8 +1,12 @@ from jupyter_server.utils import url_path_join as ujoin -from .api import IconHandler, ServersInfoHandler +from .api import ( + IconHandler, ServersInfoHandler, ServersAPIHandler, ListServersAPIHandler +) from .config import ServerProxy as ServerProxyConfig -from .config import get_entrypoint_server_processes, make_handlers, make_server_process +from .config import ( + get_entrypoint_server_processes, make_handlers, make_server_process +) from .handlers import setup_handlers @@ -63,11 +67,23 @@ def _load_jupyter_server_extension(nbapp): ".*", [ ( - ujoin(base_url, "server-proxy/servers-info"), + ujoin(base_url, "api/server-proxy/servers-info"), ServersInfoHandler, {"server_processes": server_processes}, ), - (ujoin(base_url, "server-proxy/icon/(.*)"), IconHandler, {"icons": icons}), + ( + ujoin(base_url, r"server-proxy/icon/(?P.*)"), + IconHandler, + {"icons": icons} + ), + ( + ujoin(base_url, r"api/server-proxy"), + ListServersAPIHandler + ), + ( + ujoin(base_url, r"api/server-proxy/(?P.*)"), + ServersAPIHandler + ), ], ) diff --git a/jupyter_server_proxy/api.py b/jupyter_server_proxy/api.py index d183a53f..825c5a35 100644 --- a/jupyter_server_proxy/api.py +++ b/jupyter_server_proxy/api.py @@ -1,9 +1,12 @@ import mimetypes +import json from jupyter_server.base.handlers import JupyterHandler from jupyter_server.utils import url_path_join as ujoin from tornado import web +from .manager import manager + class ServersInfoHandler(JupyterHandler): def initialize(self, server_processes): @@ -70,3 +73,41 @@ async def get(self, name): with open(self.icons[name]) as f: self.write(f.read()) self.set_header("Content-Type", content_type) + + +class ServersAPIHandler(JupyterHandler): + """Handler to get metadata or terminate of a given server""" + + @web.authenticated + async def delete(self, name): + """Delete a server proxy by name""" + try: + val = await manager.terminate_server_proxy_app(name) + if val is None: + raise Exception( + f"Proxy {name} not found. Are you sure the {name} " + f"is managed by jupyter-server-proxy?" + ) + else: + self.set_status(204) + self.finish() + except Exception as e: + raise web.HTTPError(404, str(e)) + + @web.authenticated + async def get(self, name): + """Get meta data of a running server proxy""" + app = manager.get_server_proxy_app(name) + self.set_status(200) + self.finish(json.dumps(app._asdict())) + + +class ListServersAPIHandler(JupyterHandler): + """Handler to list all running server proxies""" + + @web.authenticated + async def get(self): + """list running servers""" + apps = manager.list_server_proxy_apps() + self.set_status(200) + self.finish(json.dumps([app._asdict() for app in apps])) From 34a0cba6bf8ad1427ae3e4139f90516a6fad247b Mon Sep 17 00:00:00 2001 From: mahendrapaipuri Date: Wed, 12 Apr 2023 11:44:45 +0200 Subject: [PATCH 03/25] Add proxy app to manager upon starting We also check if proxy is "running" and if it is not, we remove proc object to restart the proxy. This way proxy will be restarted only when user does it via lancher. --- jupyter_server_proxy/handlers.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/jupyter_server_proxy/handlers.py b/jupyter_server_proxy/handlers.py index a253e1b5..2a753df0 100644 --- a/jupyter_server_proxy/handlers.py +++ b/jupyter_server_proxy/handlers.py @@ -23,6 +23,7 @@ from .unixsock import UnixResolver from .utils import call_with_asked_args from .websocket import WebSocketHandlerMixin, pingable_ws_connect +from .manager import manager class RewritableResponse(HasTraits): @@ -800,9 +801,17 @@ async def ensure_process(self): # Invariant here should be: when lock isn't being held, either 'proc' is in state & # running, or not. async with self.state["proc_lock"]: + + # If the server process is terminated via Runningsessions or killed + # outside of jsp, we should be able to restart the process. If + # process is not in running stated, remove proc object and restart + # the process + if 'proc' in self.state: + if not self.state['proc'].running: + del self.state['proc'] + if "proc" not in self.state: # FIXME: Prevent races here - # FIXME: Handle graceful exits of spawned processes here # When command option isn't truthy, it means its a process not # to be managed/started by jupyter-server-proxy. This means we @@ -836,6 +845,10 @@ async def ensure_process(self): if not is_ready: await proc.kill() raise web.HTTPError(500, f"could not start {self.name} in time") + + # If process started succesfully, add it to manager + # Add the server proxy app to manager + await manager.add_server_proxy_app(self.name, self.base_url, cmd, self.port, proc) except: # Make sure we remove proc from state in any error condition del self.state["proc"] From 6b743c901e6633f11f3f4eac68e1f9fe91fe9283 Mon Sep 17 00:00:00 2001 From: mahendrapaipuri Date: Wed, 12 Apr 2023 11:55:20 +0200 Subject: [PATCH 04/25] Add manager for lab extension This manager will be added to running sessions widget so that user can view and terminate running server proxy apps. Proxy app metadata will be shown when we hover over proxy app name. --- labextension/src/index.ts | 80 +++++++++++++++- labextension/src/manager.ts | 163 ++++++++++++++++++++++++++++++++ labextension/src/restapi.ts | 58 ++++++++++++ labextension/src/serverproxy.ts | 90 ++++++++++++++++++ 4 files changed, 390 insertions(+), 1 deletion(-) create mode 100644 labextension/src/manager.ts create mode 100644 labextension/src/restapi.ts create mode 100644 labextension/src/serverproxy.ts diff --git a/labextension/src/index.ts b/labextension/src/index.ts index d90cce9a..7e679623 100644 --- a/labextension/src/index.ts +++ b/labextension/src/index.ts @@ -5,7 +5,21 @@ import { } from "@jupyterlab/application"; import { ILauncher } from "@jupyterlab/launcher"; import { PageConfig } from "@jupyterlab/coreutils"; +import { IRunningSessionManagers, IRunningSessions } from '@jupyterlab/running'; import { IFrame, MainAreaWidget, WidgetTracker } from "@jupyterlab/apputils"; +import { LabIcon } from '@jupyterlab/ui-components'; +import { ServerProxyManager } from './manager'; +import { IModel as IServerProxyModel } from './serverproxy'; +import serverProxyAppSvgstr from '../style/icons/proxy.svg'; + +export const ServerProxyAppIcon = new LabIcon({ + name: 'server-proxy:proxyAppIcon', + svgstr: serverProxyAppSvgstr +}); + +namespace CommandIDs { + export const open = 'running-server-proxy:open'; +} function newServerProxyWidget( id: string, @@ -32,6 +46,53 @@ function newServerProxyWidget( return widget; } +/** + * This function adds the active server proxy applications to running sessions + * so that user can track currently running applications via server proxy. + * User can shut down the applications as well to restart them in future + * + */ + function addRunningSessionManager( + managers: IRunningSessionManagers, + app: JupyterFrontEnd, + manager: ServerProxyManager +): void { + managers.add({ + name: 'Server Proxy Apps', + running: () => + Array.from(manager.running()).map( + model => new RunningServerProxyApp(model) + ), + shutdownAll: () => manager.shutdownAll(), + refreshRunning: () => manager.refreshRunning(), + runningChanged: manager.runningChanged, + shutdownAllConfirmationText: 'Are you sure you want to close all server proxy applications?' + }); + + class RunningServerProxyApp implements IRunningSessions.IRunningItem { + constructor(model: IServerProxyModel) { + this._model = model; + } + open(): void { + app.commands.execute(CommandIDs.open, { sp: this._model }); + } + icon(): LabIcon { + return ServerProxyAppIcon; + } + label(): string { + return `${this._model.name}`; + } + labelTitle(): string { + return `cmd: ${this._model.cmd}\nport: ${this._model.port}\nmanaged: ${this._model.managed}`; + } + shutdown(): Promise { + return manager.shutdown(this._model.name); + } + + private _model: IServerProxyModel; + } +} + /** * The activate function is registered to be called on activation of the * jupyterlab extension. @@ -42,10 +103,11 @@ async function activate( app: JupyterFrontEnd, launcher: ILauncher, restorer: ILayoutRestorer, + sessions: IRunningSessionManagers | null ): Promise { // Fetch configured server processes from {base_url}/server-proxy/servers-info const response = await fetch( - PageConfig.getBaseUrl() + "server-proxy/servers-info", + PageConfig.getBaseUrl() + "api/server-proxy/servers-info", ); if (!response.ok) { console.log( @@ -76,6 +138,13 @@ async function activate( } const { commands, shell } = app; + + // Add server proxy session manager to running sessions + if (sessions) { + let manager = new ServerProxyManager(); + addRunningSessionManager(sessions, app, manager); + } + commands.addCommand(command, { label: (args) => args["title"] as string, execute: (args) => { @@ -105,6 +174,15 @@ async function activate( }, }); + commands.addCommand(CommandIDs.open, { + execute: args => { + const model = args['sp'] as IServerProxyModel; + const url = PageConfig.getBaseUrl() + model.url; + window.open(url, '_blank'); + return; + } + }); + for (let server_process of data.server_processes) { if (!server_process.launcher_entry.enabled) { continue; diff --git a/labextension/src/manager.ts b/labextension/src/manager.ts new file mode 100644 index 00000000..57338393 --- /dev/null +++ b/labextension/src/manager.ts @@ -0,0 +1,163 @@ +import { Signal, ISignal } from '@lumino/signaling'; +import { ServerConnection } from '@jupyterlab/services'; +import { listRunning, shutdown } from './restapi'; +import * as ServerProxyApp from './serverproxy'; + +/** + * A server proxy manager. + */ +export class ServerProxyManager implements ServerProxyApp.IManager { + /** + * Construct a new server proxy manager. + */ + constructor(options: ServerProxyManager.IOptions = {}) { + this.serverSettings = options.serverSettings || ServerConnection.makeSettings(); + this._refreshTimer = (setInterval as any)(() => { + if (typeof document !== 'undefined' && document.hidden) { + return; + } + this._refreshRunning(); + }, 10000); + } + + /** + * The server settings of the manager. + */ + readonly serverSettings: ServerConnection.ISettings; + + /** + * A signal emitted when the running server proxies change. + */ + get runningChanged(): ISignal { + return this._runningChanged; + } + + /** + * A signal emitted when there is a connection failure. + */ + get connectionFailure(): ISignal { + return this._connectionFailure; + } + + /** + * Test whether the delegate has been disposed. + */ + get isDisposed(): boolean { + return this._isDisposed; + } + + /** + * Dispose of the resources used by the manager. + */ + dispose(): void { + if (this.isDisposed) { + return; + } + this._isDisposed = true; + clearInterval(this._refreshTimer); + Signal.clearData(this); + } + + /** + * Create an iterator over the most recent running proxy apps. + * + * @returns A new iterator over the running proxy apps. + */ + running(): IterableIterator { + return this._models[Symbol.iterator](); + } + + /** + * Shut down a server proxy app by name. + */ + async shutdown(name: string): Promise { + await shutdown(name, this.serverSettings); + await this.refreshRunning(); + } + + /** + * Shut down all server proxy apps. + * + * @returns A promise that resolves when all of the apps are shut down. + */ + async shutdownAll(): Promise { + // Update the list of models to make sure our list is current. + await this.refreshRunning(); + + // Shut down all models. + await Promise.all( + this._names.map(name => shutdown(name, this.serverSettings)) + ); + + // Update the list of models to clear out our state. + await this.refreshRunning(); + } + + /** + * Force a refresh of the running server proxy apps. + * + * @returns A promise that with the list of running proxy apps. + */ + async refreshRunning(): Promise { + return this._refreshRunning(); + } + + /** + * Refresh the running proxy apps. + */ + private async _refreshRunning(): Promise { + let models: ServerProxyApp.IModel[]; + try { + models = await listRunning(this.serverSettings); + } catch (err: any) { + // Handle network errors, as well as cases where we are on a + // JupyterHub and the server is not running. JupyterHub returns a + // 503 (<2.0) or 424 (>2.0) in that case. + if ( + err instanceof ServerConnection.NetworkError || + err.response?.status === 503 || + err.response?.status === 424 + ) { + this._connectionFailure.emit(err); + } + throw err; + } + + if (this.isDisposed) { + return; + } + + const names = models.map(({ name }) => name).sort(); + if (names === this._names) { + // Identical models list, so just return + return; + } + + this._names = names; + this._models = models; + this._runningChanged.emit(this._models); + } + + private _names: string[] = []; + private _models: ServerProxyApp.IModel[] = []; + + private _isDisposed = false; + private _refreshTimer = -1; + private _runningChanged = new Signal(this); + private _connectionFailure = new Signal(this); +} + +/** + * The namespace for `BaseManager` class statics. + */ +export namespace ServerProxyManager { + /** + * The options used to initialize a SessionManager. + */ + export interface IOptions { + /** + * The server settings for the manager. + */ + serverSettings?: ServerConnection.ISettings; + } +} diff --git a/labextension/src/restapi.ts b/labextension/src/restapi.ts new file mode 100644 index 00000000..d25f0b44 --- /dev/null +++ b/labextension/src/restapi.ts @@ -0,0 +1,58 @@ +import { URLExt } from '@jupyterlab/coreutils'; +import { ServerConnection } from '@jupyterlab/services'; +import { IModel } from './serverproxy' + +/** + * The url for the server proxy service. + */ +const SERVER_PROXY_SERVICE_URL = 'api/server-proxy'; + +/** + * List the running server proxy apps. + * + * @param settings - The server settings to use. + * + * @returns A promise that resolves with the list of running session models. + */ +export async function listRunning( + settings: ServerConnection.ISettings = ServerConnection.makeSettings() +): Promise { + const url = URLExt.join(settings.baseUrl, SERVER_PROXY_SERVICE_URL); + const response = await ServerConnection.makeRequest(url, {}, settings); + if (response.status !== 200) { + const err = await ServerConnection.ResponseError.create(response); + throw err; + } + const data = await response.json(); + + if (!Array.isArray(data)) { + throw new Error('Invalid server proxy list'); + } + + return data; +} + +/** + * Shut down a server proxy app by name. + * + * @param name - The name of the target server proxy app. + * + * @param settings - The server settings to use. + * + * @returns A promise that resolves when the app is shut down. + */ +export async function shutdown( + name: string, + settings: ServerConnection.ISettings = ServerConnection.makeSettings() +): Promise { + const url = URLExt.join(settings.baseUrl, SERVER_PROXY_SERVICE_URL, name); + const init = { method: 'DELETE' }; + const response = await ServerConnection.makeRequest(url, init, settings); + if (response.status === 404) { + const msg = `Server proxy "${name}" does not exist. Are you sure "${name}" is started by jupyter-server-proxy?`; + console.warn(msg); + } else if (response.status !== 204) { + const err = await ServerConnection.ResponseError.create(response); + throw err; + } +} diff --git a/labextension/src/serverproxy.ts b/labextension/src/serverproxy.ts new file mode 100644 index 00000000..586ec80a --- /dev/null +++ b/labextension/src/serverproxy.ts @@ -0,0 +1,90 @@ +import { JSONObject } from '@lumino/coreutils'; +import { ISignal } from '@lumino/signaling'; +import { ServerConnection } from '@jupyterlab/services'; + +/** + * The server model for a proxy. + */ +export interface IModel extends JSONObject { + /** + * The name of the proxy app. + */ + readonly name: string; + + /** + * The cmd used to launch proxy app. + */ + readonly cmd: string; + + /** + * The port at which proxy app is running. Port 0 means unix socket. + */ + readonly port: string; + + /** + * The url endpoint of the proxy app. + */ + readonly url: string; + + /** + * Proxy app managed by jupyter-server-proxy or not. + */ + managed: boolean; +} + +/** + * The interface for a server proxy manager. + * + * The manager is responsible for maintaining the state of running + * server proxy apps. + */ +export interface IManager { + /** + * The server settings for the manager. + */ + readonly serverSettings: ServerConnection.ISettings; + + /** + * A signal emitted when the running server proxy apps change. + */ + runningChanged: ISignal; + + /** + * A signal emitted when there is a connection failure. + */ + connectionFailure: ISignal; + + /** + * Create an iterator over the known server proxy apps. + * + * @returns A new iterator over the server proxy apps. + */ + running(): IterableIterator; + + /** + * Shut down a proxy app by name. + * + * @param name - The name of the proxy app. + * + * @returns A promise that resolves when the app is shut down. + */ + shutdown(name: string): Promise; + + /** + * Shut down all proxy apps. + * + * @returns A promise that resolves when all of the apps are shut down. + */ + shutdownAll(): Promise; + + /** + * Force a refresh of the running proxy apps. + * + * @returns A promise that with the list of running proxy apps. + * + * #### Notes + * This is not typically meant to be called by the user, since the + * manager maintains its own internal state. + */ + refreshRunning(): Promise; +} From d9be2489b25cc1256e3683b6ea094b0d3c204713 Mon Sep 17 00:00:00 2001 From: mahendrapaipuri Date: Wed, 12 Apr 2023 11:55:40 +0200 Subject: [PATCH 05/25] Add proxy app svg for creating icon --- labextension/src/svg.d.ts | 4 ++++ labextension/style/icons/proxy.svg | 22 ++++++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 labextension/src/svg.d.ts create mode 100644 labextension/style/icons/proxy.svg diff --git a/labextension/src/svg.d.ts b/labextension/src/svg.d.ts new file mode 100644 index 00000000..b497c6ec --- /dev/null +++ b/labextension/src/svg.d.ts @@ -0,0 +1,4 @@ +declare module '*.svg' { + const image: string; + export default image; +} diff --git a/labextension/style/icons/proxy.svg b/labextension/style/icons/proxy.svg new file mode 100644 index 00000000..70bc0aaa --- /dev/null +++ b/labextension/style/icons/proxy.svg @@ -0,0 +1,22 @@ + +Created with Fabric.js 1.7.22 + + + + + + From 3c6cd9ab1cdb969c0ffb9338160b49d0334a3074 Mon Sep 17 00:00:00 2001 From: mahendrapaipuri Date: Wed, 12 Apr 2023 12:20:17 +0200 Subject: [PATCH 06/25] Add missing arg to extension --- labextension/src/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/labextension/src/index.ts b/labextension/src/index.ts index 7e679623..8080965d 100644 --- a/labextension/src/index.ts +++ b/labextension/src/index.ts @@ -221,7 +221,8 @@ const extension: JupyterFrontEndPlugin = { id: "@jupyterhub/jupyter-server-proxy:add-launcher-entries", autoStart: true, requires: [ILauncher, ILayoutRestorer], - activate: activate, + optional: [IRunningSessionManagers], + activate: activate }; export default extension; From 38fc1800f27a2cdea734a60bdc4c23eac1f2a5f8 Mon Sep 17 00:00:00 2001 From: mahendrapaipuri Date: Wed, 12 Apr 2023 12:20:30 +0200 Subject: [PATCH 07/25] Update package.json --- jupyter_server_proxy/handlers.py | 6 +- labextension/package.json | 11 +- labextension/yarn.lock | 205 +++++++++++++++++-------------- 3 files changed, 123 insertions(+), 99 deletions(-) diff --git a/jupyter_server_proxy/handlers.py b/jupyter_server_proxy/handlers.py index 2a753df0..b2e33a08 100644 --- a/jupyter_server_proxy/handlers.py +++ b/jupyter_server_proxy/handlers.py @@ -806,9 +806,9 @@ async def ensure_process(self): # outside of jsp, we should be able to restart the process. If # process is not in running stated, remove proc object and restart # the process - if 'proc' in self.state: - if not self.state['proc'].running: - del self.state['proc'] + if "proc" in self.state: + if not self.state["proc"].running: + del self.state["proc"] if "proc" not in self.state: # FIXME: Prevent races here diff --git a/labextension/package.json b/labextension/package.json index 331bc37a..2e741698 100644 --- a/labextension/package.json +++ b/labextension/package.json @@ -18,7 +18,8 @@ }, "files": [ "LICENSE", - "lib/**/*.{d.ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}" + "lib/**/*.{d.ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}", + "style/**/*.{css,eot,gif,html,jpg,json,png,svg,woff2,ttf}" ], "main": "lib/index.js", "types": "lib/index.d.ts", @@ -44,13 +45,11 @@ }, "dependencies": { "@jupyterlab/application": "^2.0 || ^3.0", - "@jupyterlab/launcher": "^2.0 || ^3.0" - }, - "resolutions": { - "loader-utils": ">=2.0.3" + "@jupyterlab/launcher": "^2.0 || ^3.0", + "@jupyterlab/running": "^2.0 || ^3.0" }, "devDependencies": { - "@jupyterlab/builder": "^3.2.4", + "@jupyterlab/builder": "^3.6.3", "rimraf": "^3.0.2", "typescript": "~4.8.4", "yarn-deduplicate": "^6.0.0", diff --git a/labextension/yarn.lock b/labextension/yarn.lock index 0dc4c484..bbd0d35e 100644 --- a/labextension/yarn.lock +++ b/labextension/yarn.lock @@ -10,9 +10,9 @@ regenerator-runtime "^0.13.11" "@blueprintjs/colors@^4.0.0-alpha.3": - version "4.1.22" - resolved "https://registry.npmjs.org/@blueprintjs/colors/-/colors-4.1.22.tgz#033cbf03705100d5114d54161c225eec5cfeacfb" - integrity sha512-qcC7nWW9TTSS7aDxE5gbo9vrxo+IOpC6/Kzpi0rdOBYFDd02PppCdnCCjGYw1/IopSsZ9EWqDLmD7zuy0H+WEA== + version "4.1.21" + resolved "https://registry.npmjs.org/@blueprintjs/colors/-/colors-4.1.21.tgz#622c56ac7f9af466680eafcdbaf26e5c9152ad3b" + integrity sha512-5csitaTn1xyHktMRyXAcvWzsbrgtP9pK7ZmYX9f0TGjB1UG5zNaTGLexX0aFqop44SpfsSP5mbA8xGBniy8nZA== "@blueprintjs/core@^3.36.0", "@blueprintjs/core@^3.54.0": version "3.54.0" @@ -189,7 +189,7 @@ sanitize-html "~2.7.3" url "^0.11.0" -"@jupyterlab/builder@^3.2.4": +"@jupyterlab/builder@^3.6.3": version "3.6.3" resolved "https://registry.npmjs.org/@jupyterlab/builder/-/builder-3.6.3.tgz#a4b22efe34e9598b84122ff10509d3d890017b6a" integrity sha512-oY1a/r75RMoPzhSmuVu+DfjL0cKk1ceHTniZsM2wPuhjjyoF875u6CDzArJatpOOuTgLm7CY5OcU3LCIK1OAgg== @@ -389,6 +389,19 @@ lodash.escape "^4.0.1" marked "^4.0.17" +"@jupyterlab/running@^2.0 || ^3.0": + version "3.6.3" + resolved "https://registry.npmjs.org/@jupyterlab/running/-/running-3.6.3.tgz#a4a5e2510d1b38321a58aefd20b68418c8373178" + integrity sha512-KRHLCM7Qng/QSswuw5d2kTRqxDmFQX/SAsJrAZk18m7cHituZUtoMOmU74mgguGnryrynbuQZFgAVOg74NRQwQ== + dependencies: + "@jupyterlab/apputils" "^3.6.3" + "@jupyterlab/translation" "^3.6.3" + "@jupyterlab/ui-components" "^3.6.3" + "@lumino/coreutils" "^1.11.0" + "@lumino/disposable" "^1.10.0" + "@lumino/signaling" "^1.10.0" + react "^17.0.1" + "@jupyterlab/services@^6.6.3": version "6.6.3" resolved "https://registry.npmjs.org/@jupyterlab/services/-/services-6.6.3.tgz#303938e5dc5aebce7a86324a64ed89c25c61c9e7" @@ -678,10 +691,15 @@ "@types/estree" "*" "@types/json-schema" "*" -"@types/estree@*", "@types/estree@^1.0.0": - version "1.0.1" - resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz#aa22750962f3bf0e79d753d3cc067f010c95f194" - integrity sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA== +"@types/estree@*": + version "1.0.0" + resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz#5fb2e536c1ae9bf35366eed879e827fa59ca41c2" + integrity sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ== + +"@types/estree@^0.0.51": + version "0.0.51" + resolved "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40" + integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ== "@types/json-schema@*", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.7", "@types/json-schema@^7.0.8": version "7.0.11" @@ -689,9 +707,9 @@ integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== "@types/node@*": - version "18.15.12" - resolved "https://registry.npmjs.org/@types/node/-/node-18.15.12.tgz#833756634e78c829e1254db006468dadbb0c696b" - integrity sha512-Wha1UwsB3CYdqUm2PPzh/1gujGCNtWVUYF0mB00fJFoR4gTyWTDPjSm+zBF787Ahw8vSGgBja90MkgFwvB86Dg== + version "18.15.11" + resolved "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz#b3b790f09cb1696cffcec605de025b088fa4225f" + integrity sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q== "@types/prop-types@*": version "15.7.5" @@ -699,9 +717,9 @@ integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w== "@types/react@^17.0.0": - version "17.0.58" - resolved "https://registry.npmjs.org/@types/react/-/react-17.0.58.tgz#c8bbc82114e5c29001548ebe8ed6c4ba4d3c9fb0" - integrity sha512-c1GzVY97P0fGxwGxhYq989j4XwlcHQoto6wQISOC2v6wm3h0PORRWJFHlkRjfGsiG3y1609WdQ+J+tKxvrEd6A== + version "17.0.57" + resolved "https://registry.npmjs.org/@types/react/-/react-17.0.57.tgz#341152f222222075caf020ae6e0e68b9b835404c" + integrity sha512-e4msYpu5QDxzNrXDHunU/VPyv2M1XemGG/p7kfCjUiPtlLDCWLGQfgAMng6YyisWYxZ09mYdQlmMnyS0NfZdEg== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -726,33 +744,10 @@ "@types/source-list-map" "*" source-map "^0.6.1" -"@webassemblyjs/ast@1.11.5", "@webassemblyjs/ast@^1.11.5": - version "1.11.5" - resolved "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.5.tgz#6e818036b94548c1fb53b754b5cae3c9b208281c" - integrity sha512-LHY/GSAZZRpsNQH+/oHqhRQ5FT7eoULcBqgfyTB5nQHogFnK3/7QoN7dLnwSE/JkUAF0SrRuclT7ODqMFtWxxQ== - dependencies: - "@webassemblyjs/helper-numbers" "1.11.5" - "@webassemblyjs/helper-wasm-bytecode" "1.11.5" - -"@webassemblyjs/floating-point-hex-parser@1.11.5": - version "1.11.5" - resolved "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.5.tgz#e85dfdb01cad16b812ff166b96806c050555f1b4" - integrity sha512-1j1zTIC5EZOtCplMBG/IEwLtUojtwFVwdyVMbL/hwWqbzlQoJsWCOavrdnLkemwNoC/EOwtUFch3fuo+cbcXYQ== - -"@webassemblyjs/helper-api-error@1.11.5": - version "1.11.5" - resolved "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.5.tgz#1e82fa7958c681ddcf4eabef756ce09d49d442d1" - integrity sha512-L65bDPmfpY0+yFrsgz8b6LhXmbbs38OnwDCf6NpnMUYqa+ENfE5Dq9E42ny0qz/PdR0LJyq/T5YijPnU8AXEpA== - -"@webassemblyjs/helper-buffer@1.11.5": - version "1.11.5" - resolved "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.5.tgz#91381652ea95bb38bbfd270702351c0c89d69fba" - integrity sha512-fDKo1gstwFFSfacIeH5KfwzjykIE6ldh1iH9Y/8YkAZrhmu4TctqYjSh7t0K2VyDSXOZJ1MLhht/k9IvYGcIxg== - -"@webassemblyjs/helper-numbers@1.11.5": - version "1.11.5" - resolved "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.5.tgz#23380c910d56764957292839006fecbe05e135a9" - integrity sha512-DhykHXM0ZABqfIGYNv93A5KKDw/+ywBFnuWybZZWcuzWHfbp21wUfRkbtz7dMGwGgT4iXjWuhRMA2Mzod6W4WA== +"@webassemblyjs/ast@1.11.1": + version "1.11.1" + resolved "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7" + integrity sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw== dependencies: "@webassemblyjs/floating-point-hex-parser" "1.11.5" "@webassemblyjs/helper-api-error" "1.11.5" @@ -979,6 +974,11 @@ base64-js@^1.3.1: resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== +big.js@^5.2.2: + version "5.2.2" + resolved "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" + integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -1043,9 +1043,9 @@ call-bind@^1.0.0, call-bind@^1.0.2: get-intrinsic "^1.0.2" caniuse-lite@^1.0.30001449: - version "1.0.30001480" - resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001480.tgz#9bbd35ee44c2480a1e3a3b9f4496f5066817164a" - integrity sha512-q7cpoPPvZYgtyC4VaBSN0Bt+PJ4c4EYRf0DrduInOz2SkFpHD5p3LnvEpqBp7UnJn+8x1Ogl1s38saUxe+ihQQ== + version "1.0.30001478" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001478.tgz#0ef8a1cf8b16be47a0f9fc4ecfc952232724b32a" + integrity sha512-gMhDyXGItTHipJj2ApIvR+iVB5hd0KP3svMWWXDvZOmjzJJassGLMfxRkQCSYgGd2gtdL/ReeiyvMSFD1Ss6Mw== chalk@^2.3.0, chalk@^2.4.1: version "2.4.2" @@ -1103,9 +1103,9 @@ color-name@1.1.3: integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== colorette@^2.0.14: - version "2.0.20" - resolved "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" - integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== + version "2.0.19" + resolved "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798" + integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ== commander@^2.20.0: version "2.20.3" @@ -1157,9 +1157,9 @@ concat-map@0.0.1: integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== core-js-pure@^3.6.5: - version "3.30.1" - resolved "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.30.1.tgz#7d93dc89e7d47b8ef05d7e79f507b0e99ea77eec" - integrity sha512-nXBEVpmUnNRhz83cHd9JRQC52cTMcuXAmR56+9dSMpRdpeA4I1PX6yjmhd71Eyc/wXNsdBdUDIj1QTIeZpU5Tg== + version "3.30.0" + resolved "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.30.0.tgz#41b6c42e5f363bd53d79999bd35093b17e42e1bf" + integrity sha512-+2KbMFGeBU0ln/csoPqTe0i/yfHbrd2EUhNMObsGtXMKS/RTtlkYyi+/3twLcevbgNR0yM/r0Psa3TEoQRpFMQ== cross-spawn@^6.0.5: version "6.0.5" @@ -1251,7 +1251,7 @@ deferred-leveldown@~5.3.0: abstract-leveldown "~6.2.1" inherits "^2.0.3" -define-properties@^1.1.3, define-properties@^1.1.4, define-properties@^1.2.0: +define-properties@^1.1.3, define-properties@^1.1.4: version "1.2.0" resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz#52988570670c9eacedd8064f4a990f2405849bd5" integrity sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA== @@ -1312,9 +1312,14 @@ duplicate-package-checker-webpack-plugin@^3.0.0: semver "^5.4.1" electron-to-chromium@^1.4.284: - version "1.4.368" - resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.368.tgz#75901f97d3e23da2e66feb1e61fbb8e70ac96430" - integrity sha512-e2aeCAixCj9M7nJxdB/wDjO6mbYX+lJJxSJCXDzlr5YPGYVofuJwGN9nKg2o6wWInjX6XmxRinn3AeJMK81ltw== + version "1.4.359" + resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.359.tgz#5c4d13cb08032469fcd6bd36457915caa211356b" + integrity sha512-OoVcngKCIuNXtZnsYoqlCvr0Cf3NIPzDIgwUfI9bdTFjXCrr79lI0kwQstLPZ7WhCezLlGksZk/BFAzoXC7GDw== + +emojis-list@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" + integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== encoding-down@^6.3.0: version "6.3.0" @@ -1326,10 +1331,10 @@ encoding-down@^6.3.0: level-codec "^9.0.0" level-errors "^2.0.0" -enhanced-resolve@^5.13.0: - version "5.13.0" - resolved "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.13.0.tgz#26d1ecc448c02de997133217b5c1053f34a0a275" - integrity sha512-eyV8f0y1+bzyfh8xAwW/WTSZpLbjhqc4ne9eGSH4Zo2ejdyiNG9pU6mf9DG8a7+Auk6MFTlNOT4Y2y/9k8GKVg== +enhanced-resolve@^5.10.0: + version "5.12.0" + resolved "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz#300e1c90228f5b570c4d35babf263f6da7155634" + integrity sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ== dependencies: graceful-fs "^4.2.4" tapable "^2.2.0" @@ -1398,10 +1403,10 @@ es-abstract@^1.19.0, es-abstract@^1.20.4: unbox-primitive "^1.0.2" which-typed-array "^1.1.9" -es-module-lexer@^1.2.1: - version "1.2.1" - resolved "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.2.1.tgz#ba303831f63e6a394983fde2f97ad77b22324527" - integrity sha512-9978wrXM50Y4rTMmW5kXIC09ZdXQZqkE4mxhwkd8VbzsGkXGPgV4zWuqQJgCEzYngdo2dYDa0l8xhX4fkSwJSg== +es-module-lexer@^0.9.0: + version "0.9.3" + resolved "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz#6f13db00cc38417137daf74366f535c8eb438f19" + integrity sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ== es-set-tostringtag@^2.0.1: version "2.0.1" @@ -1964,7 +1969,14 @@ json-schema-traverse@^0.4.1: resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== -json5@^2.1.1: +json5@^1.0.1: + version "1.0.2" + resolved "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" + integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== + dependencies: + minimist "^1.2.0" + +json5@^2.1.1, json5@^2.1.2: version "2.2.3" resolved "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== @@ -2100,10 +2112,23 @@ loader-runner@^4.2.0: resolved "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== -loader-utils@>=2.0.3, loader-utils@^1.0.0, loader-utils@^2.0.0, loader-utils@~2.0.0: - version "3.2.1" - resolved "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz#4fb104b599daafd82ef3e1a41fb9265f87e1f576" - integrity sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw== +loader-utils@^1.0.0: + version "1.4.2" + resolved "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz#29a957f3a63973883eb684f10ffd3d151fec01a3" + integrity sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg== + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^1.0.1" + +loader-utils@^2.0.0, loader-utils@~2.0.0: + version "2.0.4" + resolved "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c" + integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw== + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^2.1.2" locate-path@^5.0.0: version "5.0.0" @@ -2196,7 +2221,7 @@ minimatch@^3.0.4, minimatch@^3.1.1: dependencies: brace-expansion "^1.1.7" -minimist@~1.2.0: +minimist@^1.2.0, minimist@~1.2.0: version "1.2.8" resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== @@ -2252,7 +2277,7 @@ moment@^2.24.0: resolved "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108" integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w== -nanoid@^3.1.23, nanoid@^3.3.6: +nanoid@^3.1.23, nanoid@^3.3.4: version "3.3.6" resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c" integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA== @@ -2511,9 +2536,9 @@ postcss-value-parser@^4.1.0: integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== postcss@^8.2.15, postcss@^8.3.11: - version "8.4.23" - resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.23.tgz#df0aee9ac7c5e53e1075c24a3613496f9e6552ab" - integrity sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA== + version "8.4.21" + resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz#c639b719a57efc3187b13a1d765675485f4134f4" + integrity sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg== dependencies: nanoid "^3.3.6" picocolors "^1.0.0" @@ -2664,8 +2689,8 @@ regexp.prototype.flags@^1.2.0, regexp.prototype.flags@^1.4.3: integrity sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA== dependencies: call-bind "^1.0.2" - define-properties "^1.2.0" - functions-have-names "^1.2.3" + define-properties "^1.1.3" + functions-have-names "^1.2.2" requires-port@^1.0.0: version "1.0.0" @@ -2768,9 +2793,9 @@ semver@^6.0.0: integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== semver@^7.3.5, semver@^7.3.8: - version "7.5.0" - resolved "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz#ed8c5dc8efb6c629c88b23d41dc9bf40c1d96cd0" - integrity sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA== + version "7.4.0" + resolved "https://registry.npmjs.org/semver/-/semver-7.4.0.tgz#8481c92feffc531ab1e012a8ffc15bdd3a0f4318" + integrity sha512-RgOxM8Mw+7Zus0+zcLEUn8+JfoLpj/huFTItQy2hsM4khuC1HYRDp0cU482Ewn/Fcy6bCjufD8vAj7voC66KQw== dependencies: lru-cache "^6.0.0" @@ -3022,7 +3047,7 @@ terser-webpack-plugin@^4.1.0: terser "^5.3.4" webpack-sources "^1.4.3" -terser-webpack-plugin@^5.3.7: +terser-webpack-plugin@^5.1.3: version "5.3.7" resolved "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.7.tgz#ef760632d24991760f339fe9290deb936ad1ffc7" integrity sha512-AfKwIktyP7Cu50xNjXF/6Qb5lBNzYaWpU6YfoX3uZicTx0zTy0stDDCsvjDapKsSDvOeWo5MEq4TmdBy2cNoHw== @@ -3034,9 +3059,9 @@ terser-webpack-plugin@^5.3.7: terser "^5.16.5" terser@^5.16.5, terser@^5.3.4: - version "5.17.1" - resolved "https://registry.npmjs.org/terser/-/terser-5.17.1.tgz#948f10830454761e2eeedc6debe45c532c83fd69" - integrity sha512-hVl35zClmpisy6oaoKALOpS0rDYLxRFLHhRuDlEGTKey9qHjS1w9GMORjuwIMt70Wan4lwsLYyWDVnWgF+KUEw== + version "5.16.9" + resolved "https://registry.npmjs.org/terser/-/terser-5.16.9.tgz#7a28cb178e330c484369886f2afd623d9847495f" + integrity sha512-HPa/FdTB9XGI2H1/keLFZHxl6WNvAI4YalHGtDQTlMnJcoqSab1UwL4l1hGEhs6/GmLHBZIg/YgB++jcbzoOEg== dependencies: "@jridgewell/source-map" "^0.3.2" acorn "^8.5.0" @@ -3129,9 +3154,9 @@ universalify@^2.0.0: integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== update-browserslist-db@^1.0.10: - version "1.0.11" - resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz#9a2a641ad2907ae7b3616506f4b977851db5b940" - integrity sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA== + version "1.0.10" + resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3" + integrity sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ== dependencies: escalade "^3.1.1" picocolors "^1.0.0" @@ -3276,9 +3301,9 @@ webpack-sources@^3.2.3: integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== webpack@^5.41.1: - version "5.80.0" - resolved "https://registry.npmjs.org/webpack/-/webpack-5.80.0.tgz#3e660b4ab572be38c5e954bdaae7e2bf76010fdc" - integrity sha512-OIMiq37XK1rWO8mH9ssfFKZsXg4n6klTEDL7S8/HqbAOBBaiy8ABvXvz0dDCXeEF9gqwxSvVk611zFPjS8hJxA== + version "5.78.0" + resolved "https://registry.npmjs.org/webpack/-/webpack-5.78.0.tgz#836452a12416af2a7beae906b31644cb2562f9e6" + integrity sha512-gT5DP72KInmE/3azEaQrISjTvLYlSM0j1Ezhht/KLVkrqtv10JoP/RXhwmX/frrutOPuSq3o5Vq0ehR/4Vmd1g== dependencies: "@types/eslint-scope" "^3.7.3" "@types/estree" "^1.0.0" @@ -3449,9 +3474,9 @@ yarn-deduplicate@^6.0.0: tslib "^2.4.1" yjs@^13.5.40: - version "13.5.53" - resolved "https://registry.npmjs.org/yjs/-/yjs-13.5.53.tgz#6531378981b89cfadd107145f7fb9f65f708a01f" - integrity sha512-B4UUycEK8BcYf195HL4LN4Az4Sg2+QzTHnabFHjQwLvGn96v/G+4CS52xNZk/0QWNXhLRCb+2GK3JmcX5fiCEQ== + version "13.5.52" + resolved "https://registry.npmjs.org/yjs/-/yjs-13.5.52.tgz#aec0535e16d45ed4defd6489fffae2b17e30fdb3" + integrity sha512-wTajR70VeI6uztpUk4kMcXYHSRzuUlNyJPdBG9NII0EcFf27DwGduZEm3XbP7VSzlGx5n6uenBhOPX+YuPH/tA== dependencies: lib0 "^0.2.72" From f54062012fc6d7468f2167b37efbd87a9cc8ae57 Mon Sep 17 00:00:00 2001 From: mahendrapaipuri Date: Wed, 12 Apr 2023 12:48:31 +0200 Subject: [PATCH 08/25] Use staticfilehandler to serve icons --- jupyter_server_proxy/__init__.py | 17 ++++++------ jupyter_server_proxy/api.py | 45 +++++++------------------------- 2 files changed, 19 insertions(+), 43 deletions(-) diff --git a/jupyter_server_proxy/__init__.py b/jupyter_server_proxy/__init__.py index 9a3dd89f..99e5a07d 100644 --- a/jupyter_server_proxy/__init__.py +++ b/jupyter_server_proxy/__init__.py @@ -58,10 +58,16 @@ def _load_jupyter_server_extension(nbapp): serverproxy_config, ) - icons = {} + icon_handlers = [] for sp in server_processes: if sp.launcher_entry.enabled and sp.launcher_entry.icon_path: - icons[sp.name] = sp.launcher_entry.icon_path + icon_handlers.append( + ( + ujoin(base_url, f"server-proxy/icon/{sp.name}"), + IconHandler, + {"path": sp.launcher_entry.icon_path} + ) + ) nbapp.web_app.add_handlers( ".*", @@ -71,11 +77,6 @@ def _load_jupyter_server_extension(nbapp): ServersInfoHandler, {"server_processes": server_processes}, ), - ( - ujoin(base_url, r"server-proxy/icon/(?P.*)"), - IconHandler, - {"icons": icons} - ), ( ujoin(base_url, r"api/server-proxy"), ListServersAPIHandler @@ -84,7 +85,7 @@ def _load_jupyter_server_extension(nbapp): ujoin(base_url, r"api/server-proxy/(?P.*)"), ServersAPIHandler ), - ], + ] + icon_handlers, ) nbapp.log.debug( diff --git a/jupyter_server_proxy/api.py b/jupyter_server_proxy/api.py index 825c5a35..faf6ada2 100644 --- a/jupyter_server_proxy/api.py +++ b/jupyter_server_proxy/api.py @@ -1,4 +1,3 @@ -import mimetypes import json from jupyter_server.base.handlers import JupyterHandler @@ -37,42 +36,18 @@ async def get(self): self.write({"server_processes": data}) -# FIXME: Should be a StaticFileHandler subclass -class IconHandler(JupyterHandler): - """ - Serve launcher icons - """ +# Took it from JupyterHub LogoHandler +class IconHandler(web.StaticFileHandler): + """A singular handler for serving the icon.""" - def initialize(self, icons): - """ - icons is a dict of titles to paths - """ - self.icons = icons + def get(self): + return super().get('') - async def get(self, name): - if name not in self.icons: - raise web.HTTPError(404) - path = self.icons[name] - - # Guess mimetype appropriately - # Stolen from https://github.com/tornadoweb/tornado/blob/b399a9d19c45951e4561e6e580d7e8cf396ef9ff/tornado/web.py#L2881 - mime_type, encoding = mimetypes.guess_type(path) - if encoding == "gzip": - content_type = "application/gzip" - # As of 2015-07-21 there is no bzip2 encoding defined at - # http://www.iana.org/assignments/media-types/media-types.xhtml - # So for that (and any other encoding), use octet-stream. - elif encoding is not None: - content_type = "application/octet-stream" - elif mime_type is not None: - content_type = mime_type - # if mime_type not detected, use application/octet-stream - else: - content_type = "application/octet-stream" - - with open(self.icons[name]) as f: - self.write(f.read()) - self.set_header("Content-Type", content_type) + @classmethod + def get_absolute_path(cls, root, path): + """We only serve one file, ignore relative path""" + import os + return os.path.abspath(root) class ServersAPIHandler(JupyterHandler): From 3719188f9ff94a266b75cd61e929d6e2805d768d Mon Sep 17 00:00:00 2001 From: mahendrapaipuri Date: Wed, 12 Apr 2023 13:36:27 +0200 Subject: [PATCH 09/25] Update server info API URL --- jupyter_server_proxy/static/tree.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyter_server_proxy/static/tree.js b/jupyter_server_proxy/static/tree.js index 41725aba..44a11873 100644 --- a/jupyter_server_proxy/static/tree.js +++ b/jupyter_server_proxy/static/tree.js @@ -12,7 +12,7 @@ define(["jquery", "base/js/namespace", "base/js/utils"], function ( function load() { if (!Jupyter.notebook_list) return; - var servers_info_url = base_url + "server-proxy/servers-info"; + var servers_info_url = base_url + "api/server-proxy/servers-info"; $.get(servers_info_url, function (data) { /* locate the right-side dropdown menu of apps and notebooks */ var $menu = $(".tree-buttons").find(".dropdown-menu"); From 5bab31e9cb6c84cc551c5b11f986d85b792fb153 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 12 Apr 2023 13:17:15 +0000 Subject: [PATCH 10/25] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- jupyter_server_proxy/__init__.py | 26 +++---- jupyter_server_proxy/api.py | 3 +- jupyter_server_proxy/handlers.py | 7 +- jupyter_server_proxy/manager.py | 42 +++++------ jupyter_server_proxy/utils.py | 3 +- labextension/src/index.ts | 39 ++++++----- labextension/src/manager.ts | 115 ++++++++++++++++--------------- labextension/src/restapi.ts | 20 +++--- labextension/src/serverproxy.ts | 6 +- labextension/src/svg.d.ts | 2 +- 10 files changed, 133 insertions(+), 130 deletions(-) diff --git a/jupyter_server_proxy/__init__.py b/jupyter_server_proxy/__init__.py index 99e5a07d..b57f718b 100644 --- a/jupyter_server_proxy/__init__.py +++ b/jupyter_server_proxy/__init__.py @@ -1,12 +1,13 @@ from jupyter_server.utils import url_path_join as ujoin from .api import ( - IconHandler, ServersInfoHandler, ServersAPIHandler, ListServersAPIHandler + IconHandler, + ListServersAPIHandler, + ServersAPIHandler, + ServersInfoHandler, ) from .config import ServerProxy as ServerProxyConfig -from .config import ( - get_entrypoint_server_processes, make_handlers, make_server_process -) +from .config import get_entrypoint_server_processes, make_handlers, make_server_process from .handlers import setup_handlers @@ -62,10 +63,10 @@ def _load_jupyter_server_extension(nbapp): for sp in server_processes: if sp.launcher_entry.enabled and sp.launcher_entry.icon_path: icon_handlers.append( - ( + ( ujoin(base_url, f"server-proxy/icon/{sp.name}"), IconHandler, - {"path": sp.launcher_entry.icon_path} + {"path": sp.launcher_entry.icon_path}, ) ) @@ -77,15 +78,10 @@ def _load_jupyter_server_extension(nbapp): ServersInfoHandler, {"server_processes": server_processes}, ), - ( - ujoin(base_url, r"api/server-proxy"), - ListServersAPIHandler - ), - ( - ujoin(base_url, r"api/server-proxy/(?P.*)"), - ServersAPIHandler - ), - ] + icon_handlers, + (ujoin(base_url, r"api/server-proxy"), ListServersAPIHandler), + (ujoin(base_url, r"api/server-proxy/(?P.*)"), ServersAPIHandler), + ] + + icon_handlers, ) nbapp.log.debug( diff --git a/jupyter_server_proxy/api.py b/jupyter_server_proxy/api.py index faf6ada2..445bb3c7 100644 --- a/jupyter_server_proxy/api.py +++ b/jupyter_server_proxy/api.py @@ -41,12 +41,13 @@ class IconHandler(web.StaticFileHandler): """A singular handler for serving the icon.""" def get(self): - return super().get('') + return super().get("") @classmethod def get_absolute_path(cls, root, path): """We only serve one file, ignore relative path""" import os + return os.path.abspath(root) diff --git a/jupyter_server_proxy/handlers.py b/jupyter_server_proxy/handlers.py index b2e33a08..94b4d0ab 100644 --- a/jupyter_server_proxy/handlers.py +++ b/jupyter_server_proxy/handlers.py @@ -20,10 +20,10 @@ from traitlets import Bytes, Dict, Instance, Integer, Unicode, Union, default, observe from traitlets.traitlets import HasTraits +from .manager import manager from .unixsock import UnixResolver from .utils import call_with_asked_args from .websocket import WebSocketHandlerMixin, pingable_ws_connect -from .manager import manager class RewritableResponse(HasTraits): @@ -801,7 +801,6 @@ async def ensure_process(self): # Invariant here should be: when lock isn't being held, either 'proc' is in state & # running, or not. async with self.state["proc_lock"]: - # If the server process is terminated via Runningsessions or killed # outside of jsp, we should be able to restart the process. If # process is not in running stated, remove proc object and restart @@ -848,7 +847,9 @@ async def ensure_process(self): # If process started succesfully, add it to manager # Add the server proxy app to manager - await manager.add_server_proxy_app(self.name, self.base_url, cmd, self.port, proc) + await manager.add_server_proxy_app( + self.name, self.base_url, cmd, self.port, proc + ) except: # Make sure we remove proc from state in any error condition del self.state["proc"] diff --git a/jupyter_server_proxy/manager.py b/jupyter_server_proxy/manager.py index af4d10ce..d350591f 100644 --- a/jupyter_server_proxy/manager.py +++ b/jupyter_server_proxy/manager.py @@ -1,18 +1,14 @@ """Manager for jupyter server proxy""" from collections import namedtuple -from tornado.ioloop import PeriodicCallback + from jupyter_server.utils import url_path_join as ujoin +from tornado.ioloop import PeriodicCallback from .utils import check_pid - -ServerProxy = namedtuple('ServerProxy', [ - 'name', 'url', 'cmd', 'port', 'managed' -]) -ServerProxyProc = namedtuple('ServerProxyProc', [ - 'name', 'proc' -]) +ServerProxy = namedtuple("ServerProxy", ["name", "url", "cmd", "port", "managed"]) +ServerProxyProc = namedtuple("ServerProxyProc", ["name", "proc"]) async def monitor_server_proxy_procs(): @@ -53,25 +49,31 @@ async def add_server_proxy_app(self, name, base_url, cmd, port, proc): # Add proxy server metadata self.server_proxy_apps.append( ServerProxy( - name=name, - url=ujoin(base_url, name), - cmd=' '.join(cmd), - port=port, - managed=True if proc else False, - )) + name=name, + url=ujoin(base_url, name), + cmd=" ".join(cmd), + port=port, + managed=True if proc else False, + ) + ) # Add proxy server proc object so that we can send SIGTERM # when user chooses to shut it down self._server_proxy_procs.append( ServerProxyProc( - name=name, - proc=proc, - )) + name=name, + proc=proc, + ) + ) async def del_server_proxy_app(self, name): """Remove a launched proxy server from list""" - self.server_proxy_apps = [app for app in self.server_proxy_apps if app.name != name] - self._server_proxy_procs = [app for app in self._server_proxy_procs if app.name != name] + self.server_proxy_apps = [ + app for app in self.server_proxy_apps if app.name != name + ] + self._server_proxy_procs = [ + app for app in self._server_proxy_procs if app.name != name + ] self.num_active_server_proxy_apps -= 1 def get_server_proxy_app(self, name): @@ -121,5 +123,5 @@ async def terminate_all(self): manager = ServerProxyAppManager() # Create a Periodic call back function to check the status of processes -pc = PeriodicCallback(monitor_server_proxy_procs, 1e4) +pc = PeriodicCallback(monitor_server_proxy_procs, 1e4) pc.start() diff --git a/jupyter_server_proxy/utils.py b/jupyter_server_proxy/utils.py index 133fb513..cf2c57fa 100644 --- a/jupyter_server_proxy/utils.py +++ b/jupyter_server_proxy/utils.py @@ -1,4 +1,5 @@ import os + from traitlets import TraitType @@ -53,7 +54,7 @@ def validate(self, obj, value): def check_pid(pid): - """ Check For the existence of a unix pid""" + """Check For the existence of a unix pid""" try: os.kill(pid, 0) except OSError: diff --git a/labextension/src/index.ts b/labextension/src/index.ts index 8080965d..c3121cd9 100644 --- a/labextension/src/index.ts +++ b/labextension/src/index.ts @@ -5,20 +5,20 @@ import { } from "@jupyterlab/application"; import { ILauncher } from "@jupyterlab/launcher"; import { PageConfig } from "@jupyterlab/coreutils"; -import { IRunningSessionManagers, IRunningSessions } from '@jupyterlab/running'; +import { IRunningSessionManagers, IRunningSessions } from "@jupyterlab/running"; import { IFrame, MainAreaWidget, WidgetTracker } from "@jupyterlab/apputils"; -import { LabIcon } from '@jupyterlab/ui-components'; -import { ServerProxyManager } from './manager'; -import { IModel as IServerProxyModel } from './serverproxy'; -import serverProxyAppSvgstr from '../style/icons/proxy.svg'; +import { LabIcon } from "@jupyterlab/ui-components"; +import { ServerProxyManager } from "./manager"; +import { IModel as IServerProxyModel } from "./serverproxy"; +import serverProxyAppSvgstr from "../style/icons/proxy.svg"; export const ServerProxyAppIcon = new LabIcon({ - name: 'server-proxy:proxyAppIcon', - svgstr: serverProxyAppSvgstr + name: "server-proxy:proxyAppIcon", + svgstr: serverProxyAppSvgstr, }); namespace CommandIDs { - export const open = 'running-server-proxy:open'; + export const open = "running-server-proxy:open"; } function newServerProxyWidget( @@ -52,21 +52,22 @@ function newServerProxyWidget( * User can shut down the applications as well to restart them in future * */ - function addRunningSessionManager( +function addRunningSessionManager( managers: IRunningSessionManagers, app: JupyterFrontEnd, - manager: ServerProxyManager + manager: ServerProxyManager, ): void { managers.add({ - name: 'Server Proxy Apps', + name: "Server Proxy Apps", running: () => Array.from(manager.running()).map( - model => new RunningServerProxyApp(model) + (model) => new RunningServerProxyApp(model), ), shutdownAll: () => manager.shutdownAll(), refreshRunning: () => manager.refreshRunning(), runningChanged: manager.runningChanged, - shutdownAllConfirmationText: 'Are you sure you want to close all server proxy applications?' + shutdownAllConfirmationText: + "Are you sure you want to close all server proxy applications?", }); class RunningServerProxyApp implements IRunningSessions.IRunningItem { @@ -103,7 +104,7 @@ async function activate( app: JupyterFrontEnd, launcher: ILauncher, restorer: ILayoutRestorer, - sessions: IRunningSessionManagers | null + sessions: IRunningSessionManagers | null, ): Promise { // Fetch configured server processes from {base_url}/server-proxy/servers-info const response = await fetch( @@ -175,12 +176,12 @@ async function activate( }); commands.addCommand(CommandIDs.open, { - execute: args => { - const model = args['sp'] as IServerProxyModel; + execute: (args) => { + const model = args["sp"] as IServerProxyModel; const url = PageConfig.getBaseUrl() + model.url; - window.open(url, '_blank'); + window.open(url, "_blank"); return; - } + }, }); for (let server_process of data.server_processes) { @@ -222,7 +223,7 @@ const extension: JupyterFrontEndPlugin = { autoStart: true, requires: [ILauncher, ILayoutRestorer], optional: [IRunningSessionManagers], - activate: activate + activate: activate, }; export default extension; diff --git a/labextension/src/manager.ts b/labextension/src/manager.ts index 57338393..c7dab41c 100644 --- a/labextension/src/manager.ts +++ b/labextension/src/manager.ts @@ -1,7 +1,7 @@ -import { Signal, ISignal } from '@lumino/signaling'; -import { ServerConnection } from '@jupyterlab/services'; -import { listRunning, shutdown } from './restapi'; -import * as ServerProxyApp from './serverproxy'; +import { Signal, ISignal } from "@lumino/signaling"; +import { ServerConnection } from "@jupyterlab/services"; +import { listRunning, shutdown } from "./restapi"; +import * as ServerProxyApp from "./serverproxy"; /** * A server proxy manager. @@ -11,13 +11,14 @@ export class ServerProxyManager implements ServerProxyApp.IManager { * Construct a new server proxy manager. */ constructor(options: ServerProxyManager.IOptions = {}) { - this.serverSettings = options.serverSettings || ServerConnection.makeSettings(); - this._refreshTimer = (setInterval as any)(() => { - if (typeof document !== 'undefined' && document.hidden) { - return; - } - this._refreshRunning(); - }, 10000); + this.serverSettings = + options.serverSettings || ServerConnection.makeSettings(); + this._refreshTimer = (setInterval as any)(() => { + if (typeof document !== "undefined" && document.hidden) { + return; + } + this._refreshRunning(); + }, 10000); } /** @@ -29,33 +30,33 @@ export class ServerProxyManager implements ServerProxyApp.IManager { * A signal emitted when the running server proxies change. */ get runningChanged(): ISignal { - return this._runningChanged; + return this._runningChanged; } /** * A signal emitted when there is a connection failure. */ get connectionFailure(): ISignal { - return this._connectionFailure; + return this._connectionFailure; } /** * Test whether the delegate has been disposed. */ get isDisposed(): boolean { - return this._isDisposed; + return this._isDisposed; } /** * Dispose of the resources used by the manager. */ dispose(): void { - if (this.isDisposed) { - return; - } - this._isDisposed = true; - clearInterval(this._refreshTimer); - Signal.clearData(this); + if (this.isDisposed) { + return; + } + this._isDisposed = true; + clearInterval(this._refreshTimer); + Signal.clearData(this); } /** @@ -64,15 +65,15 @@ export class ServerProxyManager implements ServerProxyApp.IManager { * @returns A new iterator over the running proxy apps. */ running(): IterableIterator { - return this._models[Symbol.iterator](); + return this._models[Symbol.iterator](); } /** * Shut down a server proxy app by name. */ async shutdown(name: string): Promise { - await shutdown(name, this.serverSettings); - await this.refreshRunning(); + await shutdown(name, this.serverSettings); + await this.refreshRunning(); } /** @@ -81,16 +82,16 @@ export class ServerProxyManager implements ServerProxyApp.IManager { * @returns A promise that resolves when all of the apps are shut down. */ async shutdownAll(): Promise { - // Update the list of models to make sure our list is current. - await this.refreshRunning(); + // Update the list of models to make sure our list is current. + await this.refreshRunning(); - // Shut down all models. - await Promise.all( - this._names.map(name => shutdown(name, this.serverSettings)) - ); + // Shut down all models. + await Promise.all( + this._names.map((name) => shutdown(name, this.serverSettings)), + ); - // Update the list of models to clear out our state. - await this.refreshRunning(); + // Update the list of models to clear out our state. + await this.refreshRunning(); } /** @@ -99,43 +100,43 @@ export class ServerProxyManager implements ServerProxyApp.IManager { * @returns A promise that with the list of running proxy apps. */ async refreshRunning(): Promise { - return this._refreshRunning(); + return this._refreshRunning(); } /** * Refresh the running proxy apps. */ private async _refreshRunning(): Promise { - let models: ServerProxyApp.IModel[]; - try { - models = await listRunning(this.serverSettings); - } catch (err: any) { - // Handle network errors, as well as cases where we are on a - // JupyterHub and the server is not running. JupyterHub returns a - // 503 (<2.0) or 424 (>2.0) in that case. - if ( - err instanceof ServerConnection.NetworkError || - err.response?.status === 503 || - err.response?.status === 424 - ) { - this._connectionFailure.emit(err); - } - throw err; + let models: ServerProxyApp.IModel[]; + try { + models = await listRunning(this.serverSettings); + } catch (err: any) { + // Handle network errors, as well as cases where we are on a + // JupyterHub and the server is not running. JupyterHub returns a + // 503 (<2.0) or 424 (>2.0) in that case. + if ( + err instanceof ServerConnection.NetworkError || + err.response?.status === 503 || + err.response?.status === 424 + ) { + this._connectionFailure.emit(err); } + throw err; + } - if (this.isDisposed) { - return; - } + if (this.isDisposed) { + return; + } - const names = models.map(({ name }) => name).sort(); - if (names === this._names) { + const names = models.map(({ name }) => name).sort(); + if (names === this._names) { // Identical models list, so just return - return; - } + return; + } - this._names = names; - this._models = models; - this._runningChanged.emit(this._models); + this._names = names; + this._models = models; + this._runningChanged.emit(this._models); } private _names: string[] = []; diff --git a/labextension/src/restapi.ts b/labextension/src/restapi.ts index d25f0b44..31db658a 100644 --- a/labextension/src/restapi.ts +++ b/labextension/src/restapi.ts @@ -1,11 +1,11 @@ -import { URLExt } from '@jupyterlab/coreutils'; -import { ServerConnection } from '@jupyterlab/services'; -import { IModel } from './serverproxy' +import { URLExt } from "@jupyterlab/coreutils"; +import { ServerConnection } from "@jupyterlab/services"; +import { IModel } from "./serverproxy"; /** * The url for the server proxy service. */ -const SERVER_PROXY_SERVICE_URL = 'api/server-proxy'; +const SERVER_PROXY_SERVICE_URL = "api/server-proxy"; /** * List the running server proxy apps. @@ -15,7 +15,7 @@ const SERVER_PROXY_SERVICE_URL = 'api/server-proxy'; * @returns A promise that resolves with the list of running session models. */ export async function listRunning( - settings: ServerConnection.ISettings = ServerConnection.makeSettings() + settings: ServerConnection.ISettings = ServerConnection.makeSettings(), ): Promise { const url = URLExt.join(settings.baseUrl, SERVER_PROXY_SERVICE_URL); const response = await ServerConnection.makeRequest(url, {}, settings); @@ -26,7 +26,7 @@ export async function listRunning( const data = await response.json(); if (!Array.isArray(data)) { - throw new Error('Invalid server proxy list'); + throw new Error("Invalid server proxy list"); } return data; @@ -43,16 +43,16 @@ export async function listRunning( */ export async function shutdown( name: string, - settings: ServerConnection.ISettings = ServerConnection.makeSettings() + settings: ServerConnection.ISettings = ServerConnection.makeSettings(), ): Promise { const url = URLExt.join(settings.baseUrl, SERVER_PROXY_SERVICE_URL, name); - const init = { method: 'DELETE' }; + const init = { method: "DELETE" }; const response = await ServerConnection.makeRequest(url, init, settings); if (response.status === 404) { const msg = `Server proxy "${name}" does not exist. Are you sure "${name}" is started by jupyter-server-proxy?`; console.warn(msg); } else if (response.status !== 204) { - const err = await ServerConnection.ResponseError.create(response); - throw err; + const err = await ServerConnection.ResponseError.create(response); + throw err; } } diff --git a/labextension/src/serverproxy.ts b/labextension/src/serverproxy.ts index 586ec80a..3013e483 100644 --- a/labextension/src/serverproxy.ts +++ b/labextension/src/serverproxy.ts @@ -1,6 +1,6 @@ -import { JSONObject } from '@lumino/coreutils'; -import { ISignal } from '@lumino/signaling'; -import { ServerConnection } from '@jupyterlab/services'; +import { JSONObject } from "@lumino/coreutils"; +import { ISignal } from "@lumino/signaling"; +import { ServerConnection } from "@jupyterlab/services"; /** * The server model for a proxy. diff --git a/labextension/src/svg.d.ts b/labextension/src/svg.d.ts index b497c6ec..070d6622 100644 --- a/labextension/src/svg.d.ts +++ b/labextension/src/svg.d.ts @@ -1,4 +1,4 @@ -declare module '*.svg' { +declare module "*.svg" { const image: string; export default image; } From d1653d11babfd9717a5b81a2e42e3031d3c79462 Mon Sep 17 00:00:00 2001 From: mahendrapaipuri Date: Fri, 28 Apr 2023 10:20:36 +0200 Subject: [PATCH 11/25] Rework manager based on PR comments Do not instantiate manager in the file anymore. This will be done during extension loading Rework monitoring for proxy apps natively using asyncio. This will be added as callback to ServerApp IO loop during extension loading Manager spits out logs in debug mode when there is proxy app is added/removed from it. Added uni_socket to server proxy app dataclass Remove unnecessary async methods Properly handle the get_server_proxy_{app,proc} methods when server proxy is not found in the manager. We return a default ServerProxyApp tuple in this case with empty values --- jupyter_server_proxy/config.py | 18 +++++- jupyter_server_proxy/manager.py | 106 +++++++++++++++++++------------- jupyter_server_proxy/utils.py | 10 --- 3 files changed, 79 insertions(+), 55 deletions(-) diff --git a/jupyter_server_proxy/config.py b/jupyter_server_proxy/config.py index f0c1348f..d6966449 100644 --- a/jupyter_server_proxy/config.py +++ b/jupyter_server_proxy/config.py @@ -11,9 +11,11 @@ from importlib.metadata import entry_points from jupyter_server.utils import url_path_join as ujoin -from traitlets import Dict, List, Tuple, Union, default, observe +from traitlets import Dict, List, Tuple, Union, Int, default, observe from traitlets.config import Configurable +from jupyter_server.utils import url_path_join as ujoin + from .handlers import AddSlashHandler, NamedLocalProxyHandler, SuperviseAndProxyHandler try: @@ -102,7 +104,7 @@ def get_entrypoint_server_processes(serverproxy_config): return sps -def make_handlers(base_url, server_processes): +def make_handlers(base_url, manager, server_processes): """ Get tornado handlers for registered server_processes """ @@ -110,7 +112,7 @@ def make_handlers(base_url, server_processes): for sp in server_processes: if sp.command: handler = _make_supervisedproxy_handler(sp) - kwargs = dict(state={}) + kwargs = dict(state={}, manager=manager) else: if not (sp.port or isinstance(sp.unix_socket, str)): warn( @@ -341,3 +343,13 @@ def _host_whitelist_deprecated(self, change): ) ) self.host_allowlist = change.new + + monitor_interval = Int( + 10, + help=""" + Proxy polling interval in seconds. The server proxy manager will keep + polling the status of running servers with a frequency set by this + interval. + """, + config=True + ) diff --git a/jupyter_server_proxy/manager.py b/jupyter_server_proxy/manager.py index d350591f..7b055459 100644 --- a/jupyter_server_proxy/manager.py +++ b/jupyter_server_proxy/manager.py @@ -1,48 +1,57 @@ """Manager for jupyter server proxy""" -from collections import namedtuple - -from jupyter_server.utils import url_path_join as ujoin -from tornado.ioloop import PeriodicCallback - -from .utils import check_pid - -ServerProxy = namedtuple("ServerProxy", ["name", "url", "cmd", "port", "managed"]) -ServerProxyProc = namedtuple("ServerProxyProc", ["name", "proc"]) +import asyncio +from collections import namedtuple -async def monitor_server_proxy_procs(): - """Perodically monitor the server proxy processes. If user terminates - the process outside of jupyter-server-proxy, we should be able to - capture that and remove proxy app from manager""" - # Get current active apps - procs = manager._list_server_proxy_procs() +from traitlets import List, Int +from traitlets.config import LoggingConfigurable - # Check if all pids are alive - for proc in procs: - exists = check_pid(proc.proc.pid) - if not exists: - await manager.del_server_proxy_app(proc.name) +from jupyter_server.utils import url_path_join as ujoin -class ServerProxyAppManager: +ServerProxy = namedtuple( + "ServerProxy", + [ + "name", + "url", + "cmd", + "port", + "managed", + "unix_socket" + ], + defaults=[""] * 6 +) +ServerProxyProc = namedtuple( + "ServerProxyProc", + [ + "name", + "proc" + ], + defaults=[""] * 2 +) + + +class ServerProxyAppManager(LoggingConfigurable): """ A class for listing and stopping server proxies that are started by jupyter server proxy. """ - def __init__(self): - """Initialize the server proxy manager""" - # List of server proxy apps - self.server_proxy_apps = [] + server_proxy_apps = List( + help="List of server proxy apps" + ) - # List of server proxy app proc objects. For internal use only - self._server_proxy_procs = [] + _server_proxy_procs = List( + help="List of server proxy app proc objects" + ) - # Total number of currently running proxy apps - self.num_active_server_proxy_apps = 0 + num_active_server_proxy_apps = Int( + 0, + help="Total number of currently running proxy apps" + ) - async def add_server_proxy_app(self, name, base_url, cmd, port, proc): + def add_server_proxy_app(self, name, base_url, cmd, port, proc, unix_socket): """Add a launched proxy server to list""" self.num_active_server_proxy_apps += 1 @@ -54,6 +63,7 @@ async def add_server_proxy_app(self, name, base_url, cmd, port, proc): cmd=" ".join(cmd), port=port, managed=True if proc else False, + unix_socket=unix_socket if unix_socket is not None else '' ) ) @@ -65,8 +75,9 @@ async def add_server_proxy_app(self, name, base_url, cmd, port, proc): proc=proc, ) ) + self.log.debug("Server proxy %s added to server proxy manager" % name) - async def del_server_proxy_app(self, name): + def del_server_proxy_app(self, name): """Remove a launched proxy server from list""" self.server_proxy_apps = [ app for app in self.server_proxy_apps if app.name != name @@ -74,15 +85,15 @@ async def del_server_proxy_app(self, name): self._server_proxy_procs = [ app for app in self._server_proxy_procs if app.name != name ] - self.num_active_server_proxy_apps -= 1 + self.num_active_server_proxy_apps = len(self.server_proxy_apps) def get_server_proxy_app(self, name): """Get a given server proxy app""" - return next((app for app in self.server_proxy_apps if app.name == name), {}) + return next((app for app in self.server_proxy_apps if app.name == name), ServerProxy()) def _get_server_proxy_proc(self, name): """Get a given server proxy app""" - return next((app for app in self._server_proxy_procs if app.name == name), {}) + return next((app for app in self._server_proxy_procs if app.name == name), ServerProxyProc()) def list_server_proxy_apps(self): """List all active server proxy apps""" @@ -107,10 +118,13 @@ async def terminate_server_proxy_app(self, name): await app.proc.terminate() # Remove proxy app from list - await self.del_server_proxy_app(name) + self.del_server_proxy_app(name) + + self.log.debug("Server proxy %s removed from server proxy manager" % name) return True except (KeyError, AttributeError): + self.log.warning("Server proxy %s not found in server proxy manager" % name) return None async def terminate_all(self): @@ -118,10 +132,18 @@ async def terminate_all(self): for app in self.server_proxy_apps: await self.terminate_server_proxy_app(app) - -# Create a default manager to keep track of server proxy apps. -manager = ServerProxyAppManager() - -# Create a Periodic call back function to check the status of processes -pc = PeriodicCallback(monitor_server_proxy_procs, 1e4) -pc.start() + async def monitor(self, monitor_interval): + while True: + procs = self._list_server_proxy_procs() + + # Check if processes are running + for proc in procs: + running = proc.proc.running + if not running: + self.log.warning( + "Server proxy %s is not running anymore. " + "Removing from server proxy manager" % proc.name + ) + self.del_server_proxy_app(proc.name) + + await asyncio.sleep(monitor_interval) diff --git a/jupyter_server_proxy/utils.py b/jupyter_server_proxy/utils.py index cf2c57fa..dccace08 100644 --- a/jupyter_server_proxy/utils.py +++ b/jupyter_server_proxy/utils.py @@ -51,13 +51,3 @@ def validate(self, obj, value): return value else: self.error(obj, value) - - -def check_pid(pid): - """Check For the existence of a unix pid""" - try: - os.kill(pid, 0) - except OSError: - return False - else: - return True From 6f30064d10862374fb54f52f5338579134f17a10 Mon Sep 17 00:00:00 2001 From: mahendrapaipuri Date: Fri, 28 Apr 2023 10:25:45 +0200 Subject: [PATCH 12/25] Add OpenAPI spec file An OpenAPI spec file is added to keep track of APIs for better maintainability All APIs are moved into /server-proxy/ subdirectory. Seems like root / is used by jupyter_server and /lab/ is used by jupyterlab_server. Moving all APIs under /server-proxy will future incompatibilities Initialise ServersAPIHandler with manager instead of importing it Use a function to setup API handlers which can be called directly during extension loading --- jupyter_server_proxy/api.py | 71 +++++++++++++++----- jupyter_server_proxy/api.yml | 127 +++++++++++++++++++++++++++++++++++ 2 files changed, 182 insertions(+), 16 deletions(-) create mode 100644 jupyter_server_proxy/api.yml diff --git a/jupyter_server_proxy/api.py b/jupyter_server_proxy/api.py index 445bb3c7..e18b099f 100644 --- a/jupyter_server_proxy/api.py +++ b/jupyter_server_proxy/api.py @@ -4,8 +4,6 @@ from jupyter_server.utils import url_path_join as ujoin from tornado import web -from .manager import manager - class ServersInfoHandler(JupyterHandler): def initialize(self, server_processes): @@ -36,8 +34,9 @@ async def get(self): self.write({"server_processes": data}) -# Took it from JupyterHub LogoHandler -class IconHandler(web.StaticFileHandler): +# IconHandler has been copied from JupyterHub's IconHandler: +# https://github.com/jupyterhub/jupyterhub/blob/4.0.0b2/jupyterhub/handlers/static.py#L22-L31 +class ServersIconHandler(web.StaticFileHandler): """A singular handler for serving the icon.""" def get(self): @@ -52,13 +51,22 @@ def get_absolute_path(cls, root, path): class ServersAPIHandler(JupyterHandler): - """Handler to get metadata or terminate of a given server""" + """Handler to get metadata or terminate of a given server or all servers""" + + def initialize(self, manager): + self.manager = manager @web.authenticated async def delete(self, name): """Delete a server proxy by name""" + if not name: + raise web.HTTPError( + 403, "Please set the name of a running server proxy that " + "user wishes to terminate" + ) + try: - val = await manager.terminate_server_proxy_app(name) + val = await self.manager.terminate_server_proxy_app(name) if val is None: raise Exception( f"Proxy {name} not found. Are you sure the {name} " @@ -73,17 +81,48 @@ async def delete(self, name): @web.authenticated async def get(self, name): """Get meta data of a running server proxy""" - app = manager.get_server_proxy_app(name) + if name: + apps = self.manager.get_server_proxy_app(name)._asdict() + # If no server proxy found this will be a dict with empty values + if not apps['name']: + raise web.HTTPError( + 404, f"Server proxy {name} not found" + ) + else: + apps = [app._asdict() for app in self.manager.list_server_proxy_apps()] + self.set_status(200) - self.finish(json.dumps(app._asdict())) + self.finish(json.dumps(apps)) -class ListServersAPIHandler(JupyterHandler): - """Handler to list all running server proxies""" +def setup_api_handlers(web_app, manager, server_processes): + base_url = web_app.settings["base_url"] + + # Make a list of icon handlers + icon_handlers = [] + for sp in server_processes: + if sp.launcher_entry.enabled and sp.launcher_entry.icon_path: + icon_handlers.append( + ( + ujoin(base_url, f"server-proxy/icon/{sp.name}"), + ServersIconHandler, + {"path": sp.launcher_entry.icon_path}, + ) + ) + + web_app.add_handlers( + ".*", + [ + ( + ujoin(base_url, "server-proxy/api/servers-info"), + ServersInfoHandler, + {"server_processes": server_processes}, + ), + ( + ujoin(base_url, r"server-proxy/api/servers/(?P.*)"), + ServersAPIHandler, + {"manager": manager} + ), + ] + icon_handlers + ) - @web.authenticated - async def get(self): - """list running servers""" - apps = manager.list_server_proxy_apps() - self.set_status(200) - self.finish(json.dumps([app._asdict() for app in apps])) diff --git a/jupyter_server_proxy/api.yml b/jupyter_server_proxy/api.yml new file mode 100644 index 00000000..91bc45fb --- /dev/null +++ b/jupyter_server_proxy/api.yml @@ -0,0 +1,127 @@ +openapi: "3.1.0" +info: + title: Jupyter Server Proxy + description: The REST API for Jupyter Server Proxy Package + version: 4.0.0 + license: + name: BSD-3-Clause + +paths: + /server-proxy/api/servers-info: + get: + summary: Get List of Server Proxy Entrypoints + description: | + Gets the list of server proxy entrypoints and their launcher meta data + responses: + "200": + description: Server Proxy Entrypoints + content: + application/json: + schema: + properties: + server_processes: + type: array + description: list of server proxy entrypoints + items: + $ref: "#/components/schemas/ListEntryPoints" + + /server-proxy/icon/{server_name}: + get: + summary: Get Icon of Server Proxy Application + description: | + Gets the icon of server proxy application + responses: + "200": + description: Server Proxy Application's Icon + content: + image/*: + schema: + type: string + description: encoded string of server proxy application's icon image + format: binary + + /server-proxy/api/servers/: + get: + summary: Get Currently Running Server Proxy Applications + description: | + Gets the list of all running server proxy applications along with their metadata + responses: + "200": + description: Active Server Proxy Applications + content: + application/json: + schema: + type: array + description: list of running server proxy applications and their metadata + items: + $ref: "#/components/schemas/Server" + + /server-proxy/api/servers/{server_name}: + parameters: + - name: server_name + description: Server Proxy Application Name + in: path + required: true + schema: + type: string + get: + summary: Get Metadata of a given Server Proxy Application + description: | + Gets metadata for a given server proxy application + responses: + "200": + description: Metadata of Server Proxy Application + content: + application/json: + schema: + $ref: "#/components/schemas/Server" + "404": + description: Server proxy application not found + delete: + summary: Delete a Managed Server Proxy Application + description: | + Terminates and deletes a given managed server proxy application + responses: + "204": + description: Succesfully terminated server proxy application + "403": + description: Forbidden. No server proxy application name set + "404": + description: Server proxy application not found + +components: + schemas: + ListEntryPoints: + type: object + properties: + name: + type: string + launcher_entry: + type: object + properties: + enabled: + type: string + title: + type: string + path_info: + type: string + icon_url: + type: string + new_browser_tab: + type: string + Server: + type: object + properties: + name: + type: string + url: + type: string + cmd: + type: string + port: + type: string + managed: + type: string + unix_socket: + type: string + From f69a67aca5f1edc7c7f4d2d887ce9913baa15100 Mon Sep 17 00:00:00 2001 From: mahendrapaipuri Date: Fri, 28 Apr 2023 10:28:26 +0200 Subject: [PATCH 13/25] Instantiate manager during extension loading We add manager as a traitlet to ServerApp and add monitor as a callback to ServerApp IO lopp Simplify API handlers setup --- jupyter_server_proxy/__init__.py | 60 +++++++++++++++----------------- jupyter_server_proxy/handlers.py | 11 +++--- 2 files changed, 33 insertions(+), 38 deletions(-) diff --git a/jupyter_server_proxy/__init__.py b/jupyter_server_proxy/__init__.py index b57f718b..2ace07b9 100644 --- a/jupyter_server_proxy/__init__.py +++ b/jupyter_server_proxy/__init__.py @@ -1,14 +1,10 @@ -from jupyter_server.utils import url_path_join as ujoin - -from .api import ( - IconHandler, - ListServersAPIHandler, - ServersAPIHandler, - ServersInfoHandler, -) +import traitlets + +from .manager import ServerProxyAppManager from .config import ServerProxy as ServerProxyConfig from .config import get_entrypoint_server_processes, make_handlers, make_server_process from .handlers import setup_handlers +from .api import setup_api_handlers # Jupyter Extension points @@ -43,14 +39,33 @@ def _jupyter_labextension_paths(): def _load_jupyter_server_extension(nbapp): # Set up handlers picked up via config base_url = nbapp.web_app.settings["base_url"] + + # Add server_proxy_manager trait to ServerApp and Instantiate a manager + nbapp.add_traits( + server_proxy_manager=traitlets.Instance(ServerProxyAppManager) + ) + manager = nbapp.server_proxy_manager = ServerProxyAppManager() serverproxy_config = ServerProxyConfig(parent=nbapp) + # Add a long running background task that monitors the running proxies + try: + nbapp.io_loop.call_later( + serverproxy_config.monitor_interval, + manager.monitor, + serverproxy_config.monitor_interval + ) + except AttributeError: + nbapp.log.debug( + "[jupyter-server-proxy] Server proxy manager is only supportted " + "for Notebook >= 7", + ) + server_processes = [ make_server_process(name, server_process_config, serverproxy_config) for name, server_process_config in serverproxy_config.servers.items() ] server_processes += get_entrypoint_server_processes(serverproxy_config) - server_handlers = make_handlers(base_url, server_processes) + server_handlers = make_handlers(base_url, manager, server_processes) nbapp.web_app.add_handlers(".*", server_handlers) # Set up default non-server handler @@ -59,29 +74,10 @@ def _load_jupyter_server_extension(nbapp): serverproxy_config, ) - icon_handlers = [] - for sp in server_processes: - if sp.launcher_entry.enabled and sp.launcher_entry.icon_path: - icon_handlers.append( - ( - ujoin(base_url, f"server-proxy/icon/{sp.name}"), - IconHandler, - {"path": sp.launcher_entry.icon_path}, - ) - ) - - nbapp.web_app.add_handlers( - ".*", - [ - ( - ujoin(base_url, "api/server-proxy/servers-info"), - ServersInfoHandler, - {"server_processes": server_processes}, - ), - (ujoin(base_url, r"api/server-proxy"), ListServersAPIHandler), - (ujoin(base_url, r"api/server-proxy/(?P.*)"), ServersAPIHandler), - ] - + icon_handlers, + setup_api_handlers( + nbapp.web_app, + manager, + server_processes, ) nbapp.log.debug( diff --git a/jupyter_server_proxy/handlers.py b/jupyter_server_proxy/handlers.py index 94b4d0ab..79e3c426 100644 --- a/jupyter_server_proxy/handlers.py +++ b/jupyter_server_proxy/handlers.py @@ -20,7 +20,6 @@ from traitlets import Bytes, Dict, Instance, Integer, Unicode, Union, default, observe from traitlets.traitlets import HasTraits -from .manager import manager from .unixsock import UnixResolver from .utils import call_with_asked_args from .websocket import WebSocketHandlerMixin, pingable_ws_connect @@ -712,8 +711,9 @@ def __init__(self, *args, **kwargs): self.command = list() super().__init__(*args, **kwargs) - def initialize(self, state): + def initialize(self, state, manager): self.state = state + self.manager = manager if "proc_lock" not in state: state["proc_lock"] = Lock() @@ -803,7 +803,7 @@ async def ensure_process(self): async with self.state["proc_lock"]: # If the server process is terminated via Runningsessions or killed # outside of jsp, we should be able to restart the process. If - # process is not in running stated, remove proc object and restart + # process is not in running state, remove proc object and restart # the process if "proc" in self.state: if not self.state["proc"].running: @@ -846,9 +846,8 @@ async def ensure_process(self): raise web.HTTPError(500, f"could not start {self.name} in time") # If process started succesfully, add it to manager - # Add the server proxy app to manager - await manager.add_server_proxy_app( - self.name, self.base_url, cmd, self.port, proc + self.manager.add_server_proxy_app( + self.name, self.base_url, cmd, self.port, proc, self.unix_socket ) except: # Make sure we remove proc from state in any error condition From 97ce111c055534927f5b012e66aad5e85a09d21b Mon Sep 17 00:00:00 2001 From: mahendrapaipuri Date: Fri, 28 Apr 2023 10:29:22 +0200 Subject: [PATCH 14/25] Add OpenAPI spec file to docs to show REST API --- docs/requirements.txt | 1 + docs/source/api.rst | 8 ++++++++ docs/source/conf.py | 3 ++- docs/source/index.md | 1 + 4 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 docs/source/api.rst diff --git a/docs/requirements.txt b/docs/requirements.txt index 63f4be10..079fe1d7 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -2,5 +2,6 @@ myst-parser sphinx-autobuild sphinx-book-theme sphinx-copybutton +sphinxcontrib-openapi sphinxext-opengraph sphinxext-rediraffe diff --git a/docs/source/api.rst b/docs/source/api.rst new file mode 100644 index 00000000..c3b4e58e --- /dev/null +++ b/docs/source/api.rst @@ -0,0 +1,8 @@ +.. _api: + +============= +REST API +============= + +.. openapi:: ../../jupyter_server_proxy/api.yml + :examples: \ No newline at end of file diff --git a/docs/source/conf.py b/docs/source/conf.py index 97320dee..2a29182b 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -23,9 +23,10 @@ "sphinx_copybutton", "sphinxext.opengraph", "sphinxext.rediraffe", + "sphinxcontrib.openapi", ] root_doc = "index" -source_suffix = [".md"] +source_suffix = [".md", ".rst"] exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] diff --git a/docs/source/index.md b/docs/source/index.md index 1ece9d14..0f6ccdb7 100644 --- a/docs/source/index.md +++ b/docs/source/index.md @@ -30,6 +30,7 @@ install server-process launchers arbitrary-ports-hosts +api ``` ## Convenience packages for popular applications From dafd67114a87d319e1cdfbf6051efe5d85eae504 Mon Sep 17 00:00:00 2001 From: mahendrapaipuri Date: Fri, 28 Apr 2023 10:29:47 +0200 Subject: [PATCH 15/25] Add note about manager in the Lab UI in docs --- .../_static/images/labextension-manager.gif | Bin 0 -> 300662 bytes docs/source/launchers.md | 23 ++++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 docs/source/_static/images/labextension-manager.gif diff --git a/docs/source/_static/images/labextension-manager.gif b/docs/source/_static/images/labextension-manager.gif new file mode 100644 index 0000000000000000000000000000000000000000..6c64ecc54e7949413b6800105b27df4bbeda694a GIT binary patch literal 300662 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 z zWmxi6+(}KLicKM4)CC9_01em!`l3Hy-3Xo_S(9yrC1_clpsX1HFJ%<~1o#B}1=)=i zJ%rfW?>YyYUEs%M0`Su`%S{T&69Di!2zQtP1ULYxt+(Ahh6iBQX>|jvB{O(MVecX{ zIFqwSs57xp0_{}@ejS4$7=kL`9wo>H4km(pu!mkK+aajoH>=)@7=j=!i+`OA??qhj zU5FW_XyBJg1*=3e)hVz-coWk`lhu!KZN1VngdN4NxJScZIJi!ruh^NkE- zR%T{)W@wgXYL1L+RtRIpVlB=RDA+i&80Um2XEQ!$EJ^3KVCOJdTuW7oeZxUyMF-ZU zT#|LX!F#q|HGv9v+~cYQb7Nh2kUR#?xd+JH(|rM#VCetHeO=R?U9WQh6F2}ZgSXM$ zLxjiz19(;h4&DZS=!D+6*ZsM$AO<8D0$Si6UN8dgA%hyuWj6d}UY_1xJ_sZbW=kZ_ z`smxYFo#S~1f*_eZ$<<^-~&5&YD8EDHm+iFF5|Q?YNS>KrG93nc50}WYPX>0KC)`E zIAeIWk{qxFYi{GJ&gQf*>$BcswO$Lc{t|l@Po@Z0J$QvBZ~&FHS>n>R3qELs(Bu^B zt3J5Aa#QH*ng9@UXozlLiGGPIaO^=N+CwM>q#ZQhN{0tCXwD{Gg?L(2XoXYoYt4;p z$WCa_1HFb8i*ZnbD4?6?wFjF%2%M%~>$QiS_T~TG)-+otL}G3acSZ|&u!OAM>Su21 zr+(@?xC3PfYqoA@vNnt3PVQ#r>ZbN;J9zHsp6BT%Q@2J7xQ=TanCrBt>*6kJ^S%=F zMvJ=klDoE5rodGKm;gXfElGB~!G75i>xlyhT~2m@A94xC7HQmF?T98_1c$f2w$~;w zfWPW1(e5qLPKaTE0F`CKbcJA)KHU#b2=!w>_cM!P7y^8-huXevTo&eW5Q5$A?a}V( zVNhb>7V0mp?y^7zq(%mFScau$Zs&gPJD>v@RtxpkakAiXMeuPT*KY3a?&q$9BTw(} z9_#ToXDh)1Zs>9^_i}FlhFYKkp4t*5FV+7P#qzv;=PmzoHxKhMFY_zm5-X=_iN%Uf zjAP-kQ4=Btakz(fP+r>Z*Mng3F@OTPVFMf3Z8ex}w}o+gcn4i5;*%N!>+OY@9)~Pg z;^ZSw1zB&iIOF_2!oHB42gzwsY}b^Rj?z zT5t++paLv$f-?tIZ02?Lwvu0OieMl1Vvk}wnQx1IDIB(eA%KD*c~}HQh;oJZ_kR!gG{<;kH;a~kd3%U?rsj7d_xCqoc)CXO z^4)o{koc#d2YOO=kk9#*4-2Mu3a8(AkT+E*k#_ja)2yKPj6Vyl&hBSEbyUBDI#6{w zDEffs+jN)Y&U&@F2p33Kud&sO-+2I&|o~sraT*!;Bges$)0r-o1q& z1rpq7lA$*oDsj*{S#YIFdM;<$>-aIG!jS`M@=Uq1rAwGNWb!m=6J*h(N|!Qq<@Bkm zbyBBNt!niuRZv*Ba;@r$DcG=L$C52;wk%YrXV(_3caP-Sa>y~kOsKP@S-EuWCc=wIFWt#~pebL>#do}yzEcl|m!l2cp2}jx^Qrf7ucmE##@_6!h%nSa!zWsYQiBub8Y~|M4 zM!>*iglzCNhR|cd9q5vRD>3++gV$-uQ+3OExK45(9@pH4C7O65iUw6jVv1!&NS}9# zfLEDinCUmsd4r{A7<*~>=nh%HK}cX(+{HMfW%y<0S423T=U3v#8}sTHj)=u zj_4H@B^qe_G9{Rf@i~^CF;Z4zeoXGy*^@gWswksr4msbIKb@q7P{5%1;6X2@M&?GO z!elCjjq15!n{jdlr>$4Hq7JXU`uZ!d!3sMpvBesDEV9WeyDYQKI{R$1dCt02c4C5M ziKKz%XNO-BVVWr~KKhs_8fkQjszIuXg)N}kX2vZ<-WG~pxZ;v)?pQ*m`e>-EZ6ppA zLODUyTKcBT@0eKr8!$`*pE_-cwdQ&it`jo{F~%9&DXqpcRZCVm+0v-wlJPoOC`RI@ zL2emkj28cv!3C)cR>D&D)@o}6RK_9oi$%V5Q9&&+4#hDA3?E4?%@wP2BhP&fp0 z+t5Xub@b9%chmIKQBVDF$I(e_aaLsO8aCQ#OUJR=8Ck7Vq&Jg1=*~GR^Uk;|mmx+m z`BLd9s9?qIZplr4Yd792>&>^{vhEwW(NRl2`AW6~S2eEp07B|G7CAAVl$yR-Z| z@WP9QIpkM!e!b_PJAQEWW#x9g;kBZaUehlfORu3boGu+eux5B+i9Gg;ym}0%d-9{-+-i5fdA&h@{97CV1vov>70+7M1E1ua;0@;-g@9p_ z5C${2LE#LoeMvE&+0bV(^&tcsqW}dAV+cc0#BdbVFd+`>^uG1QuP8GbnV@ubyNWOZ z8S}u~;=-WBFol(wnQcl4_z|L+yyb110y&=3t!_Pd^`v^ zD0(l1I2dCY&qzWF=J150BOO&-fX9nv8;uWJ91vUt|ka75+8EZLYBE_=EU2d#8b&MoUl4l1# zn1deS_(d$_Z}oiv}#peN6c-Zh8}(ps5sksL>wNOz4^(={juAnw730O1$G_IzRb^CbE-w++(BsPTH50vh+Vb^B*hs35J>W^L76mOF&aZJ)9yH zmuu1`NE@=yi8VA=4=qSFL~+cma&@bsc!fg7Q3C@^fEn(Xf&&tuhjiE@3vd7E0a-Os zkFz=e0fCr?J;u=k2TWkCZOy|JHedxeEa3nX$OHk2!Gi{9;1Y=h2M-QlSG;Bcup2nX zEF^#cJ=B1aQEib(S4UEpWTJk3Q3gF!+S8W)6sVn4=NG)7hN4!GETv809Z;*Sx_|M_^XfR#&yh4POCBK`sOo6t$>5ny`Uq zkb?$tfJYV(AXhF>KpI=1z%#^AgGkVW32vAMIckuHcx2(Q^4Nj_7GaKAIG_{bfWaTk zA&dtwvI%cUhd5{;iF))m6Y|(a1vJ5qCK$l8(LGUURcA}FECaW9kVpSGR_oGpYEh_P zh^-f-2Q%f4_9P{y9Yj#9VwJJDlb)0*jAbk|8XLE%27M}#nHv`8o~VN#R_Jv1_)B1} zP#> z1U&45&IRehdD|fmd$eN)d7vdk=(o&5w9wR{IDr+R7 z8kwjRF5o~3B5>$%CsGEKC0b2P|N4njA<4dj5coas#ObyGmyO~^x^rSWGukOKylAmGy}JZ&a0 z`oQ~UIduFTI)HB^6FGwj!Bwqsn7SC$uTDY|c=2%46uBs7FI?Jvnm1Oro#AmmSE+BA z@{+f6xgrM(lT8F&e!JXOFsHJW?V;U34pJfBP4*6SZr}fDh@%I2K=04dEQJkB;~hmu z#|_+2j{NR-=>WFGg$sP(1TUBqIQK+7gb@KeXg!e_*2FrdaDcCa-8o`!8`(8t4*TVu zwR`9Lk30T$xF_KqfLDmb!?OHAHNUseU-7vhc?Am9_@SVCI))zSf-Ue#5$<<(yG6Y@_ zFn}JMf&*~E($T}U6+j>K0P(%wZ5*H4B%eksg96b3MmXQ|{SU{znBT!2F$9QB6-4%h z#WFC7M5NuS^-fSJ9FWam64c<_#nP&vUz4ey;Q9Z{LAf6ZQU|b|Ps|mYNg!JkW?@E{ zVA`Od@;L$@cne0X8rwzRP{A1WB|#XB+V%Oc$h^APL5wo6O%jWW$GC*F3yJH9X=Yra~$};v;gG7GB~)a3R`wAy2fH?+kMJBFarOhFm?mh{2h8K6<&?W0TdW2-S- zK+e%X)*Z^hA~$+t`Mu&cs+=qqA1!_eI(CIRvg0ml1U$y03a9`*+T%T9Vm@k2Hm=3< z_!w}ZAwUWwArjEGS$rRGc>x%N13mP@ODbec0;O1JWyf{pSB7Oyj-`?Xr9)CA zami#xh~tJVp08b`hae?YY-CgRWnVUwR8pm2N*8fl6Iq6Z8nl=lewr8ffATW(>Mr65ysi=%!=_9>0Mm*p23sm8No>rc|V+YBnWnx~6qj zWntzJR+hy$9OU$QfmebhtZ^pYd1hF2=VICE| zbe;fU4x4}eCx8a1fDWivSfvSVXIT_O^vPiOrQzQlC-%uEQz&Rl5@8z39fSg6a?0n2 zp`uBeBiZ1mRAj>~?x$0N0#tehfvzZvwy2A~Xq6dg`}n3wj6)gNrh`&n9zMrFnWtFT zD30c+8nPjIrXr*%C(F@iblLx>afql?sDp|2B?{;Yj8>_YUMZIDqKv|igoZ^puz?r| zX&CSVk!C1DAm~|qsTee<8j`7WAnArKq>tv1l71GGb_D=*g$hJzQ!1j0W+|Wss-U_k zY+?+J(!@K2LpTtFq7uxTa_Dm;>Y_HPhQO(>d8jKUDLGmsh+2%3<|m)7<0w#RS5!kN ztN^N}s;Z)DD7?a;zACK7YN0~Rp{9+c9x0@L7N*vgrV=Hm8YPwCXEvn5DDdeDSc5fS zMO1W!t2$yetb#gV#R`zkoK3@k(m*9p857uno3H~DFhD0X0Uoe}3`jz(2I#C#ORb{K zt_oksuFopJ?!>gS%WrkogutA@fVghD7(gS6&~75u?ERO^5q!!u|r zw{}GjctTYe!vl1}ItYU@tV6k;D}bi!tgLI;v}=dHD@esF4$dq4)GJ8c>sI8eUnZ+M zlqg3=rN1giDA>w|$w9(ifVFaLS8QunumiSEtOYm&Rcx%!#@v?j5XvqENIdP+y2aBz zs+_{7i$E>bPHl9EY;V8s<|Kx9q8 zC9uN`JS>K>12!MA@Uv=;1DT!8`ffi@7B zC$LvJtOFCsfjz8(IjF-fI6xhk!!ZkLdlG5~BqlfSS-54eHiPQ~P| zL*-W8cvYRb4sD+;!wmcZJ4{8@378dBKr<}xC1k@3J44m|fi~3ewl3HutOLb5LN*YX z#SSf17(>yn1MrsrYktAO1V_N`#%eX3fU8c$E1W>`YH4;}Wz+J+NPO@2YOQ-Br*L$! z_pXEYg7GzK?{bzeHJNN7cIw24Xf`0LHTY?C(s5TTYyGYRzzXaYSH-mMF9q~$0UPlr zRGlZ-g9oU>JYbm7R&Jj)EE03Dp-Bb06|M*KEC|DZD53L4k0DMt}I8=iU zCfKt7Tv1oWQCnBd-tV(AuT=!HRq$_u0k980fC6-aReOMJdq6eFgF5`P1XHjD=R;TY`c;fG5bXI@B=53fL@MG|Xm2)UNAFMH3uAc^h^Tn=9)Y7U3rCOG9>gr-fn8DGs@J8^gCy|D=zb&^Q18 zYB&e$I5Vrw%587kb>6arfgdk7NN zv}Y+Z+exHP3$73OnOpj{W4f;cD|Ib7SEvKBqW}sZyRsh#vp2hJKf7H|JG@u_X|>nM zd{cUTCq%vHdbjtwk-Dp|hV8G*pSZv1y0g0ntUyh~yTZ5Vywgd(XM`>|fB*;p0c?OX zD1E5O7HzyhSgLYzQBD8M=-#XYQmMqi#mqm%Z$h&e@}VkuQYWds_uiL+07TJJbLqL;wQx z!7h{l0E)ElqgC<313k<@<|;rR$UQSfL!)^a$z$VT?!7@QKr5&KDHKE~fWip~KnL7| z0;EC*tb9Sd0tYC-l-ffotbhk7K-eEdDD=ENtbpSeggO9(0DA+ibx?p}oxOquJZP(? zKmn^WpWWbl8 zL4-<~ia^nrtx}8{Lzgyv8g**bt4q6b{o2(!*|TZawtbs6DBQbw*N)vAcyQkABzrJ^ z+&D;W7lk)>{v3LA>C>rKmkk>>Ywg>)o33~7VhMvFDyw1kmOc5e7oa z9)T)D!JcXaxJMle?^%JwR0P20o)bdhNF7%?xUeF1L|mu;fTZlQrvp{)0YwFSMsW%? z2+`t5E%63oK>=;%IYGAqHd?_c5t2fI1KAYlii5sXS>PTQVU)2V6hOI06$Rp2VV-OX zU?;>6g(R|~Fzp%h9&H4WXp|AWs`5%CX&kK{wwh#%gAwLYX8~6_{4gmI4=VA*6k%l1 z9xlD?s-p#7nP&x+E;SLw0<>AtrV}W;)S44{^id@d)-(|`wv=>`*Is=+4LjGY!%eqg zZKLeh?e0kkIpmbeDA{PGotD~aD+<;%YqeeNyYPOskAMOQ(7+jc%%JazJnEURz6;K= zN0&h4p`nw3vY;=9O$L&oUW&%)!GS)|@pV@Ty{%CHfC2=_%bs}(c+|~0D+5)5bxJAH zo^=FDC4_-|;)oOmMo1_}5m)h0DFNoOm1H9gHLYMKCv?)D6+{^bn+5FQ${q%I(z4T= zJAEbRiIC#hW5Xs=d7xJ2S!W|{3Xn`hmN_b#Qa|UhN1F}?-MK5DbwyOsnHfcvl#?D# zaUe@6wisiLgB}QlS17V(tc>mca-fQr%@`CCU7fh^bxtlz0CgT7NQ7L;f-uo-CEpfX zV{JRYa^EHo4ZJ1yJkB1BHXohz(tpJkS<^>f)YtMFoTwNKrlHp!cHA>EG4=BVo(e%V4aV9Qd%VL5g%xjqdFL$9c@m~ zDXAk%IrAldNRSBykIudO12c6$)n3`QZ2aDXTFnAG#4*S5kH&UzEU9tv8a zKL|>&6(pia#a>7f4|rxWm7!B*))Bt(jjtLy8^{V$aYIl%4tc7RVs|Q+x!G*MinB?b z6vLAZFQUXa8B5+2$yi3yq0Wrka-V}-haee10t3;g9er*GuRxfi2K~AhJb<^ae%XT< zgm{-dm>`McouzCCV;+tuLW&iP094ceD_jdRwlTwKk8U_pg`i57w236;LiZqr*VLD! zD3*|BHoFM}xW<7{%;8}8M({js9qLd=nQQ{Jdma>_(n4cGqqWBIw6P*rP(U8yF~|D& z&W`Bl!hrr0py2&+95C=iJ%|y3KRSqg$}?Mq82KQGENvq^Bw{wARI?Ucl6!#z$p~O+ z5fP=x8uti=hYDb~R_JULLOV_WN;|UBiL5|@Q+i_0ns>jhoaDY1?8-y>-x( zC!FeLRwptK1-N2JrZQ|QL;#)1qM*%ACXaqa~&B!{SMOxqfHZ=%7@0fxE zCeR3Y*rSj35Z*+D5dj5gKob@16;lUNT((rTA|1fkQ@kou9{wdT+w<5|%c}y2tYC~f znuv`GAcBy*0x}?a$T*k(s@L{8b%{SY$kuA6M0=D%0Wq1S-L!$25L_si6=6pLJJ6Lj zN$@A^%}{`oLXiq9;KE!}W?h%)U?`6Ez>3UMC>)>wL#eld?KP|l0kfIKV)c_A=&xc7 zyD|lgpcM*u#o*kV5dQj?GYYU(Xf0yk;Y!i8ncF8nS4)7!_NTIv!tKU>YFd z?UiTX9&MFpkXLq)aOJEl1XC+^as)1UF?8qcj6j5S9vh5RD&v{~vm$j7G+Pw?Oe;6l z&=cVR&Flf_MX!d>m)2I6Er&%epQbCLc*T+MI*~GKhYNZ@fe2VVf)TJ9*0GlLtZ7|q zTi+Vjxz_crdEIOOU;i4|!4~$giCt`C9~;@pR`#-)jqJ||R3wO=$U0!6X`)oy(S(L{ zUnNa5sg2RvFOE>SC&y`HVeZqWv8`!drX3%YAOixB_q^#{Z+qVx-}%<}zWLp6f7g2e z1NisA30`o69~|KcSNOsi-f)LM9O4m|_`)+F7n)(iN$e;LxkI<{((u{5u3)fV6d-Mm z4|?1ux0bnwRje#_nIqK@#gS-UbDE1H&=uK&8+dSl3234oH;6={$}@$WOdRP+@3+8} z-gKuw9qLh+`qZgj^@C>s1(a4fcR=pvt#2pOU+)#mt>bbl!n`6~A)1&+UJ?50+yIL( z$1IdJlszy9@y{}tZ2cIx3Op4$$hy*6yW zXU9MObiqpP<4}0rhAN$$Y{`RvkdnRcIJQuP? z|4svL(?JuJoCE(n#7}S5=YKwa*K z`w~zA7mxuP&;bw5`))!6LSZaYVF4Cktiq`PoT3^~XaYR|8_o$bNTeX*tC2+E1n>*0 zg2ZG07@z=7;88jN1qy%zkR=otfB}SNFgz|5To4AMOaw{|{bVN6wBZAL;sP^pAU3c> zJdgrIklG4>1T~{&27(k&Pz8@d6yeSn{t#R4B~Gj4U{B`QlC2=BamwfpcL*Q ziPT{pMt~F?W*($QB}QQdq$UzWP$4{IR_x(N8Y#l;0R+rAyY~OL!z3DzPs@z$spV0oqP9G9q&J%FjGe5*L6HEfEtn(MUQG67K;N$1h|5 zdV~~J!W5Gt6@iKueeo4zaUEu{7H`p0bg>$~aRh!b7>7}`?%^2SkXPVvI^@tg=rGhU zPxDCe|2AL=s4Ei^g&t&}4F;+D29N1pFCq8s5E;=S9}*&0&k?mDVpIT0%n58(0TkTC zf+!Ma#=;s8CuQ1>6ts~6N^B$?rWMdaM;Pn`R^bG;P=!1M1$1y!3ScG6VsYAHwT7@F z_=?gzGA=H1EVx1=Ir4%crW!p&BwMnPOcEtQK_$`R6gSJ`m&vBq4GRG9vqKAq%b{=ThnF5-;~M_JY!WL|_5`j>to1 zP6eu=11NGG=8Bj=(nE~sh}dBTB1jz^scnv^17_qEgiI(1h!jV`14^tQI;DoPDl=bD z9ju`MEHmedG35%4+Y}Qp1rrrVp)e1#Via>JjYNFNCo(6EGDE~NF;g=yB4Rr8Gc!Xp zy{R;X0yS0h9pO(bSA#60!z`(T4(H(&szE!oGZj){JGB!JIf5;(Ya-}D7)Wm-2njZWTzO^#t?*0hH<{(tZ{z zF-t@QI)GF_VJvWyG3AROD&jX4V2A`NPBKOXR=_Er0wzZ@D@U_#K6D-bLNptYiED@v z_xzG>M)N_dAwnm#LhUCv-AO|YgG0-L5+!3rSD-2f;w4YOL}?O5M@2|k)J3J_IiqtP z>o1F{b2{kI%aE@$f&m%CYaq;^0TzKg3(?{1Q{hk`J_P_i5sm;<02@9+828N@SU>>K zl;Bo?-{$n`0!~5uO#$YC1I)DE>{Qk*4Fp1f z-~N;X1i(-iZcZb0;TRxM9j;ILtyH6qLH&XjSH>yK%4=Go0B*)Y6{2G{Yp8m3iB1Bj zupvrTLdbyb*EN1mt}gq2u%>sXU>XwcPHf#g}EwJh3DN~M!Zt;kBH z<2t`nJ5S&Q1hyK8ku}s);RN;I2tXY=K;G6A;fAUJ7{C=+LEj`a0UQ=$|Lp@_F=OW; z0iy0y_014Wgkj~4W998*6;3GVQ{E<(-&&zl^X+5>pk)_MV;ybfB1d0T71_iQV9STC;7(f*;0>I=g8&qH$;v@y^4HRM}-$)@9 z2;g?rfdm9V1$fsQrk4OlK^+dO1w0@dP$V1bR2$ZT9ZF%|oL5cN0R*Ik02tsE$afS7 zKmb0o7Ta}x)3JONCIog#-clfZ1t1%^!dN$u5-Cyu9Ef}64F$%xeCG`tT7Y`3fj`&b z6*e|{cNcuAchtx?78GVpi}!f5A$d{298knXfLD0`7ogrccN94I1$ji?q(X)F%@smn z-n2mlG`JLMv4YjKXBJ=`pn?FZ!9eMae%HZ%Q5ZXCICD2w046m7tf7djw|A%a5(CO+ zu2zf9SKc1jdoy?yOhM$-K}{&Jf(IZ!vu++-VTS+2Mb@;2Z#aip*o993hNI#h_UTbn zK>*wre&tsMSm9S)VNe;EixD^*Aoz(_AQt9f6z;(TRF)kQm5Q&Icm)_!7gXyKiXB)< zTVnLe)WMUjMR6CmH5%7)9v3>WRC3hQ8iX+cK7e{_h5+^#8!~_ZRDqBefK&+J1I(d4 zDT{TrAp>G{9T=c`-AOV_4ezQRvK7at=L}BEu07jsA z^9_4IAYq(Y07PIOK!5;1L2>*mJ~Kdt`Br6(wU0ZtoYizHfZ}acp<=UPb!9GL%$Wh$ z0Tf=s8e-vq`Bwn^mv}{30kXGiQgdO}VFhY=9z1{m?3n>NHUwa}NDewmY`B>_mY(IU zNDQF>GC&(f!H45ho4MJW1>j}nts1KNnzccG=PjjIx}^zVoCRPM2$|pFSl)1WmPMgr zdGDngT9iRSYU)j(3&5PI*_yFg0A85^V)=t zcHTA`Q#-n%FFFJ=`ia%S6kPBXikNTvYpmT_VZ0~>BD(4vk)b+yTR{2tM!8x_*_81K zm5oJ}pM#aRMJ_);tLW`lcbc&qI(%8+e+2+_^DP?;L1oDpd-kpq&$$EM(;gz3rgbC( zc3S`v;2tpbx0`uW1>hAlA{HR~-dX?~z!{xYA*?rA1U_IMxOU!7ngFyx6}nsA$orWy z*_LKF6hO-w{1c^z8wP8cp&c54`#N!Qc-~l`J>NEd<(s|*AQh%~9d>#YFt()SZLNQG z-kSS^SNEJZwWh0khOs-7Q5t0bIn_@@!9*N(RB6~0c)_(nkzY0xrkQ#d+}>Ju-nLdUuJp#qZ%DP2o`liX99z*x6y&AEsjkbrgK|)-!y; z?YOC1T_7N~rXzjcR-L!gU1Nsv1Ne5QZ5n)FDOTcG;E$~P9Op10boF4XTaX!wF!L1%LxGdLH8Q)=m8t&-}U} z8Gw`5)pK0l#v)Ds>uv2J`_@}w;_L0P_;{_sS$ox9WK*7HD+AwB08oYdz2nCN?tuUV z&Jg}rgyUV23w74*jerG!1(-L}=dIkG+v8DM#sz@c*WK>heY$mA$bp>hc{=r1UjS5L z6jp(Y`EAtVeU2Br^d;66f*1Al((xn6y0O~>x}M%vp%uDhQ~}@KE?(b4|GlBSh*@cd zM}EpN=apK33Bukofg)*h9bu3%D zds1%V9OkP1k3;-^n_ z+9n7PwT^*LSJqhlj4FV@0()Wwh+yZl8mj~aQduJ)AX|!`_Bt>)Q9u9#RaSq35+z{N zDy?VvT>=C(R8gyt0@^&5Jr>zyc)79|Ui?k>6L)xR=o16>EZ|cqs*u+TU9VJ=*kV2{ zP>)#t0Vtq+;f*K2efjy6U~{LX_7rOb?gt%S(kaKBa{{pV69@BD!va%~Nk*A@Cyq&G znP&PVLPk5S$!13oNkq{>7Gcz8opvhprbu`0$)`dprF2uEH5npkp*Z!aQBV6#@X9>% zWcQ>}OgW{VHuGR(9Rb>avR^i#Sc=UEKb^qSPb)waLTPBiC4d5~tiw(#Fp7zuUn^bo zN(TF_@(Ka!DC*Ox?1Ugd3zn|)9|-uV1ZGq=*jOV9aI^|g04Wh^;~)v>UW(kb>QvjoJnR}(n1KQaOKd98 z5}<-Lq`*o_V5$mNt9q2S`f98&YRYND53>_7P&l61;w#x$lS(xM7Mxva9?S#609U%Q zU%2Czi!rRS`oyTC*%8x1kAr0LDEx`G^N0KI$}bOknw&r15abmt4t@0rNpF4j z4N<;Q=7R3yy`YY0KpQjZA>a4K)0KS_K|B3I9>3;kqKe;^qKZ&Bw()R!FI_9Zr zOpcj{>G-2RpYYFr=Bpn7f0w2>Y0rB1z@9-AhY<-*5PB61$p$}&5blL9P(B#pO~#iH zY>WaFENr0*S?C{MAPE`8Q%!UILc9u zX_N>E??@04ny@A`*`rKQh>&X7qZ-)2MxziS3K?Q?k&JAlBOeLLNJ?^&j15DZ?koa}6;JKx#PbJ}s2cP@wjE9YLvy(1a>< zp$uJ}2#5wNaY=Nd6s@R5FN)EOYILI8!stOgs zM6T+ntZH?uT+OQDx(e2=el@IQb<|kP%GG*YRhJ1_t69-n*0aKOu5_*IR@bW1UD{Kh z`P64j3nIu@6!MS<0mV)Yun2R^!U3HygB#9ak0#`y9!dou1HL6V5D;s3O(hu$OheVs zigvW5Wu6EMvewkrL#?W1ZE9Z&+t|wX9;QS(W{d*{ohTI+SCAe5!L@>Yx*LEwoLVMdv8J@_ zZLfRZOQzEjcfRzkuYF5m++ZR%n93c9KN$kh=(+(N0=a@5;4uSA9F@8=^cFTQrKnY` zAQtmL3<@ljjV>)<9a$kjHAqnkngvUWE#Q_qW<-EdJnFsyJR zfD3o4OZK@2(LgbP&UQc@-%9a{6+i%MJVYHNfUlFE4CScG_r6rFvX!ZvUtjLmm;DvU zOm7P2QRH-|6o71G@lX$*GGVgb4V^YJPyn!qWfkIi&O;*;f^~e0K@3Pz2JYdN(;!AG zr3i0gu<@K%WF{1@AOHbK5EyHaS1SaV2eBBynmZH!AkW`YjwlkafCU8k1RPLD?vR=2 z%oHF!QEs)XU+v5(UwPKFuC?~E?8hzpvCD(BhhPmkkLMnC5%SmtW9?Cl2Qc6h0%