-
-
Notifications
You must be signed in to change notification settings - Fork 5
🔙 Backport Support talking to Colab from the JKC #34
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Changes to support talking to Colab from the JKC
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This pull request adds support for the Jupyter Kernel Client to communicate with Google Colab by introducing a new JSON-based WebSocket subprotocol alongside the existing v1 binary protocol.
Changes:
- Introduced
JupyterSubprotocolenum to specify communication protocols (DEFAULT, V1, JSON) - Added JSON serialization/deserialization functions for Colab compatibility
- Enhanced
KernelWebSocketClientandWSSessionto support multiple subprotocols - Modified
KernelHttpManagerto accept and forward custom headers - Added dependency-groups configuration for uv package manager
- Updated test dependencies and added Makefile test target
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 14 comments.
Show a summary per file
| File | Description |
|---|---|
| pyproject.toml | Added dependency-groups for uv, specified new test dependencies, and added test target configuration |
| jupyter_kernel_client/wsclient.py | Introduced JupyterSubprotocol enum, added subprotocol parameter to WSSession and KernelWebSocketClient, implemented protocol-specific message serialization logic |
| jupyter_kernel_client/utils.py | Added JSON serialization/deserialization functions for Colab support |
| jupyter_kernel_client/manager.py | Added headers parameter support and updated User-Agent string |
| jupyter_kernel_client/client.py | Added null check for manager in stop method |
| jupyter_kernel_client/init.py | Exported JupyterSubprotocol enum |
| Makefile | Added test target for running pytest |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| headers = { | ||
| "Accept": "application/json", | ||
| "Content-Type": "application/json", | ||
| "User-Agent": "Jupyter Kernel Client" |
Copilot
AI
Jan 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The User-Agent header value was changed from "Jupyter kernels CLI" to "Jupyter Kernel Client". While this change is likely intentional to better reflect the library's name, it represents a breaking change that could impact server-side analytics, logging, or behavior that depends on the User-Agent string.
| "User-Agent": "Jupyter Kernel Client" | |
| "User-Agent": "Jupyter kernels CLI" |
| [dependency-groups] | ||
| dev = [ | ||
| {include-group = "test"} | ||
| ] | ||
| test = [ | ||
| "ipykernel>=6.31.0", | ||
| "jupyter-server>=2.17.0", | ||
| "numpy>=2.0.2", | ||
| "pandas>=2.3.3", | ||
| "pytest>=8.4.2", | ||
| "pytest-asyncio>=1.2.0", | ||
| "pytest-cov>=7.0.0", | ||
| "pytest-timeout>=2.4.0", | ||
| ] |
Copilot
AI
Jan 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The dependency-groups section duplicates and conflicts with the existing project.optional-dependencies.test section. The dependency-groups.test has different version constraints (e.g., ipykernel>=6.31.0 vs ipykernel without version, jupyter-server>=2.17.0 vs jupyter_server>=2.10,<3). This duplication can cause confusion about which dependencies to use and may lead to inconsistent test environments. Consider consolidating these or clarifying which should be used.
|
|
||
|
|
Copilot
AI
Jan 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are extra blank lines here. Line 480 appears to be unnecessary trailing whitespace, and line 481 adds an extra blank line. This violates code style conventions and may cause linting issues.
| token_param: String | None | ||
| The name of the token parameter to use. |
Copilot
AI
Jan 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The docstring comment on line 474 incorrectly states "String | None" for the token_param type. However, there is no token_param parameter in the init method signature. This documentation appears to be erroneous or incomplete.
| token_param: String | None | |
| The name of the token parameter to use. |
jupyter_kernel_client/utils.py
Outdated
|
|
||
| def deserialize_msg_from_ws_json(ws_msg): | ||
| """Deserialize as JSON (for Colab).""" | ||
| return json.loads(ws_msg.encode('utf-8')) |
Copilot
AI
Jan 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The deserialize_msg_from_ws_json function is calling encode on a parameter that is already bytes. According to the _on_message signature in wsclient.py (line 1247), the message parameter is already of type bytes. Calling encode on bytes will raise an AttributeError. The function should either accept a string and encode it, or accept bytes and decode it directly.
| return json.loads(ws_msg.encode('utf-8')) | |
| # `ws_msg` may be either `str` or `bytes` depending on the WebSocket client. | |
| # If it's bytes, decode it to text before passing to json.loads. | |
| if isinstance(ws_msg, bytes): | |
| text = ws_msg.decode("utf-8") | |
| else: | |
| text = ws_msg | |
| return json.loads(text) |
jupyter_kernel_client/wsclient.py
Outdated
| class JupyterSubprotocol(Enum): | ||
| """JupyterSubprotocol specifies which subprotocol the KernelWebSocketClient should specifically | ||
| request when interacting with the Jupyter server. | ||
| """ | ||
|
|
||
| # Use the default subprotocol | ||
| # https://jupyter-server.readthedocs.io/en/latest/developers/websocket-protocols.html#default-websocket-protocol | ||
| DEFAULT = 0 | ||
|
|
||
| # Use the v1.kernel.websocket.jupyter.org protocol | ||
| # https://jupyter-server.readthedocs.io/en/latest/developers/websocket-protocols.html#v1-kernel-websocket-jupyter-org-protocol | ||
| V1 = 1 | ||
|
|
||
| # JSON is specifically for Colab (until we modify this library to actually have support for DEFAULT) | ||
| JSON = 2 |
Copilot
AI
Jan 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new JupyterSubprotocol enum and JSON subprotocol support lack test coverage. Given that the repository has comprehensive test coverage for other kernel client functionality, the new subprotocol behavior (especially the JSON serialization path for Colab) should be tested to ensure correctness.
| header=header, | ||
| metadata=metadata, | ||
| ) | ||
| msg['channel'] = channel |
Copilot
AI
Jan 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The 'channel' field is unconditionally added to msg for all subprotocols. This is necessary for the JSON subprotocol but may not be appropriate for the V1 subprotocol, where the channel is passed as a separate parameter to serialize_msg_to_ws_v1. Consider adding the channel field conditionally based on the subprotocol to avoid polluting the message dict unnecessarily.
| msg['channel'] = channel | |
| # Only include the channel in the message dict for JSON subprotocol, | |
| # where the channel is expected to be part of the serialized message. | |
| if self.subprotocol == JupyterSubprotocol.JSON: | |
| msg["channel"] = channel |
| from __future__ import annotations | ||
|
|
||
| import datetime | ||
| import logging |
Copilot
AI
Jan 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Import of 'logging' is not used.
| import logging |
|
|
||
| from datetime import datetime, timedelta, timezone, tzinfo | ||
| import json | ||
| import logging |
Copilot
AI
Jan 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Import of 'logging' is not used.
| import logging |
| from __future__ import annotations | ||
|
|
||
|
|
||
| import json |
Copilot
AI
Jan 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Import of 'json' is not used.
| import json |
Add support for listing kernels
…tocol (#3) * Add serializers and deserializers for Jupyter's default websocket protocol * Adds a default serializer/deserializer - Logic unabashedly swiped from https://github.com/jupyterlab/jupyterlab/blob/main/packages/services/src/kernel/serialize.ts#L216 * Sets default values for the protocol as DEFAULT since it's... the default * Adds simple testing for the serializers/deserializers * Fixes minor bug with the logger being angry about multiple channels being defined * didn't realize that was a required arg for * ugh from_bytes as well * in tests as well * address comments * fix uv.lock * fix uv.lock --------- Co-authored-by: Drake Aiman <drakeaiman@google.com>
echarles
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Fixes #35