Skip to content

Commit 9eb78f6

Browse files
committed
WIP: Working version of clear_output
1 parent c08de64 commit 9eb78f6

File tree

2 files changed

+43
-6
lines changed

2 files changed

+43
-6
lines changed

jupyter_server_documents/kernels/kernel_client.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,6 @@ def handle_incoming_message(self, channel_name: str, msg: list[bytes]):
105105
msg_id = header["msg_id"]
106106
metadata = self.session.unpack(msg[2])
107107
cell_id = metadata.get("cellId")
108-
109108
# Clear output processor if this cell already has
110109
# an existing request.
111110
if cell_id:
@@ -218,7 +217,7 @@ async def handle_document_related_message(self, msg: t.List[bytes]) -> t.Optiona
218217
except Exception as e:
219218
self.log.error(f"Error deserializing message: {e}")
220219
raise
221-
220+
222221
parent_msg_id = dmsg["parent_header"]["msg_id"]
223222
parent_msg_data = self.message_cache.get(parent_msg_id)
224223
cell_id = parent_msg_data.get('cell_id')
@@ -273,13 +272,14 @@ async def handle_document_related_message(self, msg: t.List[bytes]) -> t.Optiona
273272
target_cell["execution_count"] = execution_count
274273
break
275274

276-
case "stream" | "display_data" | "execute_result" | "error" | "update_display_data":
275+
case "stream" | "display_data" | "execute_result" | "error" | "update_display_data" | "clear_output":
277276
if cell_id:
278277
# Process specific output messages through an optional processor
279278
if self.output_processor and cell_id:
280279
cell_id = parent_msg_data.get('cell_id')
281280
content = self.session.unpack(dmsg["content"])
282-
dmsg = self.output_processor.process_output(dmsg['msg_type'], cell_id, content)
281+
self.output_processor.process_output(dmsg['msg_type'], cell_id, content)
282+
283283
# Suppress forwarding of processed messages by returning None
284284
return None
285285

jupyter_server_documents/outputs/output_processor.py

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@
22

33
from pycrdt import Map
44

5-
from traitlets import Unicode, Bool
5+
from traitlets import Unicode, Bool, Dict
66
from traitlets.config import LoggingConfigurable
77
from jupyter_server_documents.kernels.message_cache import KernelMessageCache
88

99
class OutputProcessor(LoggingConfigurable):
1010

1111
_file_id = Unicode(default_value=None, allow_none=True)
12+
_clear_output_cells = Dict(default_value={})
1213

1314
use_outputs_service = Bool(
1415
default_value=True,
@@ -46,6 +47,17 @@ def yroom_manager(self):
4647
"""A shortcut for the jupyter server ydoc manager."""
4748
return self.settings["yroom_manager"]
4849

50+
51+
async def get_notebook_ydoc(self, file_id):
52+
room_id = f"json:notebook:{file_id}"
53+
room = self.yroom_manager.get_room(room_id)
54+
if room is None:
55+
self.log.error(f"YRoom not found: {room_id}")
56+
return
57+
notebook = await room.get_jupyter_ydoc()
58+
59+
return notebook
60+
4961
async def clear_cell_outputs(self, cell_id, room_id):
5062
"""Clears the outputs of a cell in ydoc"""
5163
room = self.yroom_manager.get_room(room_id)
@@ -62,11 +74,19 @@ async def clear_cell_outputs(self, cell_id, room_id):
6274

6375
def clear(self, cell_id):
6476
"""Clear all outputs for a given cell Id."""
77+
78+
task = None
79+
6580
if self._file_id is not None:
6681
if self.use_outputs_service:
6782
room_id = f"json:notebook:{self._file_id}"
68-
asyncio.create_task(self.clear_cell_outputs(cell_id, room_id))
83+
task = asyncio.create_task(self.clear_cell_outputs(cell_id, room_id))
6984
self.outputs_manager.clear(file_id=self._file_id, cell_id=cell_id)
85+
86+
# Remove any pending delayed clear for this cell
87+
self._clear_output_cells.pop(cell_id, None)
88+
89+
return task
7090

7191
# Outgoing messages
7292
def process_output(self, msg_type: str, cell_id: str, content: dict):
@@ -87,6 +107,23 @@ def process_output(self, msg_type: str, cell_id: str, content: dict):
87107

88108
async def output_task(self, msg_type, cell_id, content):
89109
"""A coroutine to handle output messages."""
110+
111+
if msg_type == "clear_output":
112+
if not content.get("wait", False):
113+
# Clear immediately
114+
self.clear(cell_id)
115+
else:
116+
# Mark for delayed clear, overriding any previous pending clear
117+
self._clear_output_cells[cell_id] = True
118+
119+
return
120+
121+
# Check for delayed clear before processing output
122+
if cell_id in self._clear_output_cells and msg_type != "clear_output":
123+
clear_task = self.clear(cell_id)
124+
if clear_task is not None:
125+
await clear_task
126+
90127
try:
91128
# TODO: The session manager may have multiple notebooks connected to the kernel
92129
# but currently get_session only returns the first. We need to fix this and

0 commit comments

Comments
 (0)