Skip to content

Commit

Permalink
Add bandwidthSaveMode to ServiceManager, use it in hub-extension (
Browse files Browse the repository at this point in the history
#10156)

* Add `bandwidthSaveMode` to `ServiceManager`, use it in `hub-extension`

* Move `bandwidthSaveMode` to `JupyterLab.IInfo` of `@jupyterlab/application`

* Rename `bandwidthSaveMode` to `isConnected` in `JupyterLab.IInfo`
  • Loading branch information
vkaidalov-rft committed Aug 13, 2021
1 parent a183d55 commit e671140
Show file tree
Hide file tree
Showing 12 changed files with 102 additions and 19 deletions.
30 changes: 27 additions & 3 deletions packages/application/src/lab.ts
Expand Up @@ -4,6 +4,7 @@
import { PageConfig } from '@jupyterlab/coreutils';
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';
import { createRendermimePlugins } from './mimerenderers';
Expand All @@ -18,7 +19,17 @@ export class JupyterLab extends JupyterFrontEnd<ILabShell> {
* 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.isConnected || 'when-hidden';
}
})
});
this.restored = this.shell.restored
.then(() => undefined)
.catch(() => undefined);
Expand Down Expand Up @@ -157,7 +168,7 @@ export class JupyterLab extends JupyterFrontEnd<ILabShell> {
});
}

private _info: JupyterLab.IInfo;
private _info: JupyterLab.IInfo = JupyterLab.defaultInfo;
private _paths: JupyterFrontEnd.IPaths;
}

Expand Down Expand Up @@ -207,6 +218,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 `false`. Extensions should use this value to decide whether to proceed
* with the polling.
* The extensions may also set this value to `false` 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 `true`.
*/
isConnected: boolean;
}

/**
Expand All @@ -217,7 +240,8 @@ export namespace JupyterLab {
deferred: { patterns: [], matches: [] },
disabled: { patterns: [], matches: [] },
mimeExtensions: [],
filesCached: PageConfig.getOption('cacheFiles').toLowerCase() === 'true'
filesCached: PageConfig.getOption('cacheFiles').toLowerCase() === 'true',
isConnected: true
};

/**
Expand Down
17 changes: 13 additions & 4 deletions packages/docmanager-extension/src/index.ts
Expand Up @@ -9,7 +9,8 @@ import {
ILabShell,
ILabStatus,
JupyterFrontEnd,
JupyterFrontEndPlugin
JupyterFrontEndPlugin,
JupyterLab
} from '@jupyterlab/application';
import {
Dialog,
Expand Down Expand Up @@ -89,7 +90,8 @@ const docManagerPlugin: JupyterFrontEndPlugin<IDocumentManager> = {
ICommandPalette,
ILabShell,
ISessionContextDialogs,
IDocumentProviderFactory
IDocumentProviderFactory,
JupyterLab.IInfo
],
activate: (
app: JupyterFrontEnd,
Expand All @@ -99,7 +101,8 @@ const docManagerPlugin: JupyterFrontEndPlugin<IDocumentManager> = {
palette: ICommandPalette | null,
labShell: ILabShell | null,
sessionDialogs: ISessionContextDialogs | null,
docProviderFactory: IDocumentProviderFactory | null
docProviderFactory: IDocumentProviderFactory | null,
info: JupyterLab.IInfo | null
): IDocumentManager => {
const trans = translator.load('jupyterlab');
const manager = app.serviceManager;
Expand Down Expand Up @@ -139,7 +142,13 @@ const docManagerPlugin: JupyterFrontEndPlugin<IDocumentManager> = {
sessionDialogs: sessionDialogs || undefined,
translator,
collaborative: true,
docProviderFactory: docProviderFactory ?? undefined
docProviderFactory: docProviderFactory ?? undefined,
isConnectedCallback: () => {
if (info) {
return info.isConnected;
}
return true;
}
});

// Register the file operations commands.
Expand Down
9 changes: 9 additions & 0 deletions packages/docmanager/src/manager.ts
Expand Up @@ -42,6 +42,7 @@ export class DocumentManager implements IDocumentManager {
this._collaborative = !!options.collaborative;
this._dialogs = options.sessionDialogs || sessionContextDialogs;
this._docProviderFactory = options.docProviderFactory;
this._isConnectedCallback = options.isConnectedCallback || (() => true);

this._opener = options.opener;
this._when = options.when || options.manager.ready;
Expand Down Expand Up @@ -477,6 +478,7 @@ export class DocumentManager implements IDocumentManager {
});
const handler = new SaveHandler({
context,
isConnectedCallback: this._isConnectedCallback,
saveInterval: this.autosaveInterval
});
Private.saveHandlerProperty.set(context, handler);
Expand Down Expand Up @@ -602,6 +604,7 @@ export class DocumentManager implements IDocumentManager {
private _dialogs: ISessionContext.IDialogs;
private _docProviderFactory: IDocumentProviderFactory | undefined;
private _collaborative: boolean;
private _isConnectedCallback: () => boolean;
}

/**
Expand Down Expand Up @@ -657,6 +660,12 @@ export namespace DocumentManager {
* If true, the context will connect through yjs_ws_server to share information if possible.
*/
collaborative?: boolean;

/**
* Autosaving should be paused while this callback function returns `false`.
* By default, it always returns `true`.
*/
isConnectedCallback?: () => boolean;
}

/**
Expand Down
12 changes: 11 additions & 1 deletion packages/docmanager/src/savehandler.ts
Expand Up @@ -17,6 +17,7 @@ export class SaveHandler implements IDisposable {
*/
constructor(options: SaveHandler.IOptions) {
this._context = options.context;
this._isConnectedCallback = options.isConnectedCallback || (() => true);
const interval = options.saveInterval || 120;
this._minInterval = interval * 1000;
this._interval = this._minInterval;
Expand Down Expand Up @@ -89,7 +90,9 @@ export class SaveHandler implements IDisposable {
return;
}
this._autosaveTimer = window.setTimeout(() => {
this._save();
if (this._isConnectedCallback()) {
this._save();
}
}, this._interval);
}

