Skip to content

Commit 3502c8d

Browse files
committed
Fix ipywidgets 7.7 incompatibility issue
1 parent 02428d5 commit 3502c8d

File tree

4 files changed

+770
-817
lines changed

4 files changed

+770
-817
lines changed

packages/voila/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
"main": "lib/index.js",
99
"browserslist": ">0.8%, not ie 11, not op_mini all, not dead",
1010
"dependencies": {
11-
"@jupyter-widgets/base": "^4.0.0",
12-
"@jupyter-widgets/controls": "^3.0.0",
13-
"@jupyter-widgets/jupyterlab-manager": "^3.0.0",
11+
"@jupyter-widgets/base": "^4.1.0",
12+
"@jupyter-widgets/controls": "^3.1.0",
13+
"@jupyter-widgets/jupyterlab-manager": "^3.1.0",
1414
"@jupyterlab/application": "^3.0.0",
1515
"@jupyterlab/apputils": "^3.0.0",
1616
"@jupyterlab/coreutils": "^5.0.0",

packages/voila/src/manager.ts

Lines changed: 2 additions & 192 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,6 @@ import { Widget } from '@lumino/widgets';
4040

4141
import { requireLoader } from './loader';
4242

43-
import { batchRateMap } from './utils';
44-
4543
if (typeof window !== 'undefined' && typeof window.define !== 'undefined') {
4644
window.define('@jupyter-widgets/base', base);
4745
window.define('@jupyter-widgets/controls', controls);
@@ -98,7 +96,7 @@ export class WidgetManager extends JupyterLabManager {
9896
}
9997

10098
async build_widgets(): Promise<void> {
101-
const models = await this._build_models();
99+
await this._loadFromKernel();
102100
const tags = document.body.querySelectorAll(
103101
'script[type="application/vnd.jupyter.widget-view+json"]'
104102
);
@@ -110,7 +108,7 @@ export class WidgetManager extends JupyterLabManager {
110108
try {
111109
const widgetViewObject = JSON.parse(viewtag.innerHTML);
112110
const { model_id } = widgetViewObject;
113-
const model = models[model_id];
111+
const model = await this.get_model(model_id);
114112
const widgetel = document.createElement('div');
115113
viewtag.parentElement.insertBefore(widgetel, viewtag);
116114
// TODO: fix typing
@@ -199,193 +197,5 @@ export class WidgetManager extends JupyterLabManager {
199197
});
200198
}
201199

202-
/**
203-
* This is the implementation of building widgets models making use of the
204-
* jupyter.widget.control comm channel
205-
*/
206-
async _build_models(): Promise<{ [key: string]: base.WidgetModel }> {
207-
const models: { [key: string]: base.WidgetModel } = {};
208-
const commId = base.uuid();
209-
const initComm = await this._create_comm(
210-
'jupyter.widget.control',
211-
commId,
212-
{ widgets: null },
213-
{ version: '1.0.0' }
214-
);
215-
216-
// Fetch widget states
217-
let data: any;
218-
let buffers: any;
219-
try {
220-
await new Promise((resolve, reject) => {
221-
initComm.on_msg(msg => {
222-
data = msg['content']['data'];
223-
224-
if (data.method !== 'update_states') {
225-
console.warn(`
226-
Unknown ${data.method} message on the Control channel
227-
`);
228-
return;
229-
}
230-
231-
buffers = (msg.buffers || []).map((b: any) => {
232-
if (b instanceof DataView) {
233-
return b;
234-
} else {
235-
return new DataView(b instanceof ArrayBuffer ? b : b.buffer);
236-
}
237-
});
238-
239-
resolve(null);
240-
});
241-
242-
initComm.on_close(reject);
243-
244-
// Send a states request msg
245-
initComm.send({ method: 'request_states' }, {});
246-
247-
// Reject if we didn't get a response in time
248-
setTimeout(
249-
() => reject('Control comm did not respond in time'),
250-
CONTROL_COMM_TIMEOUT
251-
);
252-
});
253-
} catch (error) {
254-
console.warn(
255-
'Failed to open "jupyter.widget.control" comm channel, fallback to slow fetching of widgets.',
256-
error
257-
);
258-
// Fallback to the old implementation for old ipywidgets versions (<=7.6)
259-
return this._build_models_slow();
260-
}
261-
262-
initComm.close();
263-
264-
const states: any = data.states;
265-
266-
// Extract buffer paths
267-
// Why do we have to do this? Is there another way?
268-
const bufferPaths: any = {};
269-
for (const bufferPath of data.buffer_paths) {
270-
if (!bufferPaths[bufferPath[0]]) {
271-
bufferPaths[bufferPath[0]] = [];
272-
}
273-
bufferPaths[bufferPath[0]].push(bufferPath.slice(1));
274-
}
275-
276-
const widgetPromises: Promise<base.WidgetModel>[] = [];
277-
278-
// Start creating all widgets
279-
for (const [widget_id, state] of Object.entries(states) as any) {
280-
try {
281-
const comm = await this._create_comm('jupyter.widget', widget_id);
282-
283-
// Put binary buffers
284-
if (widget_id in bufferPaths) {
285-
const nBuffers = bufferPaths[widget_id].length;
286-
base.put_buffers(
287-
state,
288-
bufferPaths[widget_id],
289-
buffers.splice(0, nBuffers)
290-
);
291-
}
292-
293-
const modelPromise = this.new_model(
294-
{
295-
model_name: state.model_name,
296-
model_module: state.model_module,
297-
model_module_version: state.model_module_version,
298-
model_id: widget_id,
299-
comm: comm
300-
},
301-
state.state
302-
);
303-
widgetPromises.push(modelPromise);
304-
} catch (error) {
305-
// Failed to create a widget model, we continue creating other models so that
306-
// other widgets can render
307-
console.error(error);
308-
}
309-
}
310-
311-
// Wait for widgets to be created
312-
const widgets = await Promise.all(widgetPromises);
313-
for (const model of widgets) {
314-
models[model.model_id] = model;
315-
}
316-
317-
return models;
318-
}
319-
320-
/**
321-
* This is the old implementation of building widgets models
322-
* We keep it around for supporting old ipywidgets versions (<=7.6)
323-
*/
324-
async _build_models_slow(): Promise<{ [key: string]: base.WidgetModel }> {
325-
const comm_ids = await this._get_comm_info();
326-
const models: { [key: string]: base.WidgetModel } = {};
327-
/**
328-
* For the classical notebook, iopub_msg_rate_limit=1000 (default)
329-
* And for zmq, we are affected by the default ZMQ_SNDHWM setting of 1000
330-
* See https://github.com/voila-dashboards/voila/issues/534 for a discussion
331-
*/
332-
const maxMessagesInTransit = 100; // really save limit compared to ZMQ_SNDHWM
333-
const maxMessagesPerSecond = 500; // lets be on the save side, in case the kernel sends more msg'es
334-
const widgets_info = await Promise.all(
335-
batchRateMap(
336-
Object.keys(comm_ids),
337-
async comm_id => {
338-
const comm = await this._create_comm(this.comm_target_name, comm_id);
339-
return this._update_comm(comm);
340-
},
341-
{ room: maxMessagesInTransit, rate: maxMessagesPerSecond }
342-
)
343-
);
344-
345-
await Promise.all(
346-
widgets_info.map(async widget_info => {
347-
const state = (widget_info as any).msg.content.data.state;
348-
try {
349-
const modelPromise = this.new_model(
350-
{
351-
model_name: state._model_name,
352-
model_module: state._model_module,
353-
model_module_version: state._model_module_version,
354-
comm: (widget_info as any).comm
355-
},
356-
state
357-
);
358-
const model = await modelPromise;
359-
models[model.model_id] = model;
360-
} catch (error) {
361-
// Failed to create a widget model, we continue creating other models so that
362-
// other widgets can render
363-
console.error(error);
364-
}
365-
})
366-
);
367-
return models;
368-
}
369-
370-
async _update_comm(
371-
comm: base.IClassicComm
372-
): Promise<{ comm: base.IClassicComm; msg: any }> {
373-
return new Promise((resolve, reject) => {
374-
comm.on_msg(async msg => {
375-
if (msg.content.data.buffer_paths) {
376-
base.put_buffers(
377-
msg.content.data.state,
378-
msg.content.data.buffer_paths,
379-
msg.buffers
380-
);
381-
}
382-
if (msg.content.data.method === 'update') {
383-
resolve({ comm: comm, msg: msg });
384-
}
385-
});
386-
comm.send({ method: 'request_state' }, {});
387-
});
388-
}
389-
390200
private _loader: (name: any, version: any) => Promise<any>;
391201
}

packages/voila/src/utils.ts

Lines changed: 0 additions & 35 deletions
This file was deleted.

0 commit comments

Comments
 (0)