Skip to content

Commit e4a7c6f

Browse files
committed
Clear kernel usage state when switching away
1 parent 7299c08 commit e4a7c6f

File tree

7 files changed

+149
-29
lines changed

7 files changed

+149
-29
lines changed

packages/labextension/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
"dependencies": {
4545
"@jupyterlab/application": "^3.5.1",
4646
"@jupyterlab/apputils": "^3.5.1",
47+
"@jupyterlab/console": "^3.5.1",
4748
"@jupyterlab/coreutils": "^5.5.1",
4849
"@jupyterlab/notebook": "^3.5.1",
4950
"@jupyterlab/services": "^6.5.1",

packages/labextension/src/index.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import {
2+
ILabShell,
23
JupyterFrontEnd,
34
JupyterFrontEndPlugin,
45
} from '@jupyterlab/application';
56
import { INotebookTracker } from '@jupyterlab/notebook';
67
import { LabIcon } from '@jupyterlab/ui-components';
78
import { ICommandPalette } from '@jupyterlab/apputils';
9+
import { IConsoleTracker } from '@jupyterlab/console';
810
import { KernelUsagePanel } from './panel';
911
import tachometer from '../style/tachometer.svg';
1012

@@ -13,6 +15,7 @@ import { IStatusBar } from '@jupyterlab/statusbar';
1315
import { ITranslator } from '@jupyterlab/translation';
1416

1517
import { MemoryUsage } from './memoryUsage';
18+
import { KernelWidgetTracker } from './tracker';
1619

1720
namespace CommandIDs {
1821
export const getKernelUsage = 'kernel-usage:get';
@@ -46,13 +49,15 @@ const memoryStatusPlugin: JupyterFrontEndPlugin<void> = {
4649
const kernelUsagePlugin: JupyterFrontEndPlugin<void> = {
4750
id: '@jupyter-server/resource-usage:kernel-panel-item',
4851
autoStart: true,
49-
optional: [ICommandPalette],
52+
optional: [ICommandPalette, ILabShell, IConsoleTracker],
5053
requires: [ITranslator, INotebookTracker],
5154
activate: (
5255
app: JupyterFrontEnd,
5356
translator: ITranslator,
5457
notebookTracker: INotebookTracker,
55-
palette: ICommandPalette | null
58+
palette: ICommandPalette | null,
59+
labShell: ILabShell | null,
60+
consoleTracker: IConsoleTracker | null
5661
) => {
5762
const trans = translator.load('jupyter-resource-usage');
5863

@@ -62,10 +67,15 @@ const kernelUsagePlugin: JupyterFrontEndPlugin<void> = {
6267
let panel: KernelUsagePanel | null = null;
6368

6469
function createPanel() {
65-
if ((!panel || panel.isDisposed) && notebookTracker) {
70+
if (!panel || panel.isDisposed) {
71+
const tracker = new KernelWidgetTracker({
72+
notebookTracker,
73+
labShell,
74+
consoleTracker,
75+
});
76+
6677
panel = new KernelUsagePanel({
67-
widgetAdded: notebookTracker.widgetAdded,
68-
currentNotebookChanged: notebookTracker.currentChanged,
78+
currentChanged: tracker.currentChanged,
6979
trans: trans,
7080
});
7181
shell.add(panel, 'right', { rank: 200 });

packages/labextension/src/panel.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,17 @@ import { ISignal } from '@lumino/signaling';
33
import { TranslationBundle } from '@jupyterlab/translation';
44
import { StackedPanel } from '@lumino/widgets';
55
import { LabIcon } from '@jupyterlab/ui-components';
6-
import { INotebookTracker, NotebookPanel } from '@jupyterlab/notebook';
76
import { KernelUsageWidget } from './widget';
7+
import { IWidgetWithSession } from './types';
8+
import { KernelWidgetTracker } from './tracker';
9+
810
import tachometer from '../style/tachometer.svg';
911

1012
const PANEL_CLASS = 'jp-KernelUsage-view';
1113

1214
export class KernelUsagePanel extends StackedPanel {
1315
constructor(props: {
14-
widgetAdded: ISignal<INotebookTracker, NotebookPanel | null>;
15-
currentNotebookChanged: ISignal<INotebookTracker, NotebookPanel | null>;
16+
currentChanged: ISignal<KernelWidgetTracker, IWidgetWithSession | null>;
1617
trans: TranslationBundle;
1718
}) {
1819
super();
@@ -24,9 +25,9 @@ export class KernelUsagePanel extends StackedPanel {
2425
svgstr: tachometer,
2526
});
2627
this.title.closable = true;
28+
2729
const widget = new KernelUsageWidget({
28-
widgetAdded: props.widgetAdded,
29-
currentNotebookChanged: props.currentNotebookChanged,
30+
currentChanged: props.currentChanged,
3031
panel: this,
3132
trans: props.trans,
3233
});

packages/labextension/src/tracker.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { ILabShell } from '@jupyterlab/application';
2+
import { INotebookTracker } from '@jupyterlab/notebook';
3+
import { IConsoleTracker } from '@jupyterlab/console';
4+
import { ISignal, Signal } from '@lumino/signaling';
5+
import { IWidgetWithSession, hasKernelSession } from './types';
6+
7+
/**
8+
* Tracks widgets with kernels as well as possible given the available tokens.
9+
*/
10+
export class KernelWidgetTracker {
11+
constructor(options: KernelWidgetTracker.IOptions) {
12+
const { labShell, notebookTracker, consoleTracker } = options;
13+
this._currentChanged = new Signal(this);
14+
if (labShell) {
15+
labShell.currentChanged.connect((_, update) => {
16+
const widget = update.newValue;
17+
if (widget && hasKernelSession(widget)) {
18+
this._currentChanged.emit(widget);
19+
} else {
20+
this._currentChanged.emit(null);
21+
}
22+
});
23+
} else {
24+
notebookTracker.currentChanged.connect((_, widget) => {
25+
this._currentChanged.emit(widget);
26+
});
27+
if (consoleTracker) {
28+
consoleTracker.currentChanged.connect((_, widget) => {
29+
this._currentChanged.emit(widget);
30+
});
31+
}
32+
}
33+
}
34+
35+
/**
36+
* Emits on any change of active widget. The value is a known widget with
37+
* kernel session or null if user switched to a widget which does not support
38+
* kernel sessions.
39+
*/
40+
get currentChanged(): ISignal<
41+
KernelWidgetTracker,
42+
IWidgetWithSession | null
43+
> {
44+
return this._currentChanged;
45+
}
46+
47+
private _currentChanged: Signal<
48+
KernelWidgetTracker,
49+
IWidgetWithSession | null
50+
>;
51+
}
52+
53+
/**
54+
* Namespace for kernel widget tracker.
55+
*/
56+
export namespace KernelWidgetTracker {
57+
export interface IOptions {
58+
notebookTracker: INotebookTracker;
59+
labShell: ILabShell | null;
60+
consoleTracker: IConsoleTracker | null;
61+
}
62+
}

packages/labextension/src/types.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { Widget } from '@lumino/widgets';
2+
import { ISessionContext } from '@jupyterlab/apputils';
3+
import { NotebookPanel } from '@jupyterlab/notebook';
4+
import { ConsolePanel } from '@jupyterlab/console';
5+
6+
/**
7+
* Interface used to abstract away widgets with kernel to avoid introducing
8+
* spurious dependencies on properties specific to e.g. notebook or console.
9+
*/
10+
export interface IWidgetWithSession extends Widget {
11+
/**
12+
* Session context providing kernel access.
13+
*/
14+
sessionContext: ISessionContext;
15+
}
16+
17+
/**
18+
* Check if given widget is one of the widgets known to have kernel session.
19+
*
20+
* Note: we could switch to duck-typing in future.
21+
*/
22+
export function hasKernelSession(
23+
widget: Widget
24+
): widget is ConsolePanel | NotebookPanel {
25+
return widget instanceof ConsolePanel || widget instanceof NotebookPanel;
26+
}

packages/labextension/src/widget.tsx

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@ import { ReactWidget, ISessionContext } from '@jupyterlab/apputils';
44
import { IChangedArgs } from '@jupyterlab/coreutils';
55
import { Kernel } from '@jupyterlab/services';
66
import { TranslationBundle } from '@jupyterlab/translation';
7-
import { INotebookTracker, NotebookPanel } from '@jupyterlab/notebook';
87
import { requestAPI } from './handler';
98
import { KernelUsagePanel } from './panel';
109
import useInterval from './useInterval';
1110
import { formatForDisplay } from './format';
11+
import { IWidgetWithSession } from './types';
12+
import { KernelWidgetTracker } from './tracker';
1213

1314
type Usage = {
1415
timestamp: Date | null;
@@ -43,12 +44,11 @@ type KernelChangeCallback = (
4344
) => void;
4445
let kernelChangeCallback: {
4546
callback: KernelChangeCallback;
46-
panel: NotebookPanel;
47+
panel: IWidgetWithSession;
4748
} | null = null;
4849

4950
const KernelUsage = (props: {
50-
widgetAdded: ISignal<INotebookTracker, NotebookPanel | null>;
51-
currentNotebookChanged: ISignal<INotebookTracker, NotebookPanel | null>;
51+
currentChanged: ISignal<KernelWidgetTracker, IWidgetWithSession | null>;
5252
panel: KernelUsagePanel;
5353
trans: TranslationBundle;
5454
}) => {
@@ -79,7 +79,7 @@ const KernelUsage = (props: {
7979
};
8080

8181
useEffect(() => {
82-
const createKernelChangeCallback = (panel: NotebookPanel) => {
82+
const createKernelChangeCallback = (panel: IWidgetWithSession) => {
8383
return (
8484
_sender: ISessionContext,
8585
args: IChangedArgs<
@@ -102,12 +102,13 @@ const KernelUsage = (props: {
102102
};
103103

104104
const notebookChangeCallback = (
105-
sender: INotebookTracker,
106-
panel: NotebookPanel | null
105+
_: KernelWidgetTracker,
106+
panel: IWidgetWithSession | null
107107
) => {
108108
if (panel === null) {
109109
// Ideally we would switch to a new "select a notebook to get kernel
110110
// usage" screen instead of showing outdated info.
111+
setKernelId(undefined);
111112
return;
112113
}
113114
if (kernelChangeCallback) {
@@ -131,9 +132,9 @@ const KernelUsage = (props: {
131132
}
132133
}
133134
};
134-
props.currentNotebookChanged.connect(notebookChangeCallback);
135+
props.currentChanged.connect(notebookChangeCallback);
135136
return () => {
136-
props.currentNotebookChanged.disconnect(notebookChangeCallback);
137+
props.currentChanged.disconnect(notebookChangeCallback);
137138
// In the ideal world we would disconnect kernelChangeCallback from
138139
// last panel here, but this can lead to a race condition. Instead,
139140
// we make sure there is ever only one callback active by holding
@@ -239,31 +240,27 @@ const KernelUsage = (props: {
239240
};
240241

241242
export class KernelUsageWidget extends ReactWidget {
242-
private _widgetAdded: ISignal<INotebookTracker, NotebookPanel | null>;
243-
private _currentNotebookChanged: ISignal<
244-
INotebookTracker,
245-
NotebookPanel | null
243+
private _currentChanged: ISignal<
244+
KernelWidgetTracker,
245+
IWidgetWithSession | null
246246
>;
247247
private _panel: KernelUsagePanel;
248248
private _trans: TranslationBundle;
249249
constructor(props: {
250-
widgetAdded: ISignal<INotebookTracker, NotebookPanel | null>;
251-
currentNotebookChanged: ISignal<INotebookTracker, NotebookPanel | null>;
250+
currentChanged: ISignal<KernelWidgetTracker, IWidgetWithSession | null>;
252251
panel: KernelUsagePanel;
253252
trans: TranslationBundle;
254253
}) {
255254
super();
256-
this._widgetAdded = props.widgetAdded;
257-
this._currentNotebookChanged = props.currentNotebookChanged;
255+
this._currentChanged = props.currentChanged;
258256
this._panel = props.panel;
259257
this._trans = props.trans;
260258
}
261259

262260
protected render(): React.ReactElement<any> {
263261
return (
264262
<KernelUsage
265-
widgetAdded={this._widgetAdded}
266-
currentNotebookChanged={this._currentNotebookChanged}
263+
currentChanged={this._currentChanged}
267264
panel={this._panel}
268265
trans={this._trans}
269266
/>

yarn.lock

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,29 @@
383383
react "^17.0.1"
384384
y-codemirror "^3.0.1"
385385

386+
"@jupyterlab/[email protected]":
387+
version "3.5.1"
388+
resolved "https://registry.yarnpkg.com/@jupyterlab/console/-/console-3.5.1.tgz#175d6d1b7f103830dde4d7762a174836ee5bce3e"
389+
integrity sha512-KQTZjeRg4jMRKie6W7wZxSRRTTRvj4H6jBev16ame7f/jR/pzK5Wt7bZ6nRg8h6J2oxqjsJ5gJ6aqjGPaotuWQ==
390+
dependencies:
391+
"@jupyterlab/apputils" "^3.5.1"
392+
"@jupyterlab/cells" "^3.5.1"
393+
"@jupyterlab/codeeditor" "^3.5.1"
394+
"@jupyterlab/coreutils" "^5.5.1"
395+
"@jupyterlab/nbformat" "^3.5.1"
396+
"@jupyterlab/observables" "^4.5.1"
397+
"@jupyterlab/rendermime" "^3.5.1"
398+
"@jupyterlab/services" "^6.5.1"
399+
"@jupyterlab/translation" "^3.5.1"
400+
"@jupyterlab/ui-components" "^3.5.1"
401+
"@lumino/algorithm" "^1.9.0"
402+
"@lumino/coreutils" "^1.11.0"
403+
"@lumino/disposable" "^1.10.0"
404+
"@lumino/dragdrop" "^1.13.0"
405+
"@lumino/messaging" "^1.10.0"
406+
"@lumino/signaling" "^1.10.0"
407+
"@lumino/widgets" "^1.33.0"
408+
386409
"@jupyterlab/coreutils@^5.5.1":
387410
version "5.5.1"
388411
resolved "https://registry.npmjs.org/@jupyterlab/coreutils/-/coreutils-5.5.1.tgz"

0 commit comments

Comments
 (0)