@@ -11,6 +11,7 @@ import {
11
11
WidgetView ,
12
12
put_buffers ,
13
13
ICallbacks ,
14
+ uuid ,
14
15
} from '@jupyter-widgets/base' ;
15
16
16
17
import {
@@ -48,6 +49,16 @@ export const WIDGET_VIEW_MIMETYPE = 'application/vnd.jupyter.widget-view+json';
48
49
export const WIDGET_STATE_MIMETYPE =
49
50
'application/vnd.jupyter.widget-state+json' ;
50
51
52
+ /**
53
+ * The control comm target name.
54
+ */
55
+ export const CONTROL_COMM_TARGET = 'jupyter.widget.control' ;
56
+
57
+ /**
58
+ * The supported version for the control comm channel.
59
+ */
60
+ export const CONTROL_COMM_VERSION = '1.0.0' ;
61
+
51
62
/**
52
63
* A widget manager that returns Lumino widgets.
53
64
*/
@@ -98,6 +109,10 @@ export abstract class LabWidgetManager
98
109
this . _restoredStatus = false ;
99
110
}
100
111
112
+ /**
113
+ * Fetch all widgets states using the control comm channel, or fallback to `_loadFromKernelSlow`
114
+ * if the backend does not implement the control comm.
115
+ */
101
116
protected async _loadFromKernel ( ) : Promise < void > {
102
117
if ( ! this . kernel ) {
103
118
throw new Error ( 'Kernel not set' ) ;
@@ -106,6 +121,106 @@ export abstract class LabWidgetManager
106
121
// A "load" for a kernel that does not handle comms does nothing.
107
122
return ;
108
123
}
124
+
125
+ const commId = uuid ( ) ;
126
+ const initComm = await this . _create_comm (
127
+ CONTROL_COMM_TARGET ,
128
+ commId ,
129
+ { widgets : null } ,
130
+ { version : CONTROL_COMM_VERSION }
131
+ ) ;
132
+
133
+ // Try fetching all widget states through the control comm
134
+ let data : any ;
135
+ let buffers : any ;
136
+ try {
137
+ await new Promise ( ( resolve , reject ) => {
138
+ initComm . on_msg ( ( msg ) => {
139
+ data = msg [ 'content' ] [ 'data' ] ;
140
+
141
+ if ( data . method !== 'update_states' ) {
142
+ console . warn ( `
143
+ Unknown ${ data . method } message on the Control channel
144
+ ` ) ;
145
+ return ;
146
+ }
147
+
148
+ buffers = ( msg . buffers || [ ] ) . map ( ( b : any ) => {
149
+ if ( b instanceof DataView ) {
150
+ return b ;
151
+ } else {
152
+ return new DataView ( b instanceof ArrayBuffer ? b : b . buffer ) ;
153
+ }
154
+ } ) ;
155
+
156
+ resolve ( null ) ;
157
+ } ) ;
158
+
159
+ initComm . on_close ( reject ) ;
160
+
161
+ // Send a states request msg
162
+ initComm . send ( { method : 'request_states' } , { } ) ;
163
+ } ) ;
164
+ } catch ( error ) {
165
+ console . warn (
166
+ 'Failed to open "jupyter.widget.control" comm channel, fallback to slow fetching of widgets.' ,
167
+ error
168
+ ) ;
169
+ // Fallback to the old implementation for old ipywidgets backend versions (<=7.6)
170
+ return this . _loadFromKernelSlow ( ) ;
171
+ }
172
+
173
+ initComm . close ( ) ;
174
+
175
+ const states : any = data . states ;
176
+
177
+ // Extract buffer paths
178
+ const bufferPaths : any = { } ;
179
+ for ( const bufferPath of data . buffer_paths ) {
180
+ if ( ! bufferPaths [ bufferPath [ 0 ] ] ) {
181
+ bufferPaths [ bufferPath [ 0 ] ] = [ ] ;
182
+ }
183
+ bufferPaths [ bufferPath [ 0 ] ] . push ( bufferPath . slice ( 1 ) ) ;
184
+ }
185
+
186
+ // Start creating all widgets
187
+ for ( const [ widget_id , state ] of Object . entries ( states ) as any ) {
188
+ try {
189
+ const comm = await this . _create_comm ( 'jupyter.widget' , widget_id ) ;
190
+
191
+ // Put binary buffers
192
+ if ( widget_id in bufferPaths ) {
193
+ const nBuffers = bufferPaths [ widget_id ] . length ;
194
+ put_buffers (
195
+ state ,
196
+ bufferPaths [ widget_id ] ,
197
+ buffers . splice ( 0 , nBuffers )
198
+ ) ;
199
+ }
200
+
201
+ await this . new_model (
202
+ {
203
+ model_name : state . model_name ,
204
+ model_module : state . model_module ,
205
+ model_module_version : state . model_module_version ,
206
+ model_id : widget_id ,
207
+ comm : comm ,
208
+ } ,
209
+ state . state
210
+ ) ;
211
+ } catch ( error ) {
212
+ // Failed to create a widget model, we continue creating other models so that
213
+ // other widgets can render
214
+ console . error ( error ) ;
215
+ }
216
+ }
217
+ }
218
+
219
+ /**
220
+ * Old implementation of fetching widgets one by one using
221
+ * the request_state message on each comm.
222
+ */
223
+ protected async _loadFromKernelSlow ( ) : Promise < void > {
109
224
const comm_ids = await this . _get_comm_info ( ) ;
110
225
111
226
// For each comm id that we do not know about, create the comm, and request the state.
0 commit comments