Skip to content

Commit e2b7ecb

Browse files
committed
A fix for a race condition in restoring widgets.
Right now in JupyterLab, if you create and display a widget in a notebook, then refresh the page, the widget is replaced with a “could not find model” error message. This is because the initial kernel widget restore is called before the widget manager has a kernel, so the kernel restore silently fails and the widget manager thinks it has restored things. Then when we try to render the widgets, we give up too soon, thinking that we have already restored any existing widget state. This change makes it so that the widget manager recognizes that the initial restore attempt did happen (even if it didn’t actually get anything from the kernel because the kernel was not set), but the manager also realizes that the kernel restore did *not* happen. Then when the kernel is set, the kernel connected signal triggers another restore, which can then complete the widget manager restoration.
1 parent 83f7925 commit e2b7ecb

File tree

1 file changed

+25
-12
lines changed

1 file changed

+25
-12
lines changed

packages/jupyterlab-manager/src/manager.ts

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -136,9 +136,10 @@ export abstract class LabWidgetManager extends ManagerBase<Widget>
136136

137137
protected async _loadFromKernel(): Promise<void> {
138138
if (!this.kernel) {
139-
return;
139+
throw new Error('Kernel not set');
140140
}
141141
if (this.kernel?.handleComms === false) {
142+
// A "load" for a kernel that does not handle comms does nothing.
142143
return;
143144
}
144145
const comm_ids = await this._get_comm_info();
@@ -504,10 +505,14 @@ export class KernelWidgetManager extends LabWidgetManager {
504505
* Restore widgets from kernel and saved state.
505506
*/
506507
async restoreWidgets(): Promise<void> {
507-
await this._loadFromKernel();
508-
this._restoredStatus = true;
509-
this._initialRestoredStatus = true;
510-
this._restored.emit();
508+
try {
509+
await this._loadFromKernel();
510+
this._restoredStatus = true;
511+
this._initialRestoredStatus = true;
512+
this._restored.emit();
513+
} catch (err) {
514+
// Do nothing
515+
}
511516
}
512517

513518
/**
@@ -607,15 +612,23 @@ export class WidgetManager extends LabWidgetManager {
607612
notebook: INotebookModel,
608613
{ loadKernel, loadNotebook } = { loadKernel: true, loadNotebook: true }
609614
): Promise<void> {
610-
if (loadKernel) {
611-
await this._loadFromKernel();
612-
}
613-
if (loadNotebook) {
614-
await this._loadFromNotebook(notebook);
615+
try {
616+
if (loadKernel) {
617+
await this._loadFromKernel();
618+
}
619+
if (loadNotebook) {
620+
await this._loadFromNotebook(notebook);
621+
}
622+
623+
// If the restore worked above, then update our state.
624+
this._restoredStatus = true;
625+
this._restored.emit();
626+
} catch (err) {
627+
// Do nothing if the restore did not work.
615628
}
616-
this._restoredStatus = true;
629+
// We *always* claim that the initial restore happened, so when we connect
630+
// to a kernel, we will initiate another restore. Maybe this should really be a "restore in progress" variable?
617631
this._initialRestoredStatus = true;
618-
this._restored.emit();
619632
}
620633

621634
/**

0 commit comments

Comments
 (0)