0
# Communication Framework (Comm)
1
2
Bidirectional communication system between Jupyter frontends and kernels for custom messages and interactive widgets. Enables real-time data exchange beyond standard code execution, supporting interactive widgets, custom protocols, and dynamic frontend-kernel interactions.
3
4
## Capabilities
5
6
### Comm Class
7
8
Base communication object for bidirectional messaging between kernel and frontend.
9
10
```python { .api }
11
class Comm:
12
"""
13
Bidirectional communication channel between kernel and frontend.
14
15
Enables custom messaging protocols for interactive widgets and
16
real-time data exchange beyond standard execution messages.
17
"""
18
19
def __init__(self, target_name=None, data=None, metadata=None,
20
buffers=None, **kwargs):
21
"""
22
Create a new Comm instance.
23
24
Parameters:
25
- target_name (str, optional): Name of target handler on frontend
26
- data (dict, optional): Initial data to send
27
- metadata (dict, optional): Message metadata
28
- buffers (list, optional): Binary buffer data
29
- **kwargs: Additional comm creation options
30
"""
31
32
def send(self, data=None, metadata=None, buffers=None):
33
"""
34
Send data to the frontend via this comm.
35
36
Parameters:
37
- data (dict, optional): Data to send
38
- metadata (dict, optional): Message metadata
39
- buffers (list, optional): Binary buffer data
40
"""
41
42
def close(self, data=None, metadata=None, buffers=None):
43
"""
44
Close this comm and optionally send final data.
45
46
Parameters:
47
- data (dict, optional): Final data to send
48
- metadata (dict, optional): Message metadata
49
- buffers (list, optional): Binary buffer data
50
"""
51
52
def on_msg(self, callback):
53
"""
54
Register callback for messages received on this comm.
55
56
Parameters:
57
- callback (callable): Function to call with received messages
58
Signature: callback(msg)
59
"""
60
61
def on_close(self, callback):
62
"""
63
Register callback for when this comm is closed.
64
65
Parameters:
66
- callback (callable): Function to call when comm closes
67
Signature: callback(msg)
68
"""
69
70
# Comm attributes
71
comm_id: str # Unique identifier for this comm
72
target_name: str # Target handler name
73
kernel: object # Reference to kernel instance
74
```
75
76
### Comm Manager
77
78
Central manager for handling comm lifecycle, registration, and message routing.
79
80
```python { .api }
81
class CommManager:
82
"""
83
Manager for comm instances and target handlers.
84
85
Handles comm creation, registration, message routing, and cleanup
86
for the kernel's communication infrastructure.
87
"""
88
89
def register_target(self, target_name, f):
90
"""
91
Register a handler function for a comm target.
92
93
Parameters:
94
- target_name (str): Name of the target to handle
95
- f (callable): Handler function for comm open requests
96
Signature: f(comm, open_msg)
97
"""
98
99
def unregister_target(self, target_name, f):
100
"""
101
Unregister a target handler.
102
103
Parameters:
104
- target_name (str): Target name to unregister
105
- f (callable): Handler function to remove
106
"""
107
108
def register_comm(self, comm):
109
"""
110
Register a comm instance with the manager.
111
112
Parameters:
113
- comm (Comm): Comm instance to register
114
"""
115
116
def unregister_comm(self, comm):
117
"""
118
Unregister and cleanup a comm instance.
119
120
Parameters:
121
- comm (Comm): Comm instance to unregister
122
"""
123
124
def get_comm(self, comm_id):
125
"""
126
Get a registered comm by its ID.
127
128
Parameters:
129
- comm_id (str): Unique comm identifier
130
131
Returns:
132
Comm or None: The comm instance, or None if not found
133
"""
134
135
# Manager attributes
136
comms: dict # Mapping of comm_id to Comm instances
137
targets: dict # Mapping of target_name to handler functions
138
kernel: object # Reference to kernel instance
139
```
140
141
## Usage Examples
142
143
### Basic Comm Communication
144
145
```python
146
from ipykernel.comm import Comm
147
148
# Create a comm for bidirectional communication
149
comm = Comm(target_name='my_widget')
150
151
# Send data to frontend
152
comm.send({
153
'action': 'update',
154
'value': 42,
155
'timestamp': '2023-01-01T00:00:00Z'
156
})
157
158
# Register callback for incoming messages
159
def handle_message(msg):
160
data = msg['content']['data']
161
print(f"Received: {data}")
162
163
# Echo back a response
164
comm.send({
165
'action': 'echo',
166
'original': data
167
})
168
169
comm.on_msg(handle_message)
170
171
# Close the comm when done
172
comm.close({'action': 'cleanup'})
173
```
174
175
### Comm Target Registration
176
177
```python
178
from ipykernel.comm import CommManager, Comm
179
180
# Get the kernel's comm manager (typically available as kernel.comm_manager)
181
# comm_manager = kernel.comm_manager
182
183
# Register a target handler for new comms
184
def handle_widget_comm(comm, open_msg):
185
"""Handle new widget comm connections."""
186
print(f"New comm opened: {comm.comm_id}")
187
188
# Set up message handler for this comm
189
def on_widget_msg(msg):
190
data = msg['content']['data']
191
192
if data.get('action') == 'get_state':
193
# Send current widget state
194
comm.send({
195
'action': 'state',
196
'state': {'value': 100, 'enabled': True}
197
})
198
elif data.get('action') == 'set_value':
199
# Update widget value
200
new_value = data.get('value', 0)
201
print(f"Widget value updated to: {new_value}")
202
203
# Confirm update
204
comm.send({
205
'action': 'value_updated',
206
'value': new_value
207
})
208
209
comm.on_msg(on_widget_msg)
210
211
# Send initial state
212
comm.send({
213
'action': 'initialized',
214
'widget_id': comm.comm_id
215
})
216
217
# Register the target handler
218
comm_manager.register_target('my_widget', handle_widget_comm)
219
```
220
221
### Interactive Widget Pattern
222
223
```python
224
from ipykernel.comm import Comm
225
import threading
226
import time
227
228
class SimpleSlider:
229
"""Example interactive widget using comms."""
230
231
def __init__(self, min_val=0, max_val=100, initial_val=50):
232
self.min_val = min_val
233
self.max_val = max_val
234
self.value = initial_val
235
self.observers = []
236
237
# Create comm for frontend communication
238
self.comm = Comm(target_name='simple_slider')
239
self.comm.on_msg(self._handle_frontend_msg)
240
241
# Send initial configuration
242
self.comm.send({
243
'action': 'create',
244
'config': {
245
'min': self.min_val,
246
'max': self.max_val,
247
'value': self.value
248
}
249
})
250
251
def _handle_frontend_msg(self, msg):
252
"""Handle messages from frontend."""
253
data = msg['content']['data']
254
255
if data.get('action') == 'value_change':
256
new_value = data.get('value')
257
if self.min_val <= new_value <= self.max_val:
258
old_value = self.value
259
self.value = new_value
260
261
# Notify observers
262
for callback in self.observers:
263
callback(old_value, new_value)
264
265
def observe(self, callback):
266
"""Register callback for value changes."""
267
self.observers.append(callback)
268
269
def set_value(self, value):
270
"""Programmatically set value."""
271
if self.min_val <= value <= self.max_val:
272
self.value = value
273
self.comm.send({
274
'action': 'update_value',
275
'value': value
276
})
277
278
# Usage
279
slider = SimpleSlider(0, 255, 128)
280
281
def on_value_change(old_val, new_val):
282
print(f"Slider changed from {old_val} to {new_val}")
283
284
slider.observe(on_value_change)
285
```
286
287
### Comm Cleanup and Management
288
289
```python
290
from ipykernel.comm import CommManager
291
292
# Access comm manager (typically kernel.comm_manager)
293
# comm_manager = kernel.comm_manager
294
295
# List all active comms
296
active_comms = list(comm_manager.comms.keys())
297
print(f"Active comms: {active_comms}")
298
299
# Get specific comm
300
comm_id = active_comms[0] if active_comms else None
301
if comm_id:
302
comm = comm_manager.get_comm(comm_id)
303
print(f"Comm target: {comm.target_name}")
304
305
# Clean shutdown - close all comms
306
for comm_id, comm in list(comm_manager.comms.items()):
307
comm.close({'action': 'shutdown'})
308
309
# Unregister targets
310
comm_manager.unregister_target('my_widget', handle_widget_comm)
311
```
312
313
## Message Format
314
315
Comm messages follow the Jupyter messaging protocol:
316
317
```python { .api }
318
CommMessage = {
319
"header": {
320
"msg_id": str,
321
"msg_type": str, # 'comm_open', 'comm_msg', 'comm_close'
322
"username": str,
323
"session": str,
324
"date": str,
325
"version": str
326
},
327
"parent_header": dict,
328
"metadata": dict,
329
"content": {
330
"comm_id": str, # Unique comm identifier
331
"target_name": str, # Target handler name (for open messages)
332
"data": dict, # Custom message data
333
"target_module": str # Optional target module (for open messages)
334
},
335
"buffers": list # Optional binary buffers
336
}
337
```