11"""
22A new Kernel client that is aware of ydocuments.
33"""
4+ import anyio
45import asyncio
56import json
67import typing as t
7- from traitlets import Set
8- from traitlets import Instance
9- from traitlets import Any
10- from .utils import LRUCache
8+
9+ from traitlets import Set , Instance , Any , Type , default
1110from jupyter_client .asynchronous .client import AsyncKernelClient
12- import anyio
11+
12+ from .utils import LRUCache
1313from jupyter_rtc_core .rooms .yroom import YRoom
14+ from jupyter_rtc_core .outputs import OutputProcessor
1415
1516
1617class DocumentAwareKernelClient (AsyncKernelClient ):
@@ -35,6 +36,20 @@ class DocumentAwareKernelClient(AsyncKernelClient):
3536 # status messages.
3637 _yrooms : t .Set [YRoom ] = Set (trait = Instance (YRoom ), default_value = set ())
3738
39+ output_processor = Instance (
40+ OutputProcessor ,
41+ allow_none = True
42+ )
43+
44+ output_process_class = Type (
45+ klass = OutputProcessor ,
46+ default_value = OutputProcessor
47+ ).tag (config = True )
48+
49+ @default ("output_processor" )
50+ def _default_output_processor (self ) -> OutputProcessor :
51+ self .log .info ("Creating output processor" )
52+ return OutputProcessor (parent = self , config = self .config )
3853
3954 async def start_listening (self ):
4055 """Start listening to messages coming from the kernel.
@@ -78,7 +93,8 @@ def handle_incoming_message(self, channel_name: str, msg: list[bytes]):
7893 # Cache the message ID and its socket name so that
7994 # any response message can be mapped back to the
8095 # source channel.
81- header = header = json .loads (msg [0 ])
96+ self .output_processor .process_incoming_message (channel = channel_name , msg = msg )
97+ header = header = json .loads (msg [0 ]) # TODO: use session.unpack
8298 msg_id = header ["msg_id" ]
8399 self .message_source_cache [msg_id ] = channel_name
84100 channel = getattr (self , f"{ channel_name } _channel" )
@@ -159,10 +175,9 @@ async def handle_outgoing_message(self, channel_name: str, msg: list[bytes]):
159175 # Intercept messages that are IOPub focused.
160176 if channel_name == "iopub" :
161177 message_returned = await self .handle_iopub_message (msg )
162- # TODO: If the message is not returned by the iopub handler, then
178+ # If the message is not returned by the iopub handler, then
163179 # return here and do not forward to listeners.
164180 if not message_returned :
165- self .log .warn (f"If message is handled donot forward after adding output manager" )
166181 return
167182
168183 # Update the last activity.
@@ -182,47 +197,38 @@ async def handle_iopub_message(self, msg: list[bytes]) -> t.Optional[list[bytes]
182197 Returns
183198 -------
184199 Returns the message if it should be forwarded to listeners. Otherwise,
185- returns `None` and keeps (i.e. intercepts) the message from going
186- to listenres .
200+ returns `None` and prevents (i.e. intercepts) the message from going
201+ to listeners .
187202 """
188- # NOTE: Here's where we will inject the kernel state
189- # into the awareness of a document.
190203
191204 try :
192205 dmsg = self .session .deserialize (msg , content = False )
193206 except Exception as e :
194207 self .log .error (f"Error deserializing message: { e } " )
195208 raise ValueError
196209
197- if dmsg ["msg_type" ] == "status" :
198- # Forward to all yrooms.
199- for yroom in self ._yrooms :
200- # NOTE: We need to create a real message here.
201- awareness_update_message = b""
202- self .log .debug (f"Update Awareness here: { dmsg } . YRoom: { yroom } " )
203- #self.log.debug(f"Getting YDoc: {await yroom.get_ydoc()}")
204- #yroom.add_message(awareness_update_message)
210+ # if dmsg["msg_type"] == "status":
211+ # # Forward to all yrooms.
212+ # for yroom in self._yrooms:
213+ # # NOTE: We need to create a real message here.
214+ # awareness_update_message = b""
215+ # self.log.debug(f"Update Awareness here: {dmsg}. YRoom: {yroom}")
216+ # #self.log.debug(f"Getting YDoc: {await yroom.get_ydoc()}")
217+ # #yroom.add_message(awareness_update_message)
205218
206- # TODO: returning message temporarily to not break UI
207- return msg
219+ # # TODO: returning message temporarily to not break UI
220+ # return msg
208221
209-
210- # NOTE: Inject display data into ydoc.
211- if dmsg ["msg_type" ] == "display_data" :
212- # Forward to all yrooms.
213- for yroom in self ._yrooms :
214- update_document_message = b""
215- self .log .debug (f"Update Document here: { dmsg } . Yroom: { yroom } " )
216- #self.log.debug(f"Getting YDoc: {await yroom.get_ydoc()}")
217- #yroom.add_message(update_document_message)
218-
219- # TODO: returning message temporarily to not break UI
222+ if self .output_processor is not None and dmsg ["msg_type" ] in ("stream" , "display_data" , "execute_result" , "error" ):
223+ dmsg = self .output_processor .process_outgoing_message (dmsg )
224+
225+ # If process_outgoing_message returns None, return None so the message isn't
226+ # sent to clients, otherwise return the original serialized message.
227+ if dmsg is None :
228+ return None
229+ else :
220230 return msg
221231
222- # If the message isn't handled above, return it and it will
223- # be forwarded to all listeners
224- return msg
225-
226232 async def add_yroom (self , yroom : YRoom ):
227233 """
228234 Register a YRoom with this kernel client. YRooms will
0 commit comments