@@ -11,6 +11,9 @@ var embedWidgets = require('./embed_widgets');
11
11
12
12
var MIME_TYPE = 'application/vnd.jupyter.widget-view+json' ;
13
13
14
+ var CONTROL_COMM_TARGET = 'jupyter.widget.control' ;
15
+ var CONTROL_COMM_VERSION = '1.0.0' ;
16
+
14
17
function polyfill_new_comm_buffers (
15
18
manager ,
16
19
target_name ,
@@ -99,9 +102,181 @@ export class WidgetManager extends ManagerBase {
99
102
this . handle_comm_open . bind ( this )
100
103
) ;
101
104
105
+ this . _loadFromKernel ( )
106
+ . then ( function ( ) {
107
+ // Now that we have mirrored any widgets from the kernel...
108
+ // Restore any widgets from saved state that are not live (3)
109
+ if (
110
+ widget_md &&
111
+ widget_md [ 'application/vnd.jupyter.widget-state+json' ]
112
+ ) {
113
+ var state =
114
+ notebook . metadata . widgets [
115
+ 'application/vnd.jupyter.widget-state+json'
116
+ ] ;
117
+ state = that . filterExistingModelState ( state ) ;
118
+ return that . set_state ( state ) ;
119
+ }
120
+ } )
121
+ . then ( function ( ) {
122
+ // Rerender cells that have widget data
123
+ that . notebook . get_cells ( ) . forEach ( function ( cell ) {
124
+ var rerender =
125
+ cell . output_area &&
126
+ cell . output_area . outputs . find ( function ( output ) {
127
+ return output . data && output . data [ MIME_TYPE ] ;
128
+ } ) ;
129
+ if ( rerender ) {
130
+ that . notebook . render_cell_output ( cell ) ;
131
+ }
132
+ } ) ;
133
+ } ) ;
134
+
135
+ // Create the actions and menu
136
+ this . _init_actions ( ) ;
137
+ this . _init_menu ( ) ;
138
+ }
139
+
140
+ loadClass ( className , moduleName , moduleVersion ) {
141
+ const failure = ( ) => {
142
+ throw new Error (
143
+ 'Class ' + className + ' not found in module ' + moduleName
144
+ ) ;
145
+ } ;
146
+ if ( moduleName === '@jupyter-widgets/controls' ) {
147
+ return widgets [ className ]
148
+ ? Promise . resolve ( widgets [ className ] )
149
+ : failure ( ) ;
150
+ } else if ( moduleName === '@jupyter-widgets/base' ) {
151
+ return base [ className ] ? Promise . resolve ( base [ className ] ) : failure ( ) ;
152
+ } else if ( moduleName == '@jupyter-widgets/output' ) {
153
+ return outputWidgets [ className ]
154
+ ? Promise . resolve ( outputWidgets [ className ] )
155
+ : failure ( ) ;
156
+ } else {
157
+ return new Promise ( function ( resolve , reject ) {
158
+ window . require ( [ moduleName ] , resolve , reject ) ;
159
+ } ) . then ( function ( mod ) {
160
+ if ( mod [ className ] ) {
161
+ return mod [ className ] ;
162
+ } else {
163
+ return failure ( ) ;
164
+ }
165
+ } ) ;
166
+ }
167
+ }
168
+
169
+ /**
170
+ * Fetch all widgets states using the control comm channel, or fallback to `_loadFromKernelSlow`
171
+ * if the backend does not implement the control comm.
172
+ */
173
+ _loadFromKernel ( ) {
174
+ const commId = uuid ( ) ;
175
+ const initComm = await this . _create_comm (
176
+ CONTROL_COMM_TARGET ,
177
+ commId ,
178
+ { widgets : null } ,
179
+ { version : CONTROL_COMM_VERSION }
180
+ ) ;
181
+
182
+ // Try fetching all widget states through the control comm
183
+ let data ;
184
+ let buffers ;
185
+ try {
186
+ await new Promise ( function ( resolve , reject ) {
187
+ initComm . on_msg ( function ( msg ) {
188
+ data = msg [ 'content' ] [ 'data' ] ;
189
+
190
+ if ( data . method !== 'update_states' ) {
191
+ console . warn ( `
192
+ Unknown ${ data . method } message on the Control channel
193
+ ` ) ;
194
+ return ;
195
+ }
196
+
197
+ buffers = ( msg . buffers || [ ] ) . map ( function ( b ) {
198
+ if ( b instanceof DataView ) {
199
+ return b ;
200
+ } else {
201
+ return new DataView ( b instanceof ArrayBuffer ? b : b . buffer ) ;
202
+ }
203
+ } ) ;
204
+
205
+ resolve ( null ) ;
206
+ } ) ;
207
+
208
+ initComm . on_close ( reject ) ;
209
+
210
+ // Send a states request msg
211
+ initComm . send ( { method : 'request_states' } , { } ) ;
212
+ } ) ;
213
+ } catch ( error ) {
214
+ console . warn (
215
+ 'Failed to open "jupyter.widget.control" comm channel, fallback to slow fetching of widgets.' ,
216
+ error
217
+ ) ;
218
+ // Fallback to the old implementation for old ipywidgets backend versions (<=7.6)
219
+ return this . _loadFromKernelSlow ( ) ;
220
+ }
221
+
222
+ initComm . close ( ) ;
223
+
224
+ const states = data . states ;
225
+
226
+ // Extract buffer paths
227
+ const bufferPaths = { } ;
228
+ for ( const bufferPath of data . buffer_paths ) {
229
+ if ( ! bufferPaths [ bufferPath [ 0 ] ] ) {
230
+ bufferPaths [ bufferPath [ 0 ] ] = [ ] ;
231
+ }
232
+ bufferPaths [ bufferPath [ 0 ] ] . push ( bufferPath . slice ( 1 ) ) ;
233
+ }
234
+
235
+ // Start creating all widgets
236
+ return Promise . all ( Object . keys ( states ) . map ( function ( widget_id ) {
237
+ const state = states [ widget_id ] ;
238
+
239
+ try {
240
+ return this . _create_comm ( 'jupyter.widget' , widget_id ) . then ( function ( comm ) {
241
+ // Put binary buffers
242
+ if ( widget_id in bufferPaths ) {
243
+ const nBuffers = bufferPaths [ widget_id ] . length ;
244
+ base . put_buffers (
245
+ state ,
246
+ bufferPaths [ widget_id ] ,
247
+ buffers . splice ( 0 , nBuffers )
248
+ ) ;
249
+ }
250
+
251
+ return this . new_model (
252
+ {
253
+ model_name : state . model_name ,
254
+ model_module : state . model_module ,
255
+ model_module_version : state . model_module_version ,
256
+ model_id : widget_id ,
257
+ comm : comm ,
258
+ } ,
259
+ state . state
260
+ ) ;
261
+ } ) ;
262
+ } catch ( error ) {
263
+ // Failed to create a widget model, we continue creating other models so that
264
+ // other widgets can render
265
+ console . error ( error ) ;
266
+ }
267
+ } ) ) ;
268
+
269
+ console . log ( 'Successfully loaded widgets through control channel' )
270
+ }
271
+
272
+ /**
273
+ * Old implementation of fetching widgets one by one using
274
+ * the request_state message on each comm.
275
+ */
276
+ _loadFromKernelSlow ( ) {
102
277
// Attempt to reconstruct any live comms by requesting them from the back-end (2).
103
278
var that = this ;
104
- this . _get_comm_info ( ) . then ( function ( comm_ids ) {
279
+ return this . _get_comm_info ( ) . then ( function ( comm_ids ) {
105
280
// Create comm class instances from comm ids (2).
106
281
var comm_promises = Object . keys ( comm_ids ) . map ( function ( comm_id ) {
107
282
return that . _create_comm ( that . comm_target_name , comm_id ) ;
@@ -157,68 +332,7 @@ export class WidgetManager extends ManagerBase {
157
332
} )
158
333
) ;
159
334
} )
160
- . then ( function ( ) {
161
- // Now that we have mirrored any widgets from the kernel...
162
- // Restore any widgets from saved state that are not live (3)
163
- if (
164
- widget_md &&
165
- widget_md [ 'application/vnd.jupyter.widget-state+json' ]
166
- ) {
167
- var state =
168
- notebook . metadata . widgets [
169
- 'application/vnd.jupyter.widget-state+json'
170
- ] ;
171
- state = that . filterExistingModelState ( state ) ;
172
- return that . set_state ( state ) ;
173
- }
174
- } )
175
- . then ( function ( ) {
176
- // Rerender cells that have widget data
177
- that . notebook . get_cells ( ) . forEach ( function ( cell ) {
178
- var rerender =
179
- cell . output_area &&
180
- cell . output_area . outputs . find ( function ( output ) {
181
- return output . data && output . data [ MIME_TYPE ] ;
182
- } ) ;
183
- if ( rerender ) {
184
- that . notebook . render_cell_output ( cell ) ;
185
- }
186
- } ) ;
187
- } ) ;
188
335
} ) ;
189
-
190
- // Create the actions and menu
191
- this . _init_actions ( ) ;
192
- this . _init_menu ( ) ;
193
- }
194
-
195
- loadClass ( className , moduleName , moduleVersion ) {
196
- const failure = ( ) => {
197
- throw new Error (
198
- 'Class ' + className + ' not found in module ' + moduleName
199
- ) ;
200
- } ;
201
- if ( moduleName === '@jupyter-widgets/controls' ) {
202
- return widgets [ className ]
203
- ? Promise . resolve ( widgets [ className ] )
204
- : failure ( ) ;
205
- } else if ( moduleName === '@jupyter-widgets/base' ) {
206
- return base [ className ] ? Promise . resolve ( base [ className ] ) : failure ( ) ;
207
- } else if ( moduleName == '@jupyter-widgets/output' ) {
208
- return outputWidgets [ className ]
209
- ? Promise . resolve ( outputWidgets [ className ] )
210
- : failure ( ) ;
211
- } else {
212
- return new Promise ( function ( resolve , reject ) {
213
- window . require ( [ moduleName ] , resolve , reject ) ;
214
- } ) . then ( function ( mod ) {
215
- if ( mod [ className ] ) {
216
- return mod [ className ] ;
217
- } else {
218
- return failure ( ) ;
219
- }
220
- } ) ;
221
- }
222
336
}
223
337
224
338
/**
0 commit comments