Foundational classes and utilities for building interactive widgets in Jupyter environments
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
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.
Core interface for communication channels between frontend widgets and kernel models.
/**
* Interface for Jupyter comm communication channels
*/
interface IClassicComm {
/**
* Unique identifier for the communication channel
*/
comm_id: string;
/**
* Target name identifying the comm type
*/
target_name: string;
/**
* Open a communication channel to the kernel
* @param data - Initial data to send
* @param callbacks - Message callback handlers
* @param metadata - Additional message metadata
* @param buffers - Binary data buffers
* @returns Message ID of the open message
*/
open(
data: JSONValue,
callbacks?: ICallbacks,
metadata?: JSONObject,
buffers?: ArrayBuffer[] | ArrayBufferView[]
): string;
/**
* Send a message through the communication channel
* @param data - Message data payload
* @param callbacks - Message callback handlers
* @param metadata - Additional message metadata
* @param buffers - Binary data buffers
* @returns Message ID of the sent message
*/
send(
data: JSONValue,
callbacks?: ICallbacks,
metadata?: JSONObject,
buffers?: ArrayBuffer[] | ArrayBufferView[]
): string;
/**
* Close the communication channel
* @param data - Optional closing data
* @param callbacks - Message callback handlers
* @param metadata - Additional message metadata
* @param buffers - Binary data buffers
* @returns Message ID of the close message
*/
close(
data?: JSONValue,
callbacks?: ICallbacks,
metadata?: JSONObject,
buffers?: ArrayBuffer[] | ArrayBufferView[]
): string;
/**
* Register a handler for incoming messages
* @param callback - Function to handle received messages
*/
on_msg(callback: (x: any) => void): void;
/**
* Register a handler for comm close events
* @param callback - Function to handle comm closure
*/
on_close(callback: (x: any) => void): void;
}Usage Examples:
// Setup message handling
comm.on_msg((msg) => {
console.log('Received message:', msg);
// Handle different message types
switch (msg.content.method) {
case 'update':
handleStateUpdate(msg.content.state);
break;
case 'custom':
handleCustomMessage(msg.content.data);
break;
}
});
// Setup close handling
comm.on_close((msg) => {
console.log('Comm closed:', msg);
cleanup();
});
// Send messages with callbacks
const msgId = comm.send(
{ method: 'request_data', params: { type: 'summary' } },
{
shell: {
reply: (msg) => console.log('Got reply:', msg)
},
iopub: {
status: (msg) => console.log('Status:', msg.content.execution_state)
}
}
);Configuration for handling different types of kernel messages during comm operations.
/**
* Callback handlers for different message channels
*/
interface ICallbacks {
/**
* Handlers for shell messages (replies, errors)
*/
shell?: {
[key: string]: (msg: KernelMessage.IShellMessage) => void
};
/**
* Handlers for IOPub messages (status, output, errors)
*/
iopub?: {
[key: string]: (msg: KernelMessage.IIOPubMessage) => void
};
/**
* Handler for stdin messages (input requests)
*/
input?: (msg: KernelMessage.IStdinMessage) => void;
}Usage Examples:
// Comprehensive callback setup
const callbacks: ICallbacks = {
shell: {
reply: (msg) => {
// Handle successful replies
console.log('Operation completed:', msg.content);
},
error: (msg) => {
// Handle errors
console.error('Operation failed:', msg.content);
}
},
iopub: {
status: (msg) => {
// Track kernel execution state
const state = msg.content.execution_state;
if (state === 'busy') {
showSpinner();
} else if (state === 'idle') {
hideSpinner();
}
},
execute_result: (msg) => {
// Handle execution results
displayResult(msg.content.data);
},
stream: (msg) => {
// Handle stdout/stderr output
appendOutput(msg.content.text);
},
error: (msg) => {
// Handle execution errors
displayError(msg.content.ename, msg.content.evalue, msg.content.traceback);
}
},
input: (msg) => {
// Handle input requests from kernel
const prompt = msg.content.prompt;
const response = await getUserInput(prompt);
// Send response back to kernel
}
};
// Use callbacks with comm operations
comm.send(data, callbacks);Compatibility layer that provides the classic comm API while using modern @jupyterlab/services underneath.
Manager class for creating and registering communication channels.
/**
* Manager for communication channels, provides compatibility with @jupyterlab/services
*/
class CommManager {
/**
* Initialize comm manager with kernel connection
* @param jsServicesKernel - @jupyterlab/services kernel connection
*/
constructor(jsServicesKernel: Kernel.IKernelConnection);
/**
* Setup or update kernel connection
* @param jsServicesKernel - @jupyterlab/services kernel connection
*/
init_kernel(jsServicesKernel: Kernel.IKernelConnection): void;
/**
* Create a new communication channel
* @param target_name - Comm target identifier
* @param data - Initial data to send
* @param callbacks - Message callbacks
* @param metadata - Message metadata
* @param comm_id - Specific comm ID to use
* @param buffers - Binary data buffers
* @returns Promise resolving to the created comm
*/
async new_comm(
target_name: string,
data: any,
callbacks: any,
metadata: any,
comm_id: string,
buffers?: ArrayBuffer[] | ArrayBufferView[]
): Promise<Comm>;
/**
* Register a handler for incoming comm creation requests
* @param target_name - Target name to handle
* @param f - Handler function receiving comm and message
*/
register_target(
target_name: string,
f: (comm: Comm, object: KernelMessage.IMessage) => void
): void;
/**
* Unregister a comm target handler
* @param target_name - Target name to unregister
* @param f - Handler function to remove
*/
unregister_target(
target_name: string,
f: (comm: Comm, object: KernelMessage.IMessage) => void
): void;
/**
* Register a comm instance with the manager
* @param comm - Comm instance to register
* @returns Comm ID
*/
register_comm(comm: Comm): string;
}Wrapper class that implements IClassicComm using @jupyterlab/services IComm.
/**
* Comm implementation wrapping @jupyterlab/services IComm
*/
class Comm implements IClassicComm {
/**
* Create comm wrapper
* @param jsServicesComm - @jupyterlab/services IComm instance
*/
constructor(jsServicesComm: Kernel.IComm);
/**
* Get the comm identifier
*/
get comm_id(): string;
/**
* Get the target name
*/
get target_name(): string;
/**
* Open the comm channel
* @param data - Opening data
* @param callbacks - Message callbacks
* @param metadata - Message metadata
* @param buffers - Binary buffers
* @returns Message ID
*/
open(
data: JSONValue,
callbacks?: ICallbacks,
metadata?: JSONObject,
buffers?: ArrayBuffer[] | ArrayBufferView[]
): string;
/**
* Send message through comm
* @param data - Message data
* @param callbacks - Message callbacks
* @param metadata - Message metadata
* @param buffers - Binary buffers
* @returns Message ID
*/
send(
data: JSONValue,
callbacks?: ICallbacks,
metadata?: JSONObject,
buffers?: ArrayBuffer[] | ArrayBufferView[]
): string;
/**
* Close the comm channel
* @param data - Closing data
* @param callbacks - Message callbacks
* @param metadata - Message metadata
* @param buffers - Binary buffers
* @returns Message ID
*/
close(
data?: JSONValue,
callbacks?: ICallbacks,
metadata?: JSONObject,
buffers?: ArrayBuffer[] | ArrayBufferView[]
): string;
/**
* Register message handler
* @param callback - Message handler function
*/
on_msg(callback: (x: any) => void): void;
/**
* Register close handler
* @param callback - Close handler function
*/
on_close(callback: (x: any) => void): void;
}Usage Examples:
// Initialize comm manager
const kernel = await kernelManager.startNew();
const commManager = new CommManager(kernel);
// Register comm target
commManager.register_target('my_widget', (comm, msg) => {
console.log('New widget comm created:', comm.comm_id);
// Setup handlers for this comm
comm.on_msg((msg) => {
handleWidgetMessage(comm, msg);
});
comm.on_close((msg) => {
cleanupWidget(comm.comm_id);
});
});
// Create new comm
const comm = await commManager.new_comm(
'my_widget',
{ widget_type: 'slider', initial_value: 50 },
callbacks,
{},
'slider-widget-1'
);// Handle state updates from kernel
comm.on_msg((msg) => {
if (msg.content.method === 'update') {
const state = msg.content.state;
const bufferPaths = msg.content.buffer_paths || [];
const buffers = msg.buffers || [];
// Reconstruct state with binary data
put_buffers(state, bufferPaths, buffers);
// Apply state to model
model.set_state(state);
}
});
// Send state changes to kernel
const sendStateUpdate = (changes: any) => {
// Remove binary buffers for transmission
const { state, buffer_paths, buffers } = remove_buffers(changes);
comm.send({
method: 'update',
state: state,
buffer_paths: buffer_paths
}, callbacks, {}, buffers);
};// Send custom messages
const sendCustomMessage = (action: string, data: any) => {
comm.send({
method: 'custom',
content: {
action: action,
data: data,
timestamp: Date.now()
}
});
};
// Handle custom messages
comm.on_msg((msg) => {
if (msg.content.method === 'custom') {
const { action, data } = msg.content.content;
switch (action) {
case 'highlight':
highlightWidget(data.color);
break;
case 'reset':
resetWidget();
break;
case 'export':
exportData(data.format);
break;
}
}
});// Robust message sending with error handling
const sendMessageWithRetry = async (data: any, maxRetries = 3) => {
let attempt = 0;
while (attempt < maxRetries) {
try {
const msgId = comm.send(data, {
shell: {
reply: (msg) => {
console.log('Message sent successfully:', msgId);
},
error: (msg) => {
console.error('Message failed:', msg);
throw new Error(`Message failed: ${msg.content.ename}`);
}
}
});
return msgId;
} catch (error) {
attempt++;
if (attempt >= maxRetries) {
throw error;
}
await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
}
}
};
// Connection state management
let connectionState = 'connected';
comm.on_close(() => {
connectionState = 'disconnected';
// Attempt reconnection
setTimeout(attemptReconnection, 5000);
});
const attemptReconnection = async () => {
try {
// Create new comm with same ID
const newComm = await commManager.new_comm(
comm.target_name,
{ reconnect: true },
callbacks,
{},
comm.comm_id
);
connectionState = 'connected';
console.log('Reconnected successfully');
} catch (error) {
console.error('Reconnection failed:', error);
setTimeout(attemptReconnection, 10000);
}
};Working with binary data in widget communication:
// Send data with binary buffers
const sendBinaryData = (imageData: Uint8Array, metadata: any) => {
const data = {
metadata: metadata,
image: imageData // This will be extracted as a buffer
};
// remove_buffers extracts binary data
const { state, buffer_paths, buffers } = remove_buffers(data);
comm.send({
method: 'update',
state: state,
buffer_paths: buffer_paths
}, callbacks, {}, buffers);
};
// Receive data with binary buffers
comm.on_msg((msg) => {
if (msg.content.buffer_paths && msg.buffers) {
const state = msg.content.state;
const bufferPaths = msg.content.buffer_paths;
const buffers = msg.buffers;
// Reconstruct object with binary data
put_buffers(state, bufferPaths, buffers);
// Now state.image is a DataView containing the binary data
const imageData = new Uint8Array(state.image.buffer);
displayImage(imageData);
}
});