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