Expand Down Expand Up @@ -144,6 +147,7 @@ export class SaveHandler implements IDisposable {
private _minInterval = -1;
private _interval = -1;
private _context: DocumentRegistry.Context;
private _isConnectedCallback: () => boolean;
private _isActive = false;
private _inDialog = false;
private _isDisposed = false;
Expand All @@ -163,6 +167,12 @@ export namespace SaveHandler {
*/
context: DocumentRegistry.Context;

/**
* Autosaving should be paused while this callback function returns `false`.
* By default, it always returns `true`.
*/
isConnectedCallback?: () => boolean;

/**
* The minimum save interval in seconds (default is two minutes).
*/
Expand Down
19 changes: 16 additions & 3 deletions packages/filebrowser-extension/src/index.ts
Expand Up @@ -11,7 +11,8 @@ import {
IRouter,
ITreePathUpdater,
JupyterFrontEnd,
JupyterFrontEndPlugin
JupyterFrontEndPlugin,
JupyterLab
} from '@jupyterlab/application';
import {
Clipboard,
Expand Down Expand Up @@ -246,14 +247,20 @@ const factory: JupyterFrontEndPlugin<IFileBrowserFactory> = {
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<IFileBrowserFactory> => {
const { commands } = app;
const tracker = new WidgetTracker<FileBrowser>({ namespace });
Expand All @@ -267,6 +274,12 @@ const factory: JupyterFrontEndPlugin<IFileBrowserFactory> = {
manager: docManager,
driveName: options.driveName || '',
refreshInterval: options.refreshInterval,
refreshStandby: () => {
if (info) {
return !info.isConnected || 'when-hidden';
}
return 'when-hidden';
},
state:
options.state === null
? undefined
Expand Down
7 changes: 6 additions & 1 deletion packages/filebrowser/src/model.ts
Expand Up @@ -103,7 +103,7 @@ export class FileBrowserModel implements IDisposable {
backoff: true,
max: 300 * 1000
},
standby: 'when-hidden'
standby: options.refreshStandby || 'when-hidden'
});
}

Expand Down Expand Up @@ -695,6 +695,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.
Expand Down
17 changes: 15 additions & 2 deletions packages/hub-extension/src/index.ts
Expand Up @@ -11,7 +11,8 @@ import {
ConnectionLost,
IConnectionLost,
JupyterFrontEnd,
JupyterFrontEndPlugin
JupyterFrontEndPlugin,
JupyterLab
} from '@jupyterlab/application';
import { Dialog, ICommandPalette, showDialog } from '@jupyterlab/apputils';
import { URLExt } from '@jupyterlab/coreutils';
Expand Down Expand Up @@ -125,10 +126,12 @@ const hubExtensionMenu: JupyterFrontEndPlugin<void> = {
const connectionlost: JupyterFrontEndPlugin<IConnectionLost> = {
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 || '';
Expand All @@ -149,7 +152,12 @@ const connectionlost: JupyterFrontEndPlugin<IConnectionLost> = {
if (showingError) {
return;
}

showingError = true;
if (info) {
info.isConnected = false;
}

const result = await showDialog({
title: trans.__('Server unavailable or unreachable'),
body: trans.__(
Expand All @@ -161,7 +169,12 @@ const connectionlost: JupyterFrontEndPlugin<IConnectionLost> = {
Dialog.cancelButton({ label: trans.__('Dismiss') })
]
});

if (info) {
info.isConnected = true;
}
showingError = false;

if (result.button.accept) {
await app.commands.execute(CommandIDs.restart);
}
Expand Down
2 changes: 1 addition & 1 deletion packages/services/src/kernel/manager.ts
Expand Up @@ -337,6 +337,6 @@ export namespace KernelManager {
/**
* When the manager stops polling the API. Defaults to `when-hidden`.
*/
standby?: Poll.Standby;
standby?: Poll.Standby | (() => boolean | Poll.Standby);
}
}
2 changes: 1 addition & 1 deletion packages/services/src/kernelspec/manager.ts
Expand Up @@ -147,6 +147,6 @@ export namespace KernelSpecManager {
/**
* When the manager stops polling the API. Defaults to `when-hidden`.
*/
standby?: Poll.Standby;
standby?: Poll.Standby | (() => boolean | Poll.Standby);
}
}
2 changes: 1 addition & 1 deletion packages/services/src/manager.ts
Expand Up @@ -254,6 +254,6 @@ export namespace ServiceManager {
/**
* When the manager stops polling the API. Defaults to `when-hidden`.
*/
standby?: Poll.Standby;
standby?: Poll.Standby | (() => boolean | Poll.Standby);
}
}
2 changes: 1 addition & 1 deletion packages/services/src/session/manager.ts
Expand Up @@ -355,7 +355,7 @@ export namespace SessionManager {
/**
* When the manager stops polling the API. Defaults to `when-hidden`.
*/
standby?: Poll.Standby;
standby?: Poll.Standby | (() => boolean | Poll.Standby);

/**
* Kernel Manager
Expand Down
2 changes: 1 addition & 1 deletion packages/services/src/terminal/manager.ts
Expand Up @@ -289,6 +289,6 @@ export namespace TerminalManager {
/**
* When the manager stops polling the API. Defaults to `when-hidden`.
*/
standby?: Poll.Standby;
standby?: Poll.Standby | (() => boolean | Poll.Standby);
}
}

0 comments on commit e671140

Please sign in to comment.