0
# Communication
1
2
The communication layer provides bidirectional messaging between Jupyter widgets in the frontend and their corresponding models in the kernel through comm channels and message handling.
3
4
## Capabilities
5
6
### IClassicComm Interface
7
8
Core interface for communication channels between frontend widgets and kernel models.
9
10
```typescript { .api }
11
/**
12
* Interface for Jupyter comm communication channels
13
*/
14
interface IClassicComm {
15
/**
16
* Unique identifier for the communication channel
17
*/
18
comm_id: string;
19
20
/**
21
* Target name identifying the comm type
22
*/
23
target_name: string;
24
25
/**
26
* Open a communication channel to the kernel
27
* @param data - Initial data to send
28
* @param callbacks - Message callback handlers
29
* @param metadata - Additional message metadata
30
* @param buffers - Binary data buffers
31
* @returns Message ID of the open message
32
*/
33
open(
34
data: JSONValue,
35
callbacks?: ICallbacks,
36
metadata?: JSONObject,
37
buffers?: ArrayBuffer[] | ArrayBufferView[]
38
): string;
39
40
/**
41
* Send a message through the communication channel
42
* @param data - Message data payload
43
* @param callbacks - Message callback handlers
44
* @param metadata - Additional message metadata
45
* @param buffers - Binary data buffers
46
* @returns Message ID of the sent message
47
*/
48
send(
49
data: JSONValue,
50
callbacks?: ICallbacks,
51
metadata?: JSONObject,
52
buffers?: ArrayBuffer[] | ArrayBufferView[]
53
): string;
54
55
/**
56
* Close the communication channel
57
* @param data - Optional closing data
58
* @param callbacks - Message callback handlers
59
* @param metadata - Additional message metadata
60
* @param buffers - Binary data buffers
61
* @returns Message ID of the close message
62
*/
63
close(
64
data?: JSONValue,
65
callbacks?: ICallbacks,
66
metadata?: JSONObject,
67
buffers?: ArrayBuffer[] | ArrayBufferView[]
68
): string;
69
70
/**
71
* Register a handler for incoming messages
72
* @param callback - Function to handle received messages
73
*/
74
on_msg(callback: (x: any) => void): void;
75
76
/**
77
* Register a handler for comm close events
78
* @param callback - Function to handle comm closure
79
*/
80
on_close(callback: (x: any) => void): void;
81
}
82
```
83
84
**Usage Examples:**
85
86
```typescript
87
// Setup message handling
88
comm.on_msg((msg) => {
89
console.log('Received message:', msg);
90
// Handle different message types
91
switch (msg.content.method) {
92
case 'update':
93
handleStateUpdate(msg.content.state);
94
break;
95
case 'custom':
96
handleCustomMessage(msg.content.data);
97
break;
98
}
99
});
100
101
// Setup close handling
102
comm.on_close((msg) => {
103
console.log('Comm closed:', msg);
104
cleanup();
105
});
106
107
// Send messages with callbacks
108
const msgId = comm.send(
109
{ method: 'request_data', params: { type: 'summary' } },
110
{
111
shell: {
112
reply: (msg) => console.log('Got reply:', msg)
113
},
114
iopub: {
115
status: (msg) => console.log('Status:', msg.content.execution_state)
116
}
117
}
118
);
119
```
120
121
### Message Callbacks
122
123
Configuration for handling different types of kernel messages during comm operations.
124
125
```typescript { .api }
126
/**
127
* Callback handlers for different message channels
128
*/
129
interface ICallbacks {
130
/**
131
* Handlers for shell messages (replies, errors)
132
*/
133
shell?: {
134
[key: string]: (msg: KernelMessage.IShellMessage) => void
135
};
136
137
/**
138
* Handlers for IOPub messages (status, output, errors)
139
*/
140
iopub?: {
141
[key: string]: (msg: KernelMessage.IIOPubMessage) => void
142
};
143
144
/**
145
* Handler for stdin messages (input requests)
146
*/
147
input?: (msg: KernelMessage.IStdinMessage) => void;
148
}
149
```
150
151
**Usage Examples:**
152
153
```typescript
154
// Comprehensive callback setup
155
const callbacks: ICallbacks = {
156
shell: {
157
reply: (msg) => {
158
// Handle successful replies
159
console.log('Operation completed:', msg.content);
160
},
161
error: (msg) => {
162
// Handle errors
163
console.error('Operation failed:', msg.content);
164
}
165
},
166
iopub: {
167
status: (msg) => {
168
// Track kernel execution state
169
const state = msg.content.execution_state;
170
if (state === 'busy') {
171
showSpinner();
172
} else if (state === 'idle') {
173
hideSpinner();
174
}
175
},
176
execute_result: (msg) => {
177
// Handle execution results
178
displayResult(msg.content.data);
179
},
180
stream: (msg) => {
181
// Handle stdout/stderr output
182
appendOutput(msg.content.text);
183
},
184
error: (msg) => {
185
// Handle execution errors
186
displayError(msg.content.ename, msg.content.evalue, msg.content.traceback);
187
}
188
},
189
input: (msg) => {
190
// Handle input requests from kernel
191
const prompt = msg.content.prompt;
192
const response = await getUserInput(prompt);
193
// Send response back to kernel
194
}
195
};
196
197
// Use callbacks with comm operations
198
comm.send(data, callbacks);
199
```
200
201
## Services Shim Layer
202
203
Compatibility layer that provides the classic comm API while using modern @jupyterlab/services underneath.
204
205
### CommManager
206
207
Manager class for creating and registering communication channels.
208
209
```typescript { .api }
210
/**
211
* Manager for communication channels, provides compatibility with @jupyterlab/services
212
*/
213
class CommManager {
214
/**
215
* Initialize comm manager with kernel connection
216
* @param jsServicesKernel - @jupyterlab/services kernel connection
217
*/
218
constructor(jsServicesKernel: Kernel.IKernelConnection);
219
220
/**
221
* Setup or update kernel connection
222
* @param jsServicesKernel - @jupyterlab/services kernel connection
223
*/
224
init_kernel(jsServicesKernel: Kernel.IKernelConnection): void;
225
226
/**
227
* Create a new communication channel
228
* @param target_name - Comm target identifier
229
* @param data - Initial data to send
230
* @param callbacks - Message callbacks
231
* @param metadata - Message metadata
232
* @param comm_id - Specific comm ID to use
233
* @param buffers - Binary data buffers
234
* @returns Promise resolving to the created comm
235
*/
236
async new_comm(
237
target_name: string,
238
data: any,
239
callbacks: any,
240
metadata: any,
241
comm_id: string,
242
buffers?: ArrayBuffer[] | ArrayBufferView[]
243
): Promise<Comm>;
244
245
/**
246
* Register a handler for incoming comm creation requests
247
* @param target_name - Target name to handle
248
* @param f - Handler function receiving comm and message
249
*/
250
register_target(
251
target_name: string,
252
f: (comm: Comm, object: KernelMessage.IMessage) => void
253
): void;
254
255
/**
256
* Unregister a comm target handler
257
* @param target_name - Target name to unregister
258
* @param f - Handler function to remove
259
*/
260
unregister_target(
261
target_name: string,
262
f: (comm: Comm, object: KernelMessage.IMessage) => void
263
): void;
264
265
/**
266
* Register a comm instance with the manager
267
* @param comm - Comm instance to register
268
* @returns Comm ID
269
*/
270
register_comm(comm: Comm): string;
271
}
272
```
273
274
### Comm Implementation
275
276
Wrapper class that implements IClassicComm using @jupyterlab/services IComm.
277
278
```typescript { .api }
279
/**
280
* Comm implementation wrapping @jupyterlab/services IComm
281
*/
282
class Comm implements IClassicComm {
283
/**
284
* Create comm wrapper
285
* @param jsServicesComm - @jupyterlab/services IComm instance
286
*/
287
constructor(jsServicesComm: Kernel.IComm);
288
289
/**
290
* Get the comm identifier
291
*/
292
get comm_id(): string;
293
294
/**
295
* Get the target name
296
*/
297
get target_name(): string;
298
299
/**
300
* Open the comm channel
301
* @param data - Opening data
302
* @param callbacks - Message callbacks
303
* @param metadata - Message metadata
304
* @param buffers - Binary buffers
305
* @returns Message ID
306
*/
307
open(
308
data: JSONValue,
309
callbacks?: ICallbacks,
310
metadata?: JSONObject,
311
buffers?: ArrayBuffer[] | ArrayBufferView[]
312
): string;
313
314
/**
315
* Send message through comm
316
* @param data - Message data
317
* @param callbacks - Message callbacks
318
* @param metadata - Message metadata
319
* @param buffers - Binary buffers
320
* @returns Message ID
321
*/
322
send(
323
data: JSONValue,
324
callbacks?: ICallbacks,
325
metadata?: JSONObject,
326
buffers?: ArrayBuffer[] | ArrayBufferView[]
327
): string;
328
329
/**
330
* Close the comm channel
331
* @param data - Closing data
332
* @param callbacks - Message callbacks
333
* @param metadata - Message metadata
334
* @param buffers - Binary buffers
335
* @returns Message ID
336
*/
337
close(
338
data?: JSONValue,
339
callbacks?: ICallbacks,
340
metadata?: JSONObject,
341
buffers?: ArrayBuffer[] | ArrayBufferView[]
342
): string;
343
344
/**
345
* Register message handler
346
* @param callback - Message handler function
347
*/
348
on_msg(callback: (x: any) => void): void;
349
350
/**
351
* Register close handler
352
* @param callback - Close handler function
353
*/
354
on_close(callback: (x: any) => void): void;
355
}
356
```
357
358
**Usage Examples:**
359
360
```typescript
361
// Initialize comm manager
362
const kernel = await kernelManager.startNew();
363
const commManager = new CommManager(kernel);
364
365
// Register comm target
366
commManager.register_target('my_widget', (comm, msg) => {
367
console.log('New widget comm created:', comm.comm_id);
368
369
// Setup handlers for this comm
370
comm.on_msg((msg) => {
371
handleWidgetMessage(comm, msg);
372
});
373
374
comm.on_close((msg) => {
375
cleanupWidget(comm.comm_id);
376
});
377
});
378
379
// Create new comm
380
const comm = await commManager.new_comm(
381
'my_widget',
382
{ widget_type: 'slider', initial_value: 50 },
383
callbacks,
384
{},
385
'slider-widget-1'
386
);
387
```
388
389
## Message Patterns
390
391
### Widget State Synchronization
392
393
```typescript
394
// Handle state updates from kernel
395
comm.on_msg((msg) => {
396
if (msg.content.method === 'update') {
397
const state = msg.content.state;
398
const bufferPaths = msg.content.buffer_paths || [];
399
const buffers = msg.buffers || [];
400
401
// Reconstruct state with binary data
402
put_buffers(state, bufferPaths, buffers);
403
404
// Apply state to model
405
model.set_state(state);
406
}
407
});
408
409
// Send state changes to kernel
410
const sendStateUpdate = (changes: any) => {
411
// Remove binary buffers for transmission
412
const { state, buffer_paths, buffers } = remove_buffers(changes);
413
414
comm.send({
415
method: 'update',
416
state: state,
417
buffer_paths: buffer_paths
418
}, callbacks, {}, buffers);
419
};
420
```
421
422
### Custom Message Handling
423
424
```typescript
425
// Send custom messages
426
const sendCustomMessage = (action: string, data: any) => {
427
comm.send({
428
method: 'custom',
429
content: {
430
action: action,
431
data: data,
432
timestamp: Date.now()
433
}
434
});
435
};
436
437
// Handle custom messages
438
comm.on_msg((msg) => {
439
if (msg.content.method === 'custom') {
440
const { action, data } = msg.content.content;
441
442
switch (action) {
443
case 'highlight':
444
highlightWidget(data.color);
445
break;
446
case 'reset':
447
resetWidget();
448
break;
449
case 'export':
450
exportData(data.format);
451
break;
452
}
453
}
454
});
455
```
456
457
### Error Handling and Recovery
458
459
```typescript
460
// Robust message sending with error handling
461
const sendMessageWithRetry = async (data: any, maxRetries = 3) => {
462
let attempt = 0;
463
464
while (attempt < maxRetries) {
465
try {
466
const msgId = comm.send(data, {
467
shell: {
468
reply: (msg) => {
469
console.log('Message sent successfully:', msgId);
470
},
471
error: (msg) => {
472
console.error('Message failed:', msg);
473
throw new Error(`Message failed: ${msg.content.ename}`);
474
}
475
}
476
});
477
return msgId;
478
} catch (error) {
479
attempt++;
480
if (attempt >= maxRetries) {
481
throw error;
482
}
483
await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
484
}
485
}
486
};
487
488
// Connection state management
489
let connectionState = 'connected';
490
491
comm.on_close(() => {
492
connectionState = 'disconnected';
493
// Attempt reconnection
494
setTimeout(attemptReconnection, 5000);
495
});
496
497
const attemptReconnection = async () => {
498
try {
499
// Create new comm with same ID
500
const newComm = await commManager.new_comm(
501
comm.target_name,
502
{ reconnect: true },
503
callbacks,
504
{},
505
comm.comm_id
506
);
507
connectionState = 'connected';
508
console.log('Reconnected successfully');
509
} catch (error) {
510
console.error('Reconnection failed:', error);
511
setTimeout(attemptReconnection, 10000);
512
}
513
};
514
```
515
516
## Buffer Handling
517
518
Working with binary data in widget communication:
519
520
```typescript
521
// Send data with binary buffers
522
const sendBinaryData = (imageData: Uint8Array, metadata: any) => {
523
const data = {
524
metadata: metadata,
525
image: imageData // This will be extracted as a buffer
526
};
527
528
// remove_buffers extracts binary data
529
const { state, buffer_paths, buffers } = remove_buffers(data);
530
531
comm.send({
532
method: 'update',
533
state: state,
534
buffer_paths: buffer_paths
535
}, callbacks, {}, buffers);
536
};
537
538
// Receive data with binary buffers
539
comm.on_msg((msg) => {
540
if (msg.content.buffer_paths && msg.buffers) {
541
const state = msg.content.state;
542
const bufferPaths = msg.content.buffer_paths;
543
const buffers = msg.buffers;
544
545
// Reconstruct object with binary data
546
put_buffers(state, bufferPaths, buffers);
547
548
// Now state.image is a DataView containing the binary data
549
const imageData = new Uint8Array(state.image.buffer);
550
displayImage(imageData);
551
}
552
});
553
```