Skip to content

Commit 281479c

Browse files
authored
Merge pull request #3377 from jasongrout/cl
Introduce synchronous has_model function in the manager base class.
2 parents c419bfe + c599dca commit 281479c

File tree

2 files changed

+65
-91
lines changed

2 files changed

+65
-91
lines changed

docs/source/changelog.md

+3
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,12 @@ Highlights include:
1313
- Fix installation on Python 3.10. [#3368](https://github.com/jupyter-widgets/ipywidgets/pull/3368)
1414
- Throw an error if we cannot render a widget, enabling the rendering system to fall back to rendering a different data type if available. [#3290](https://github.com/jupyter-widgets/ipywidgets/pull/3290)
1515
- Create a new widget control comm channel, enabling more efficient fetching of kernel widget state. [#3201](https://github.com/jupyter-widgets/ipywidgets/pull/3021)
16+
- Refactor logic for fetching kernel widget state to the manager base class. This logic first tries to use the new widget control comm channel, falling back to the existing method of requesting each widget's state individually. [#3337](https://github.com/jupyter-widgets/ipywidgets/pull/3337)
1617
- Enable HTMLManager output widgets to render state updates. [#3372](https://github.com/jupyter-widgets/ipywidgets/pull/3372)
1718
- Do not reset JupyterLab CSS variables if they are already defined. [#3344](https://github.com/jupyter-widgets/ipywidgets/pull/3344)
1819
- Fix variable inspector example. [#3302](https://github.com/jupyter-widgets/ipywidgets/pull/3302)
20+
- Introduce new widget manager `has_model` method for synchronously checking if a widget model is registered. [#3377](https://github.com/jupyter-widgets/ipywidgets/pull/3377)
21+
- Work around bug in Chrome rendering Combobox arrows. [#3375](https://github.com/jupyter-widgets/ipywidgets/pull/3375)
1922

2023
7.6
2124
---

packages/base/src/manager-base.ts

+62-91
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,16 @@ abstract class ManagerBase<T> {
224224
return this._models[model_id];
225225
}
226226

227+
/**
228+
* Returns true if the given model is registered, otherwise false.
229+
*
230+
* #### Notes
231+
* This is a synchronous way to check if a model is registered.
232+
*/
233+
has_model(model_id: string): boolean {
234+
return this._models[model_id] !== undefined;
235+
}
236+
227237
/**
228238
* Handle when a comm is opened.
229239
*/
@@ -464,27 +474,10 @@ abstract class ManagerBase<T> {
464474
// Create comms for all new widgets.
465475
let widget_comms = await Promise.all(
466476
Object.keys(states).map(async (widget_id) => {
467-
let comm = undefined;
468-
let modelPromise = undefined;
469-
try {
470-
modelPromise = this.get_model(widget_id);
471-
if (modelPromise === undefined) {
472-
comm = await this._create_comm('jupyter.widget', widget_id);
473-
} else {
474-
// For JLab, the promise is rejected, so we have to await to
475-
// find out if it is actually a model.
476-
await modelPromise;
477-
}
478-
} catch (e) {
479-
// The JLab widget manager will throw an error with this specific error message.
480-
if (e.message !== 'widget model not found') {
481-
throw e;
482-
}
483-
comm = await this._create_comm('jupyter.widget', widget_id);
484-
}
477+
const comm = this.has_model(widget_id) ? undefined : await this._create_comm('jupyter.widget', widget_id);
485478
return {widget_id, comm}
486479
})
487-
)
480+
);
488481

489482
await Promise.all(widget_comms.map(async ({widget_id, comm}) => {
490483
const state = states[widget_id];
@@ -497,28 +490,26 @@ abstract class ManagerBase<T> {
497490
);
498491
}
499492
try {
500-
501-
if (comm === undefined) {
502-
// model already exists here
503-
const model = await this.get_model(widget_id);
504-
model!.set_state(state.state);
505-
} else {
506-
// This must be the first await in the code path that
507-
// reaches here so that registering the model promise in
508-
// new_model can register the widget promise before it may
509-
// be required by other widgets.
510-
await this.new_model(
511-
{
512-
model_name: state.model_name,
513-
model_module: state.model_module,
514-
model_module_version: state.model_module_version,
515-
model_id: widget_id,
516-
comm: comm,
517-
},
518-
state.state
519-
);
520-
}
521-
493+
if (comm) {
494+
// This must be the first await in the code path that
495+
// reaches here so that registering the model promise in
496+
// new_model can register the widget promise before it may
497+
// be required by other widgets.
498+
await this.new_model(
499+
{
500+
model_name: state.model_name,
501+
model_module: state.model_module,
502+
model_module_version: state.model_module_version,
503+
model_id: widget_id,
504+
comm: comm,
505+
},
506+
state.state
507+
);
508+
} else {
509+
// model already exists here
510+
const model = await this.get_model(widget_id);
511+
model!.set_state(state.state);
512+
}
522513
} catch (error) {
523514
// Failed to create a widget model, we continue creating other models so that
524515
// other widgets can render
@@ -539,54 +530,36 @@ abstract class ManagerBase<T> {
539530
// For each comm id that we do not know about, create the comm, and request the state.
540531
const widgets_info = await Promise.all(
541532
Object.keys(comm_ids).map(async (comm_id) => {
542-
try {
543-
const model = this.get_model(comm_id);
544-
// TODO Have the same this.get_model implementation for
545-
// the widgetsnbextension and labextension, the one that
546-
// throws an error if the model is not found instead of
547-
// returning undefined
548-
if (model === undefined) {
549-
throw new Error('widget model not found');
550-
}
551-
await model;
552-
// If we successfully get the model, do no more.
533+
if (this.has_model(comm_id)) {
553534
return;
554-
} catch (e) {
555-
// If we have the widget model not found error, then we can create the
556-
// widget. Otherwise, rethrow the error. We have to check the error
557-
// message text explicitly because the get_model function in this
558-
// class throws a generic error with this specific text.
559-
if (e.message !== 'widget model not found') {
560-
throw e;
535+
}
536+
537+
const comm = await this._create_comm(this.comm_target_name, comm_id);
538+
539+
let msg_id = '';
540+
const info = new PromiseDelegate<Private.ICommUpdateData>();
541+
comm.on_msg((msg) => {
542+
if (
543+
(msg.parent_header as any).msg_id === msg_id &&
544+
msg.header.msg_type === 'comm_msg' &&
545+
msg.content.data.method === 'update'
546+
) {
547+
const data = msg.content.data as any;
548+
const buffer_paths = data.buffer_paths || [];
549+
const buffers = msg.buffers || [];
550+
utils.put_buffers(data.state, buffer_paths, buffers);
551+
info.resolve({ comm, msg });
561552
}
562-
const comm = await this._create_comm(this.comm_target_name, comm_id);
563-
564-
let msg_id = '';
565-
const info = new PromiseDelegate<Private.ICommUpdateData>();
566-
comm.on_msg((msg) => {
567-
if (
568-
(msg.parent_header as any).msg_id === msg_id &&
569-
msg.header.msg_type === 'comm_msg' &&
570-
msg.content.data.method === 'update'
571-
) {
572-
const data = msg.content.data as any;
573-
const buffer_paths = data.buffer_paths || [];
574-
const buffers = msg.buffers || [];
575-
utils.put_buffers(data.state, buffer_paths, buffers);
576-
info.resolve({ comm, msg });
577-
}
578-
});
579-
msg_id = comm.send(
580-
{
581-
method: 'request_state',
582-
},
583-
this.callbacks(undefined)
584-
);
553+
});
554+
msg_id = comm.send(
555+
{
556+
method: 'request_state',
557+
},
558+
this.callbacks(undefined)
559+
);
585560

586-
return info.promise;
587-
}
588-
})
589-
);
561+
return info.promise;
562+
}));
590563

591564
// We put in a synchronization barrier here so that we don't have to
592565
// topologically sort the restored widgets. `new_model` synchronously
@@ -681,8 +654,8 @@ abstract class ManagerBase<T> {
681654

682655
// If the model has already been created, set its state and then
683656
// return it.
684-
if (this._models[model_id]) {
685-
return this._models[model_id].then(model => {
657+
if (this.has_model(model_id)) {
658+
return this.get_model(model_id)!.then(model => {
686659
// deserialize state
687660
return (model.constructor as typeof WidgetModel)._deserialize_state(modelState || {}, this).then(attributes => {
688661
model.set_state(attributes); // case 2
@@ -774,9 +747,7 @@ abstract class ManagerBase<T> {
774747
protected filterExistingModelState(serialized_state: any) {
775748
let models = serialized_state.state as {[key: string]: any};
776749
models = Object.keys(models)
777-
.filter((model_id) => {
778-
return !this._models[model_id];
779-
})
750+
.filter(model_id => !this.has_model(model_id))
780751
.reduce((res, model_id) => {
781752
res[model_id] = models[model_id];
782753
return res;

0 commit comments

Comments
 (0)