@@ -210,15 +210,26 @@ export abstract class ManagerBase implements IWidgetManager {
210
210
* Get a promise for a model by model id.
211
211
*
212
212
* #### Notes
213
- * If a model is not found, undefined is returned (NOT a promise). However,
214
- * the calling code should also deal with the case where a rejected promise
215
- * is returned, and should treat that also as a model not found.
213
+ * If the model is not found, the returned Promise object is rejected.
214
+ *
215
+ * If you would like to synchronously test if a model exists, use .has_model().
216
+ */
217
+ async get_model ( model_id : string ) : Promise < WidgetModel > {
218
+ const modelPromise = this . _models [ model_id ] ;
219
+ if ( modelPromise === undefined ) {
220
+ throw new Error ( 'widget model not found' ) ;
221
+ }
222
+ return modelPromise ;
223
+ }
224
+
225
+ /**
226
+ * Returns true if the given model is registered, otherwise false.
227
+ *
228
+ * #### Notes
229
+ * This is a synchronous way to check if a model is registered.
216
230
*/
217
- get_model ( model_id : string ) : Promise < WidgetModel > | undefined {
218
- // TODO: Perhaps we should return a Promise.reject if the model is not
219
- // found. Right now this isn't a true async function because it doesn't
220
- // always return a promise.
221
- return this . _models [ model_id ] ;
231
+ has_model ( model_id : string ) : boolean {
232
+ return this . _models [ model_id ] !== undefined ;
222
233
}
223
234
224
235
/**
@@ -364,7 +375,7 @@ export abstract class ManagerBase implements IWidgetManager {
364
375
/**
365
376
* Fetch all widgets states from the kernel using the control comm channel
366
377
* If this fails (control comm handler not implemented kernel side),
367
- * it will fallback to `_loadFromKernelSlow `.
378
+ * it will fall back to `_loadFromKernelModels `.
368
379
*
369
380
* This is a utility function that can be used in subclasses.
370
381
*/
@@ -417,51 +428,67 @@ export abstract class ManagerBase implements IWidgetManager {
417
428
initComm . close ( ) ;
418
429
} catch ( error ) {
419
430
console . warn (
420
- 'Failed to fetch widgets through the "jupyter.widget.control" comm channel, fallback to slow fetching of widgets . Reason:' ,
431
+ 'Failed to fetch ipywidgets through the "jupyter.widget.control" comm channel, fallback to fetching individual model state . Reason:' ,
421
432
error
422
433
) ;
423
- // Fallback to the old implementation for old ipywidgets backend versions (<=7.6)
424
- return this . _loadFromKernelSlow ( ) ;
434
+ // Fall back to the old implementation for old ipywidgets backend versions (ipywidgets <=7.6)
435
+ return this . _loadFromKernelModels ( ) ;
425
436
}
426
437
427
438
const states : any = data . states ;
428
-
429
- // Extract buffer paths
430
439
const bufferPaths : any = { } ;
431
- for ( const bufferPath of data . buffer_paths ) {
432
- if ( ! bufferPaths [ bufferPath [ 0 ] ] ) {
433
- bufferPaths [ bufferPath [ 0 ] ] = [ ] ;
440
+ const bufferGroups : any = { } ;
441
+
442
+ // Group buffers and buffer paths by widget id
443
+ for ( let i = 0 ; i < data . buffer_paths . length ; i ++ ) {
444
+ const [ widget_id , ...path ] = data . buffer_paths [ i ] ;
445
+ const b = buffers [ i ] ;
446
+ if ( ! bufferPaths [ widget_id ] ) {
447
+ bufferPaths [ widget_id ] = [ ] ;
448
+ bufferGroups [ widget_id ] = [ ] ;
434
449
}
435
- bufferPaths [ bufferPath [ 0 ] ] . push ( bufferPath . slice ( 1 ) ) ;
450
+ bufferPaths [ widget_id ] . push ( path ) ;
451
+ bufferGroups [ widget_id ] . push ( b ) ;
436
452
}
437
453
438
- // Start creating all widgets
439
- await Promise . all (
454
+ // Create comms for all new widgets.
455
+ const widget_comms = await Promise . all (
440
456
Object . keys ( states ) . map ( async ( widget_id ) => {
457
+ const comm = this . has_model ( widget_id )
458
+ ? undefined
459
+ : await this . _create_comm ( 'jupyter.widget' , widget_id ) ;
460
+ return { widget_id, comm } ;
461
+ } )
462
+ ) ;
463
+
464
+ await Promise . all (
465
+ widget_comms . map ( async ( { widget_id, comm } ) => {
466
+ const state = states [ widget_id ] ;
467
+ // Put binary buffers
468
+ if ( widget_id in bufferPaths ) {
469
+ put_buffers ( state , bufferPaths [ widget_id ] , bufferGroups [ widget_id ] ) ;
470
+ }
441
471
try {
442
- const state = states [ widget_id ] ;
443
- const comm = await this . _create_comm ( 'jupyter.widget' , widget_id ) ;
444
-
445
- // Put binary buffers
446
- if ( widget_id in bufferPaths ) {
447
- const nBuffers = bufferPaths [ widget_id ] . length ;
448
- put_buffers (
449
- state ,
450
- bufferPaths [ widget_id ] ,
451
- buffers . splice ( 0 , nBuffers )
472
+ if ( comm ) {
473
+ // This must be the first await in the code path that
474
+ // reaches here so that registering the model promise in
475
+ // new_model can register the widget promise before it may
476
+ // be required by other widgets.
477
+ await this . new_model (
478
+ {
479
+ model_name : state . model_name ,
480
+ model_module : state . model_module ,
481
+ model_module_version : state . model_module_version ,
482
+ model_id : widget_id ,
483
+ comm : comm ,
484
+ } ,
485
+ state . state
452
486
) ;
487
+ } else {
488
+ // model already exists here
489
+ const model = await this . get_model ( widget_id ) ;
490
+ model ! . set_state ( state . state ) ;
453
491
}
454
-
455
- await this . new_model (
456
- {
457
- model_name : state . model_name ,
458
- model_module : state . model_module ,
459
- model_module_version : state . model_module_version ,
460
- model_id : widget_id ,
461
- comm : comm ,
462
- } ,
463
- state . state
464
- ) ;
465
492
} catch ( error ) {
466
493
// Failed to create a widget model, we continue creating other models so that
467
494
// other widgets can render
@@ -472,63 +499,46 @@ export abstract class ManagerBase implements IWidgetManager {
472
499
}
473
500
474
501
/**
475
- * Old implementation of fetching widgets one by one using
502
+ * Old implementation of fetching widget models one by one using
476
503
* the request_state message on each comm.
477
504
*
478
505
* This is a utility function that can be used in subclasses.
479
506
*/
480
- protected async _loadFromKernelSlow ( ) : Promise < void > {
507
+ protected async _loadFromKernelModels ( ) : Promise < void > {
481
508
const comm_ids = await this . _get_comm_info ( ) ;
482
509
483
510
// For each comm id that we do not know about, create the comm, and request the state.
484
511
const widgets_info = await Promise . all (
485
512
Object . keys ( comm_ids ) . map ( async ( comm_id ) => {
486
- try {
487
- const model = this . get_model ( comm_id ) ;
488
- // TODO Have the same this.get_model implementation for
489
- // the widgetsnbextension and labextension, the one that
490
- // throws an error if the model is not found instead of
491
- // returning undefined
492
- if ( model === undefined ) {
493
- throw new Error ( 'widget model not found' ) ;
494
- }
495
- await model ;
496
- // If we successfully get the model, do no more.
513
+ if ( this . has_model ( comm_id ) ) {
497
514
return ;
498
- } catch ( e ) {
499
- // If we have the widget model not found error, then we can create the
500
- // widget. Otherwise, rethrow the error. We have to check the error
501
- // message text explicitly because the get_model function in this
502
- // class throws a generic error with this specific text.
503
- if ( e . message !== 'widget model not found' ) {
504
- throw e ;
515
+ }
516
+
517
+ const comm = await this . _create_comm ( this . comm_target_name , comm_id ) ;
518
+
519
+ let msg_id = '' ;
520
+ const info = new PromiseDelegate < Private . ICommUpdateData > ( ) ;
521
+ comm . on_msg ( ( msg : services . KernelMessage . ICommMsgMsg ) => {
522
+ if (
523
+ ( msg . parent_header as any ) . msg_id === msg_id &&
524
+ msg . header . msg_type === 'comm_msg' &&
525
+ msg . content . data . method === 'update'
526
+ ) {
527
+ const data = msg . content . data as any ;
528
+ const buffer_paths = data . buffer_paths || [ ] ;
529
+ const buffers = msg . buffers || [ ] ;
530
+ put_buffers ( data . state , buffer_paths , buffers ) ;
531
+ info . resolve ( { comm, msg } ) ;
505
532
}
506
- const comm = await this . _create_comm ( this . comm_target_name , comm_id ) ;
507
-
508
- let msg_id = '' ;
509
- const info = new PromiseDelegate < Private . ICommUpdateData > ( ) ;
510
- comm . on_msg ( ( msg : services . KernelMessage . ICommMsgMsg ) => {
511
- if (
512
- ( msg . parent_header as any ) . msg_id === msg_id &&
513
- msg . header . msg_type === 'comm_msg' &&
514
- msg . content . data . method === 'update'
515
- ) {
516
- const data = msg . content . data as any ;
517
- const buffer_paths = data . buffer_paths || [ ] ;
518
- const buffers = msg . buffers || [ ] ;
519
- put_buffers ( data . state , buffer_paths , buffers ) ;
520
- info . resolve ( { comm, msg } ) ;
521
- }
522
- } ) ;
523
- msg_id = comm . send (
524
- {
525
- method : 'request_state' ,
526
- } ,
527
- this . callbacks ( undefined )
528
- ) ;
533
+ } ) ;
534
+ msg_id = comm . send (
535
+ {
536
+ method : 'request_state' ,
537
+ } ,
538
+ this . callbacks ( undefined )
539
+ ) ;
529
540
530
- return info . promise ;
531
- }
541
+ return info . promise ;
532
542
} )
533
543
) ;
534
544
@@ -689,8 +699,8 @@ export abstract class ManagerBase implements IWidgetManager {
689
699
690
700
// If the model has already been created, set its state and then
691
701
// return it.
692
- if ( this . _models [ model_id ] !== undefined ) {
693
- return this . _models [ model_id ] . then ( ( model ) => {
702
+ if ( this . has_model ( model_id ) ) {
703
+ return this . get_model ( model_id ) ! . then ( ( model ) => {
694
704
// deserialize state
695
705
return ( model . constructor as typeof WidgetModel )
696
706
. _deserialize_state ( modelState || { } , this )
@@ -841,9 +851,7 @@ export abstract class ManagerBase implements IWidgetManager {
841
851
protected filterExistingModelState ( serialized_state : any ) : any {
842
852
let models = serialized_state . state ;
843
853
models = Object . keys ( models )
844
- . filter ( ( model_id ) => {
845
- return ! this . _models [ model_id ] ;
846
- } )
854
+ . filter ( ( model_id ) => ! this . has_model ( model_id ) )
847
855
. reduce < IManagerStateMap > ( ( res , model_id ) => {
848
856
res [ model_id ] = models [ model_id ] ;
849
857
return res ;
0 commit comments