# `jupyter-protocol`
This crate provides comprehensive types and utilities for working with Jupyter messages, compatible with both ZeroMQ and WebSocket backends. It implements the [Jupyter Messaging Protocol](https://jupyter-client.readthedocs.io/en/latest/messaging.html) specification.
## Features
- Complete implementation of Jupyter message types
- Pattern matching on message content for easy handling and dispatch
- Serialization and deserialization of Jupyter messages
- Support for rich media types (MIME bundles)
- Utility functions for working with Jupyter kernels and clients
## Usage
Here's a basic example of how to use this crate with `runtimelib`, relying on Tokio for async:
```rust
use jupyter_protocol::{
ConnectionInfo, ExecuteRequest, ExecutionState, JupyterMessage, JupyterMessageContent,
};
use uuid::Uuid;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
use jupyter_protocol::{
ConnectionInfo, ExecuteRequest, ExecutionState, JupyterMessage, JupyterMessageContent,
};
use uuid::Uuid;
let kernel_name = "python";
let kernelspecs = runtimelib::list_kernelspecs().await;
let kernel_specification = kernelspecs
.iter()
.find(|k| k.kernel_name.eq(kernel_name))
.ok_or(anyhow::anyhow!("Python kernel not found"))?;
let ip = std::net::IpAddr::V4(std::net::Ipv4Addr::new(127, 0, 0, 1));
let ports = runtimelib::peek_ports(ip, 5).await?;
assert_eq!(ports.len(), 5);
let connection_info = ConnectionInfo {
transport: jupyter_protocol::connection_info::Transport::TCP,
ip: ip.to_string(),
stdin_port: ports[0],
control_port: ports[1],
hb_port: ports[2],
shell_port: ports[3],
iopub_port: ports[4],
signature_scheme: "hmac-sha256".to_string(),
key: uuid::Uuid::new_v4().to_string(),
kernel_name: Some(kernel_name.to_string()),
};
let runtime_dir = runtimelib::dirs::runtime_dir();
tokio::fs::create_dir_all(&runtime_dir).await.map_err(|e| {
anyhow::anyhow!(
"Failed to create jupyter runtime dir {:?}: {}",
runtime_dir,
e
)
})?;
let connection_path = runtime_dir.join("kernel-example.json");
let content = serde_json::to_string(&connection_info)?;
tokio::fs::write(connection_path.clone(), content).await?;
let working_directory = "/tmp";
let mut process = kernel_specification
.clone()
.command(&connection_path, None, None)?
.current_dir(working_directory)
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
.stdin(std::process::Stdio::piped())
.kill_on_drop(true)
.spawn()?;
let session_id = Uuid::new_v4().to_string();
// Listen for display data, execute result, stdout messages, etc.
let mut iopub_socket =
runtimelib::create_client_iopub_connection(&connection_info, "", &session_id).await?;
let mut shell_socket =
runtimelib::create_client_shell_connection(&connection_info, &session_id).await?;
// Control socket is for kernel management, not used here
// let mut control_socket =
// runtimelib::create_client_control_connection(&connection_info, &session_id).await?;
let execute_request = ExecuteRequest::new("print('Hello, World!')".to_string());
let execute_request: JupyterMessage = execute_request.into();
let execute_request_id = execute_request.header.msg_id.clone();
let iopub_handle = tokio::spawn({
async move {
loop {
match iopub_socket.read().await {
Ok(message) => match message.content {
JupyterMessageContent::Status(status) => {
//
if status.execution_state == ExecutionState::Idle
&& message.parent_header.as_ref().map(|h| h.msg_id.as_str())
== Some(execute_request_id.as_str())
{
println!("Execution finalized, exiting...");
break;
}
}
_ => {
println!("{:?}", message.content);
}
},
Err(e) => {
eprintln!("Error receiving iopub message: {}", e);
break;
}
}
}
}
});
shell_socket.send(execute_request).await?;
iopub_handle.await?;
process.start_kill()?;
Ok(())
}
```
## Documentation
For detailed documentation, please run `cargo doc --open` or visit the [docs.rs](https://docs.rs/jupyter-protocol) page for this crate.
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
## License
This project is licensed under the BSD 3-Clause License.