Skip to content

Commit d95b219

Browse files
committed
Use control comm target in LabManager
1 parent 32f59ac commit d95b219

File tree

1 file changed

+115
-0
lines changed

1 file changed

+115
-0
lines changed

python/jupyterlab_widgets/src/manager.ts

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
WidgetView,
1212
put_buffers,
1313
ICallbacks,
14+
uuid,
1415
} from '@jupyter-widgets/base';
1516

1617
import {
@@ -48,6 +49,16 @@ export const WIDGET_VIEW_MIMETYPE = 'application/vnd.jupyter.widget-view+json';
4849
export const WIDGET_STATE_MIMETYPE =
4950
'application/vnd.jupyter.widget-state+json';
5051

52+
/**
53+
* The control comm target name.
54+
*/
55+
export const CONTROL_COMM_TARGET = 'jupyter.widget.control';
56+
57+
/**
58+
* The supported version for the control comm channel.
59+
*/
60+
export const CONTROL_COMM_VERSION = '1.0.0';
61+
5162
/**
5263
* A widget manager that returns Lumino widgets.
5364
*/
@@ -98,6 +109,10 @@ export abstract class LabWidgetManager
98109
this._restoredStatus = false;
99110
}
100111

112+
/**
113+
* Fetch all widgets states using the control comm channel, or fallback to `_loadFromKernelSlow`
114+
* if the backend does not implement the control comm.
115+
*/
101116
protected async _loadFromKernel(): Promise<void> {
102117
if (!this.kernel) {
103118
throw new Error('Kernel not set');
@@ -106,6 +121,106 @@ export abstract class LabWidgetManager
106121
// A "load" for a kernel that does not handle comms does nothing.
107122
return;
108123
}
124+
125+
const commId = uuid();
126+
const initComm = await this._create_comm(
127+
CONTROL_COMM_TARGET,
128+
commId,
129+
{ widgets: null },
130+
{ version: CONTROL_COMM_VERSION }
131+
);
132+
133+
// Try fetching all widget states through the control comm
134+
let data: any;
135+
let buffers: any;
136+
try {
137+
await new Promise((resolve, reject) => {
138+
initComm.on_msg((msg) => {
139+
data = msg['content']['data'];
140+
141+
if (data.method !== 'update_states') {
142+
console.warn(`
143+
Unknown ${data.method} message on the Control channel
144+
`);
145+
return;
146+
}
147+
148+
buffers = (msg.buffers || []).map((b: any) => {
149+
if (b instanceof DataView) {
150+
return b;
151+
} else {
152+
return new DataView(b instanceof ArrayBuffer ? b : b.buffer);
153+
}
154+
});
155+
156+
resolve(null);
157+
});
158+
159+
initComm.on_close(reject);
160+
161+
// Send a states request msg
162+
initComm.send({ method: 'request_states' }, {});
163+
});
164+
} catch (error) {
165+
console.warn(
166+
'Failed to open "jupyter.widget.control" comm channel, fallback to slow fetching of widgets.',
167+
error
168+
);
169+
// Fallback to the old implementation for old ipywidgets backend versions (<=7.6)
170+
return this._loadFromKernelSlow();
171+
}
172+
173+
initComm.close();
174+
175+
const states: any = data.states;
176+
177+
// Extract buffer paths
178+
const bufferPaths: any = {};
179+
for (const bufferPath of data.buffer_paths) {
180+
if (!bufferPaths[bufferPath[0]]) {
181+
bufferPaths[bufferPath[0]] = [];
182+
}
183+
bufferPaths[bufferPath[0]].push(bufferPath.slice(1));
184+
}
185+
186+
// Start creating all widgets
187+
for (const [widget_id, state] of Object.entries(states) as any) {
188+
try {
189+
const comm = await this._create_comm('jupyter.widget', widget_id);
190+
191+
// Put binary buffers
192+
if (widget_id in bufferPaths) {
193+
const nBuffers = bufferPaths[widget_id].length;
194+
put_buffers(
195+
state,
196+
bufferPaths[widget_id],
197+
buffers.splice(0, nBuffers)
198+
);
199+
}
200+
201+
await this.new_model(
202+
{
203+
model_name: state.model_name,
204+
model_module: state.model_module,
205+
model_module_version: state.model_module_version,
206+
model_id: widget_id,
207+
comm: comm,
208+
},
209+
state.state
210+
);
211+
} catch (error) {
212+
// Failed to create a widget model, we continue creating other models so that
213+
// other widgets can render
214+
console.error(error);
215+
}
216+
}
217+
}
218+
219+
/**
220+
* Old implementation of fetching widgets one by one using
221+
* the request_state message on each comm.
222+
*/
223+
protected async _loadFromKernelSlow(): Promise<void> {
109224
const comm_ids = await this._get_comm_info();
110225

111226
// For each comm id that we do not know about, create the comm, and request the state.

0 commit comments

Comments
 (0)