Skip to content

Commit 143f456

Browse files
authored
Merge pull request #178 from krassowski/clear-state-when-switching-away
Clear state when switching away, add blank state indicator
2 parents 7299c08 + c0d30a1 commit 143f456

File tree

10 files changed

+1296
-2189
lines changed

10 files changed

+1296
-2189
lines changed

jupyter_resource_usage/api.py

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@
1414
try:
1515
import ipykernel
1616

17-
USAGE_IS_SUPPORTED = version.parse("6.9.0") <= version.parse(ipykernel.__version__)
17+
IPYKERNEL_VERSION = ipykernel.__version__
18+
USAGE_IS_SUPPORTED = version.parse("6.9.0") <= version.parse(IPYKERNEL_VERSION)
1819
except ImportError:
1920
USAGE_IS_SUPPORTED = False
21+
IPYKERNEL_VERSION = None
2022

2123

2224
class ApiHandler(APIHandler):
@@ -92,7 +94,16 @@ class KernelUsageHandler(APIHandler):
9294
@web.authenticated
9395
async def get(self, matched_part=None, *args, **kwargs):
9496
if not USAGE_IS_SUPPORTED:
95-
self.write(json.dumps({}))
97+
self.write(
98+
json.dumps(
99+
{
100+
"content": {
101+
"reason": "not_supported",
102+
"kernel_version": IPYKERNEL_VERSION,
103+
}
104+
}
105+
)
106+
)
96107
return
97108

98109
kernel_id = matched_part
@@ -108,15 +119,23 @@ async def get(self, matched_part=None, *args, **kwargs):
108119
poller = zmq.asyncio.Poller()
109120
control_socket = control_channel.socket
110121
poller.register(control_socket, zmq.POLLIN)
111-
# previous behavior was 3 retries: 1 + 2 + 3 = 6 seconds
112-
timeout_ms = 6_000
122+
timeout_ms = 10_000
113123
events = dict(await poller.poll(timeout_ms))
114124
if control_socket not in events:
115-
self.write(json.dumps({}))
125+
self.write(
126+
json.dumps(
127+
{
128+
"content": {"reason": "timeout", "timeout_ms": timeout_ms},
129+
"kernel_id": kernel_id,
130+
}
131+
)
132+
)
116133
else:
117134
res = client.control_channel.get_msg(timeout=0)
118135
if isawaitable(res):
119136
# control_channel.get_msg may return a Future,
120137
# depending on configured KernelManager class
121138
res = await res
139+
if res:
140+
res["kernel_id"] = kernel_id
122141
self.write(json.dumps(res, default=date_default))

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+
}

0 commit comments

Comments
 (0)