Skip to content

Commit 826446f

Browse files
brichetdavidbrochartpre-commit-ci[bot]
authored
Broadcast server awareness to all clients (#73)
* Add methods to modify the awareness server side, server being a regular user * Server can subscribe to awareness changes (only those from the frontend) * mypy * Add tests * ruff and mypy * Avoid depending on nest_asyncio * Use the YRoom task group for asyncrhonous call * Remove the Awareness object in favor of a futur one in pycrdt * Apply suggestions from code review Co-authored-by: David Brochart <[email protected]> * Log an error if the task group is not initialized * Update pycrdt_websocket/yroom.py Co-authored-by: David Brochart <[email protected]> * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Remove the logger in the Awareness args * Docstring * Update to pycrdt 0.10 and observe the awareness changes to broadcast local changes * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * mypy * Apply suggestions Co-authored-by: David Brochart <[email protected]> * Broadcast the changes only if the state actually changes * Apply suggestions from code review Co-authored-by: David Brochart <[email protected]> * Broadcast all updates Co-authored-by: David Brochart <[email protected]> * Update pyproject.toml Co-authored-by: David Brochart <[email protected]> --------- Co-authored-by: David Brochart <[email protected]> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: David Brochart <[email protected]>
1 parent 78daf4e commit 826446f

File tree

2 files changed

+37
-4
lines changed

2 files changed

+37
-4
lines changed

pycrdt_websocket/yroom.py

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from functools import partial
55
from inspect import isawaitable
66
from logging import Logger, getLogger
7-
from typing import Awaitable, Callable
7+
from typing import Any, Awaitable, Callable
88

99
from anyio import (
1010
TASK_STATUS_IGNORED,
@@ -16,16 +16,17 @@
1616
from anyio.abc import TaskGroup, TaskStatus
1717
from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream
1818
from pycrdt import (
19+
Awareness,
1920
Doc,
2021
Subscription,
2122
YMessageType,
2223
YSyncMessageType,
24+
create_awareness_message,
2325
create_sync_message,
2426
create_update_message,
2527
handle_sync_message,
2628
)
2729

28-
from .awareness import Awareness
2930
from .websocket import Websocket
3031
from .ystore import BaseYStore
3132
from .yutils import put_updates
@@ -77,11 +78,12 @@ def __init__(
7778
ydoc: An optional document for the room (a new one is created otherwise).
7879
"""
7980
self.ydoc = Doc() if ydoc is None else ydoc
80-
self.awareness = Awareness(self.ydoc)
8181
self.ready_event = Event()
8282
self.ready = ready
8383
self.ystore = ystore
8484
self.log = log or getLogger(__name__)
85+
self.awareness = Awareness(self.ydoc)
86+
self.awareness.observe(self.send_server_awareness)
8587
self.clients = set()
8688
self._on_message = None
8789
self.exception_handler = exception_handler
@@ -304,3 +306,34 @@ async def serve(self, websocket: Websocket):
304306
self.clients.remove(websocket)
305307
except Exception as exception:
306308
self._handle_exception(exception)
309+
310+
def send_server_awareness(self, type: str, changes: tuple[dict[str, Any], Any]) -> None:
311+
"""
312+
Callback to broadcast the server awareness to clients.
313+
314+
Arguments:
315+
type: The change type.
316+
changes: The awareness changes.
317+
"""
318+
if type != "update" or changes[1] != "local":
319+
return
320+
321+
if self._task_group is not None:
322+
updated_clients = [v for value in changes[0].values() for v in value]
323+
state = self.awareness.encode_awareness_update(updated_clients)
324+
message = create_awareness_message(state)
325+
self._task_group.start_soon(self._send_server_awareness, message)
326+
else:
327+
self.log.error("Cannot broadcast server awareness: YRoom not started")
328+
329+
async def _send_server_awareness(self, state: bytes) -> None:
330+
try:
331+
async with create_task_group() as tg:
332+
for client in self.clients:
333+
self.log.debug(
334+
"Sending awareness from server to client with endpoint: %s",
335+
client.path,
336+
)
337+
tg.start_soon(client.send, state)
338+
except Exception as e:
339+
self.log.error("Error while broadcasting awareness changes: %s", e)

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ classifiers = [
3030
dependencies = [
3131
"anyio >=3.6.2,<5",
3232
"sqlite-anyio >=0.2.3,<0.3.0",
33-
"pycrdt >=0.9.16,<0.10.0",
33+
"pycrdt >=0.10.1,<0.11.0",
3434
]
3535

3636
[project.optional-dependencies]

0 commit comments

Comments
 (0)