Skip to content

Commit 51676ad

Browse files
committed
update kernel client to always use latest rooms from YRoomManager
1 parent 339a3f4 commit 51676ad

File tree

2 files changed

+76
-12
lines changed

2 files changed

+76
-12
lines changed

jupyter_server_documents/kernels/kernel_client.py

Lines changed: 72 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
"""
22
A new Kernel client that is aware of ydocuments.
33
"""
4+
from __future__ import annotations
45
import anyio
56
import asyncio
6-
import json
77
import typing as t
88

99
from traitlets import Set, Instance, Any, Type, default
@@ -16,6 +16,9 @@
1616

1717
from .kernel_client_abc import AbstractDocumentAwareKernelClient
1818

19+
if t.TYPE_CHECKING:
20+
from jupyter_server_documents.rooms.yroom_manager import YRoomManager
21+
1922

2023
class DocumentAwareKernelClient(AsyncKernelClient):
2124
"""
@@ -39,11 +42,6 @@ def _default_message_cache(self):
3942
# message is received.
4043
_listeners = Set(allow_none=True)
4144

42-
# A set of YRooms that will intercept output and kernel
43-
# status messages.
44-
_yrooms: t.Set[YRoom] = Set(trait=Instance(YRoom), default_value=set())
45-
46-
4745
output_processor = Instance(
4846
OutputProcessor,
4947
allow_none=True
@@ -58,6 +56,24 @@ def _default_message_cache(self):
5856
def _default_output_processor(self) -> OutputProcessor:
5957
self.log.info("Creating output processor")
6058
return self.output_process_class(parent=self, config=self.config)
59+
60+
_yroom_manager: YRoomManager | None
61+
"""
62+
The YRoomManager registered via `self.bind_yroom_manager()`, which must be
63+
called before adding any `YRoom` via `self.add_yroom()`.
64+
"""
65+
66+
_yroom_ids: set[str]
67+
"""
68+
The set of room IDs that are registered with this kernel client. This class
69+
stores room IDs instead of `YRoom` instances because `YRoom` instances may
70+
be deleted once inactive.
71+
"""
72+
73+
def __init__(self, *args, **kwargs):
74+
super().__init__(*args, **kwargs)
75+
self._yroom_manager = None
76+
self._yroom_ids = set()
6177

6278
async def start_listening(self):
6379
"""Start listening to messages coming from the kernel.
@@ -286,18 +302,63 @@ async def handle_document_related_message(self, msg: t.List[bytes]) -> t.Optiona
286302
# Default return if message is processed and does not need forwarding
287303
return msg
288304

305+
@property
306+
def _yrooms(self) -> list[YRoom]:
307+
"""
308+
Returns the list of YRoom instances registered to this kernel client.
309+
"""
310+
if len(self._yroom_ids) == 0:
311+
return []
312+
313+
assert self._yroom_manager
314+
rooms: list[YRoom] = []
315+
316+
# Always call `get_room()` to get the latest reference to the room. We
317+
# must do this since rooms may be deleted upon inactivity. The
318+
# `get_room()` method returns a cached value as long as the room was not
319+
# deleted, so this is very fast in most cases.
320+
for room_id in self._yroom_ids:
321+
room = self._yroom_manager.get_room(room_id)
322+
rooms.append(room)
323+
324+
return rooms
325+
289326
async def add_yroom(self, yroom: YRoom):
290327
"""
291-
Register a YRoom with this kernel client. YRooms will
292-
intercept display and kernel status messages.
328+
Register a `YRoom` with this kernel client, given the room ID.
329+
Registered `YRoom`s will intercept display and kernel status messages.
330+
331+
`self.bind_yroom_manager()` must be called before using this method.
293332
"""
294-
self._yrooms.add(yroom)
333+
assert self._yroom_manager
334+
self._yroom_ids.add(yroom.room_id)
335+
self.log.info(
336+
f"Added room '{yroom.room_id}' to kernel '{self.kernel_name}'. "
337+
f"Total rooms: {len(self._yroom_ids)}"
338+
)
295339

296340
async def remove_yroom(self, yroom: YRoom):
297341
"""
298-
De-register a YRoom from handling kernel client messages.
342+
De-register a `YRoom` from this kernel client, given the room ID.
343+
344+
`self.bind_yroom_manager()` must be called before using this method.
345+
"""
346+
self._yrooms_ids.discard(yroom.room_id)
347+
self.log.info(
348+
f"Removed room '{yroom.room_id}' from kernel '{self.kernel_name}'. "
349+
f"Total rooms: {len(self._yroom_ids)}"
350+
)
351+
352+
@property
353+
def yroom_manager(self) -> YRoomManager:
354+
return self._yroom_manager
355+
356+
def bind_yroom_manager(self, yroom_manager: YRoomManager):
357+
"""
358+
Binds a reference to the `YRoomManager` singleton to this instance. This
359+
method must be called before adding a room.
299360
"""
300-
self._yrooms.discard(yroom)
361+
self._yroom_manager = yroom_manager
301362

302363

303364
AbstractDocumentAwareKernelClient.register(DocumentAwareKernelClient)

jupyter_server_documents/session_manager.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,13 +122,16 @@ async def create_session(
122122
# Get YRoom for this session and store its ID in `self._room_ids`
123123
yroom = self._init_session_yroom(session_id, real_path)
124124

125+
# Bind `YRoomManager` to the kernel client
126+
kernel_client = self.get_kernel_client(kernel_id)
127+
kernel_client.bind_yroom_manager(yroom_manager=self.yroom_manager)
128+
125129
# Add YRoom to this session's kernel client
126130
# TODO: we likely have a race condition here... need to
127131
# think about it more. Currently, the kernel client gets
128132
# created after the kernel starts fully. We need the
129133
# kernel client instantiated _before_ trying to connect
130134
# the yroom.
131-
kernel_client = self.get_kernel_client(kernel_id)
132135
await kernel_client.add_yroom(yroom)
133136
self.log.info(f"Connected yroom {yroom.room_id} to kernel {kernel_id}. yroom: {yroom}")
134137
return session_model

0 commit comments

Comments
 (0)