Skip to content

Commit 0c4c141

Browse files
committed
Support awareness over Comm
1 parent 3257f71 commit 0c4c141

2 files changed

Lines changed: 42 additions & 8 deletions

File tree

src/ypywidgets/comm.py

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
from __future__ import annotations
2+
from typing import Any, Callable
23

34
import comm
45
from pycrdt import (
6+
Awareness,
57
Doc,
68
Text,
79
TransactionEvent,
@@ -10,6 +12,7 @@
1012
create_sync_message,
1113
create_update_message,
1214
handle_sync_message,
15+
read_message,
1316
)
1417

1518
from .widget import Widget
@@ -48,18 +51,27 @@ def __init__(
4851
) -> None:
4952
self._ydoc = ydoc
5053
self._comm = comm
54+
self._awareness = Awareness(ydoc)
5155
msg = create_sync_message(ydoc)
5256
self._comm.send(buffers=[msg])
5357
self._comm.on_msg(self._receive)
5458

59+
@property
60+
def awareness(self) -> Awareness:
61+
return self._awareness
62+
5563
def _receive(self, msg):
5664
message = bytes(msg["buffers"][0])
57-
if message[0] == YMessageType.SYNC:
65+
mtype = message[0]
66+
if mtype == YMessageType.SYNC:
5867
reply = handle_sync_message(message[1:], self._ydoc)
5968
if reply is not None:
6069
self._comm.send(buffers=[reply])
6170
if message[1] == YSyncMessageType.SYNC_STEP2:
6271
self._ydoc.observe(self._send)
72+
elif mtype == YMessageType.AWARENESS:
73+
payload = read_message(message[1:])
74+
self._awareness.apply_awareness_update(payload, None)
6375

6476
def _send(self, event: TransactionEvent):
6577
update = event.update
@@ -69,12 +81,12 @@ def _send(self, event: TransactionEvent):
6981

7082
class CommWidget(Widget):
7183
def __init__(
72-
self,
73-
ydoc: Doc | None = None,
74-
comm_data: dict | None = None,
75-
comm_metadata: dict | None = None,
76-
comm_id: str | None = None,
77-
):
84+
self,
85+
ydoc: Doc | None = None,
86+
comm_data: dict | None = None,
87+
comm_metadata: dict | None = None,
88+
comm_id: str | None = None,
89+
):
7890
super().__init__(ydoc)
7991
model_name = self.__class__.__name__
8092
_model_name = self.ydoc["_model_name"] = Text()
@@ -85,7 +97,21 @@ def __init__(
8597
create_ydoc=not ydoc,
8698
)
8799
self._comm = create_widget_comm(comm_data, comm_metadata, comm_id)
88-
CommProvider(self.ydoc, self._comm)
100+
self._comm_provider = CommProvider(self.ydoc, self._comm)
101+
102+
@property
103+
def awareness(self) -> Awareness:
104+
return self._comm_provider.awareness
105+
106+
def on_awareness_change(
107+
self,
108+
callback: Callable[[str, tuple[dict[str, Any], Any]], None],
109+
) -> str:
110+
"""Subscribe to pycrdt Awareness updates; returns subscription id for unobserve."""
111+
return self.awareness.observe(callback)
112+
113+
def unobserve_awareness(self, subscription_id: str) -> None:
114+
self.awareness.unobserve(subscription_id)
89115

90116
def _repr_mimebundle_(self, *args, **kwargs): # pragma: nocover
91117
plaintext = repr(self)

tests/conftest.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@
55
import comm
66
import pytest
77
from pycrdt import (
8+
Awareness,
89
YMessageType,
910
YSyncMessageType,
1011
TransactionEvent,
1112
create_sync_message,
1213
create_update_message,
1314
handle_sync_message,
15+
read_message,
1416
)
1517
from ypywidgets import Widget
1618
from ypywidgets.comm import CommWidget
@@ -67,6 +69,7 @@ def __init__(self, widget_factory, comm):
6769
self.widget_factory = widget_factory
6870
self.comm = comm
6971
self.widget = None
72+
self._remote_awareness: Optional[Awareness] = None
7073
self.receive_task = asyncio.create_task(self.receive())
7174

7275
def send(self, event: TransactionEvent):
@@ -89,6 +92,11 @@ async def receive(self):
8992
self.comm.handle_msg({"buffers": [reply]})
9093
if message[1] == YSyncMessageType.SYNC_STEP2:
9194
self.widget.ydoc.observe(self.send)
95+
elif message[0] == YMessageType.AWARENESS:
96+
if self._remote_awareness is None:
97+
self._remote_awareness = Awareness(self.widget.ydoc)
98+
payload = read_message(bytes(message[1:]))
99+
self._remote_awareness.apply_awareness_update(payload, None)
92100

93101
async def get_widget(self, timeout=0.1):
94102
t = time.monotonic()

0 commit comments

Comments
 (0)