From 7444026795553f7b755ffabc575682180feb5ee9 Mon Sep 17 00:00:00 2001 From: Vadym Kaidalov Date: Wed, 28 Apr 2021 16:41:10 +0300 Subject: [PATCH] Move `bandwidthSaveMode` to `JupyterLab.IInfo` of `@jupyterlab/application` --- packages/application/src/lab.ts | 31 ++++++++++++++-- packages/docmanager-extension/src/index.ts | 14 +++++-- packages/docmanager/src/manager.ts | 13 ++++++- packages/docmanager/src/savehandler.ts | 18 ++++----- packages/docmanager/test/savehandler.spec.ts | 2 +- packages/filebrowser-extension/src/index.ts | 16 ++++++-- packages/filebrowser/src/model.ts | 9 +++-- packages/hub-extension/src/index.ts | 19 ++++++++-- packages/services/src/manager.ts | 39 +------------------- 9 files changed, 95 insertions(+), 66 deletions(-) diff --git a/packages/application/src/lab.ts b/packages/application/src/lab.ts index eedc94feb363..b6d5f3c10b89 100644 --- a/packages/application/src/lab.ts +++ b/packages/application/src/lab.ts @@ -7,6 +7,8 @@ import { Base64ModelFactory } from '@jupyterlab/docregistry'; import { IRenderMime } from '@jupyterlab/rendermime-interfaces'; +import { ServiceManager } from '@jupyterlab/services'; + import { Token } from '@lumino/coreutils'; import { JupyterFrontEnd, JupyterFrontEndPlugin } from './frontend'; @@ -25,7 +27,17 @@ export class JupyterLab extends JupyterFrontEnd { * Construct a new JupyterLab object. */ constructor(options: JupyterLab.IOptions = { shell: new LabShell() }) { - super({ ...options, shell: options.shell || new LabShell() }); + super({ + ...options, + shell: options.shell || new LabShell(), + serviceManager: + options.serviceManager || + new ServiceManager({ + standby: () => { + return this._info.bandwidthSaveMode || 'when-hidden'; + } + }) + }); this.restored = this.shell.restored .then(() => undefined) .catch(() => undefined); @@ -164,7 +176,7 @@ export class JupyterLab extends JupyterFrontEnd { }); } - private _info: JupyterLab.IInfo; + private _info: JupyterLab.IInfo = JupyterLab.defaultInfo; private _paths: JupyterFrontEnd.IPaths; } @@ -214,6 +226,18 @@ export namespace JupyterLab { * Whether files are cached on the server. */ readonly filesCached: boolean; + + /** + * Every periodic network polling should be paused while this is set + * to `true`. Extensions should use this value to decide whether to proceed + * with the polling. + * The extensions may also set this value to `true` if there is no need to + * fetch anything from the server backend basing on some conditions + * (e.g. when an error message dialog is displayed). + * At the same time, the extensions are responsible for setting this value + * back to `false`. + */ + bandwidthSaveMode: boolean; } /** @@ -224,7 +248,8 @@ export namespace JupyterLab { deferred: { patterns: [], matches: [] }, disabled: { patterns: [], matches: [] }, mimeExtensions: [], - filesCached: PageConfig.getOption('cacheFiles').toLowerCase() === 'true' + filesCached: PageConfig.getOption('cacheFiles').toLowerCase() === 'true', + bandwidthSaveMode: false }; /** diff --git a/packages/docmanager-extension/src/index.ts b/packages/docmanager-extension/src/index.ts index 358aa0f02ff6..bdb42fee1faf 100644 --- a/packages/docmanager-extension/src/index.ts +++ b/packages/docmanager-extension/src/index.ts @@ -9,7 +9,8 @@ import { ILabShell, ILabStatus, JupyterFrontEnd, - JupyterFrontEndPlugin + JupyterFrontEndPlugin, + JupyterLab } from '@jupyterlab/application'; import { @@ -99,7 +100,8 @@ const docManagerPlugin: JupyterFrontEndPlugin = { ICommandPalette, ILabShell, IMainMenu, - ISessionContextDialogs + ISessionContextDialogs, + JupyterLab.IInfo ], activate: ( app: JupyterFrontEnd, @@ -109,7 +111,8 @@ const docManagerPlugin: JupyterFrontEndPlugin = { palette: ICommandPalette | null, labShell: ILabShell | null, mainMenu: IMainMenu | null, - sessionDialogs: ISessionContextDialogs | null + sessionDialogs: ISessionContextDialogs | null, + info: JupyterLab.IInfo | null ): IDocumentManager => { const trans = translator.load('jupyterlab'); const manager = app.serviceManager; @@ -147,7 +150,10 @@ const docManagerPlugin: JupyterFrontEndPlugin = { when, setBusy: (status && (() => status.setBusy())) ?? undefined, sessionDialogs: sessionDialogs || undefined, - translator + translator, + bandwidthSaveModeCallback: () => { + return info?.bandwidthSaveMode || false; + } }); // Register the file operations commands. diff --git a/packages/docmanager/src/manager.ts b/packages/docmanager/src/manager.ts index 242e91e0a509..0f85fa97c10a 100644 --- a/packages/docmanager/src/manager.ts +++ b/packages/docmanager/src/manager.ts @@ -49,6 +49,8 @@ export class DocumentManager implements IDocumentManager { */ constructor(options: DocumentManager.IOptions) { this.translator = options.translator || nullTranslator; + this._bandwidthSaveModeCallback = + options.bandwidthSaveModeCallback || (() => false); this.registry = options.registry; this.services = options.manager; this._dialogs = options.sessionDialogs || sessionContextDialogs; @@ -485,8 +487,8 @@ export class DocumentManager implements IDocumentManager { }); const handler = new SaveHandler({ context, - saveInterval: this.autosaveInterval, - services: this.services + bandwidthSaveModeCallback: this._bandwidthSaveModeCallback, + saveInterval: this.autosaveInterval }); Private.saveHandlerProperty.set(context, handler); void context.ready.then(() => { @@ -609,6 +611,7 @@ export class DocumentManager implements IDocumentManager { private _when: Promise; private _setBusy: (() => IDisposable) | undefined; private _dialogs: ISessionContext.IDialogs; + private _bandwidthSaveModeCallback: () => boolean; } /** @@ -653,6 +656,12 @@ export namespace DocumentManager { * The applicaton language translator. */ translator?: ITranslator; + + /** + * Autosaving should be paused while this callback function returns `true`. + * By default, it always returns `false`. + */ + bandwidthSaveModeCallback?: () => boolean; } /** diff --git a/packages/docmanager/src/savehandler.ts b/packages/docmanager/src/savehandler.ts index be3119b95d1d..f44dcd125f43 100644 --- a/packages/docmanager/src/savehandler.ts +++ b/packages/docmanager/src/savehandler.ts @@ -5,8 +5,6 @@ import { IDisposable } from '@lumino/disposable'; import { Signal } from '@lumino/signaling'; -import { ServiceManager } from '@jupyterlab/services'; - import { DocumentRegistry } from '@jupyterlab/docregistry'; /** @@ -21,7 +19,8 @@ export class SaveHandler implements IDisposable { */ constructor(options: SaveHandler.IOptions) { this._context = options.context; - this._services = options.services; + this._bandwidthSaveModeCallback = + options.bandwidthSaveModeCallback || (() => false); const interval = options.saveInterval || 120; this._minInterval = interval * 1000; this._interval = this._minInterval; @@ -94,7 +93,7 @@ export class SaveHandler implements IDisposable { return; } this._autosaveTimer = window.setTimeout(() => { - if (!this._services.bandwidthSaveMode) { + if (!this._bandwidthSaveModeCallback()) { this._save(); } }, this._interval); @@ -151,7 +150,7 @@ export class SaveHandler implements IDisposable { private _minInterval = -1; private _interval = -1; private _context: DocumentRegistry.Context; - private _services: ServiceManager.IManager; + private _bandwidthSaveModeCallback: () => boolean; private _isActive = false; private _inDialog = false; private _isDisposed = false; @@ -172,13 +171,14 @@ export namespace SaveHandler { context: DocumentRegistry.Context; /** - * The minimum save interval in seconds (default is two minutes). + * Autosaving should be paused while this callback function returns `true`. + * By default, it always returns `false`. */ - saveInterval?: number; + bandwidthSaveModeCallback?: () => boolean; /** - * A service manager instance. + * The minimum save interval in seconds (default is two minutes). */ - services: ServiceManager.IManager; + saveInterval?: number; } } diff --git a/packages/docmanager/test/savehandler.spec.ts b/packages/docmanager/test/savehandler.spec.ts index 2ff90dedd30d..e18985332be1 100644 --- a/packages/docmanager/test/savehandler.spec.ts +++ b/packages/docmanager/test/savehandler.spec.ts @@ -38,7 +38,7 @@ describe('docregistry/savehandler', () => { factory, path: UUID.uuid4() + '.txt' }); - handler = new SaveHandler({ context, services: manager }); + handler = new SaveHandler({ context }); return context.initialize(true); }); diff --git a/packages/filebrowser-extension/src/index.ts b/packages/filebrowser-extension/src/index.ts index aaf5a192cc6b..3605e69f347e 100644 --- a/packages/filebrowser-extension/src/index.ts +++ b/packages/filebrowser-extension/src/index.ts @@ -11,7 +11,8 @@ import { ITreePathUpdater, IRouter, JupyterFrontEnd, - JupyterFrontEndPlugin + JupyterFrontEndPlugin, + JupyterLab } from '@jupyterlab/application'; import { @@ -253,14 +254,20 @@ const factory: JupyterFrontEndPlugin = { id: '@jupyterlab/filebrowser-extension:factory', provides: IFileBrowserFactory, requires: [IDocumentManager, ITranslator], - optional: [IStateDB, IRouter, JupyterFrontEnd.ITreeResolver], + optional: [ + IStateDB, + IRouter, + JupyterFrontEnd.ITreeResolver, + JupyterLab.IInfo + ], activate: async ( app: JupyterFrontEnd, docManager: IDocumentManager, translator: ITranslator, state: IStateDB | null, router: IRouter | null, - tree: JupyterFrontEnd.ITreeResolver | null + tree: JupyterFrontEnd.ITreeResolver | null, + info: JupyterLab.IInfo | null ): Promise => { const { commands } = app; const tracker = new WidgetTracker({ namespace }); @@ -274,6 +281,9 @@ const factory: JupyterFrontEndPlugin = { manager: docManager, driveName: options.driveName || '', refreshInterval: options.refreshInterval, + refreshStandby: () => { + return info?.bandwidthSaveMode || 'when-hidden'; + }, state: options.state === null ? undefined diff --git a/packages/filebrowser/src/model.ts b/packages/filebrowser/src/model.ts index 695e91cc82f9..f296e1f4da24 100644 --- a/packages/filebrowser/src/model.ts +++ b/packages/filebrowser/src/model.ts @@ -113,9 +113,7 @@ export class FileBrowserModel implements IDisposable { backoff: true, max: 300 * 1000 }, - standby: () => { - return services.bandwidthSaveMode || 'when-hidden'; - } + standby: options.refreshStandby || 'when-hidden' }); } @@ -700,6 +698,11 @@ export namespace FileBrowserModel { */ refreshInterval?: number; + /** + * When the model stops polling the API. Defaults to `when-hidden`. + */ + refreshStandby?: Poll.Standby | (() => boolean | Poll.Standby); + /** * An optional state database. If provided, the model will restore which * folder was last opened when it is restored. diff --git a/packages/hub-extension/src/index.ts b/packages/hub-extension/src/index.ts index 15e4eabaaaf6..c7b76b4a946f 100644 --- a/packages/hub-extension/src/index.ts +++ b/packages/hub-extension/src/index.ts @@ -13,7 +13,8 @@ import { ConnectionLost, IConnectionLost, JupyterFrontEnd, - JupyterFrontEndPlugin + JupyterFrontEndPlugin, + JupyterLab } from '@jupyterlab/application'; import { URLExt } from '@jupyterlab/coreutils'; @@ -128,10 +129,12 @@ const hubExtension: JupyterFrontEndPlugin = { const connectionlost: JupyterFrontEndPlugin = { id: '@jupyterlab/apputils-extension:connectionlost', requires: [JupyterFrontEnd.IPaths, ITranslator], + optional: [JupyterLab.IInfo], activate: ( app: JupyterFrontEnd, paths: JupyterFrontEnd.IPaths, - translator: ITranslator + translator: ITranslator, + info: JupyterLab.IInfo | null ): IConnectionLost => { const trans = translator.load('jupyterlab'); const hubPrefix = paths.urls.hubPrefix || ''; @@ -152,8 +155,12 @@ const connectionlost: JupyterFrontEndPlugin = { if (showingError) { return; } + showingError = true; - manager.bandwidthSaveMode = true; + if (info) { + info.bandwidthSaveMode = true; + } + const result = await showDialog({ title: trans.__('Server unavailable or unreachable'), body: trans.__( @@ -165,8 +172,12 @@ const connectionlost: JupyterFrontEndPlugin = { Dialog.cancelButton({ label: trans.__('Dismiss') }) ] }); - manager.bandwidthSaveMode = false; + + if (info) { + info.bandwidthSaveMode = false; + } showingError = false; + if (result.button.accept) { await app.commands.execute(CommandIDs.restart); } diff --git a/packages/services/src/manager.ts b/packages/services/src/manager.ts index edc86ceb5b14..a328f5730e91 100644 --- a/packages/services/src/manager.ts +++ b/packages/services/src/manager.ts @@ -37,12 +37,7 @@ export class ServiceManager implements ServiceManager.IManager { const defaultDrive = options.defaultDrive; const serverSettings = options.serverSettings ?? ServerConnection.makeSettings(); - - const standby = - options.standby ?? - (() => { - return this.bandwidthSaveMode || 'when-hidden'; - }); + const standby = options.standby ?? 'when-hidden'; const normalized = { defaultDrive, serverSettings, standby }; const kernelManager = new KernelManager(normalized); @@ -163,23 +158,6 @@ export class ServiceManager implements ServiceManager.IManager { return this._readyPromise; } - /** - * Every periodic network polling should be paused while - * this is set to true. Extensions should use this value - * to decide whether to proceed with the polling. - * The extensions may also set this value to true if there is no need - * to fetch anything from the server backend basing on some conditions - * (e.g. when an error message dialog is displayed). - * At the same time, the extensions are responsible for setting - * this value back to false. - */ - get bandwidthSaveMode(): boolean { - return this._bandwidthSaveMode; - } - set bandwidthSaveMode(value: boolean) { - this._bandwidthSaveMode = value; - } - private _onConnectionFailure(sender: any, err: Error): void { this._connectionFailure.emit(err); } @@ -188,7 +166,6 @@ export class ServiceManager implements ServiceManager.IManager { private _readyPromise: Promise; private _connectionFailure = new Signal(this); private _isReady = false; - private _bandwidthSaveMode = false; } /** @@ -258,18 +235,6 @@ export namespace ServiceManager { * A signal emitted when there is a connection failure with the server. */ readonly connectionFailure: ISignal; - - /** - * Every periodic network polling should be paused while - * this is set to true. Extensions should use this value - * to decide whether to proceed with the polling. - * The extensions may also set this value to true if there is no need - * to fetch anything from the server backend basing on some conditions - * (e.g. when an error message dialog is displayed). - * At the same time, the extensions are responsible for setting - * this value back to false. - */ - bandwidthSaveMode: boolean; } /** @@ -287,7 +252,7 @@ export namespace ServiceManager { readonly defaultDrive?: Contents.IDrive; /** - * When the manager stops polling the API. + * When the manager stops polling the API. Defaults to `when-hidden`. */ standby?: Poll.Standby | (() => boolean | Poll.Standby); }