From f0862467cffa10d9407304d1becb0bb20d1fee57 Mon Sep 17 00:00:00 2001 From: David Brochart Date: Mon, 25 May 2026 22:12:21 +0200 Subject: [PATCH 1/2] Fix widget initialization --- src/ypywidgets/comm.py | 15 ++++++++++++--- tests/test_attributes.py | 5 ++++- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/ypywidgets/comm.py b/src/ypywidgets/comm.py index b2d1d91..be0080c 100644 --- a/src/ypywidgets/comm.py +++ b/src/ypywidgets/comm.py @@ -54,6 +54,8 @@ def __init__( msg = create_sync_message(ydoc) self._comm.send(buffers=[msg]) self._comm.on_msg(self._receive) + self._subscription_id = ydoc.observe(self._bufferize) + self._updates: list[bytes] = [] @property def awareness(self) -> Awareness: @@ -67,15 +69,22 @@ def _receive(self, msg): if reply is not None: self._comm.send(buffers=[reply]) if message[1] == YSyncMessageType.SYNC_STEP2: - self._ydoc.observe(self._send) + self._ydoc.unobserve(self._subscription_id) + for update in self._updates: + message = create_update_message(update) + self._comm.send(buffers=[message]) + self._updates.clear() + self._subscription_id = self._ydoc.observe(self._send) case YMessageType.AWARENESS: # Same as pycrdt.websocket.yroom: strip Y message kind, decode body. update = read_message(message[1:]) self._awareness.apply_awareness_update(update, None) + def _bufferize(self, event: TransactionEvent): + self._updates.append(event.update) + def _send(self, event: TransactionEvent): - update = event.update - message = create_update_message(update) + message = create_update_message(event.update) self._comm.send(buffers=[message]) diff --git a/tests/test_attributes.py b/tests/test_attributes.py index 7e762d5..060b6fb 100644 --- a/tests/test_attributes.py +++ b/tests/test_attributes.py @@ -45,6 +45,7 @@ async def test_create_ydoc(synced_widgets, context): async def test_sync_attribute(widget_factories, synced_widgets, context): async with context: local_widget = await synced_widgets.get_local_widget() + local_widget.foo = "foo3" remote_widget = await synced_widgets.get_remote_widget() with pytest.raises(AttributeError): @@ -53,8 +54,10 @@ async def test_sync_attribute(widget_factories, synced_widgets, context): with pytest.raises(AttributeError): assert remote_widget.wrong_attr2 + await sleep(0.01) + assert remote_widget.foo == "foo3" local_widget.foo = "foo2" - assert remote_widget.foo is None # not synced yet + assert remote_widget.foo == "foo3" # not synced yet await sleep(0.01) # wait for sync assert remote_widget.foo == "foo2" From 5e0e45f5c755a4372a8f4b992d823b54a0979d9e Mon Sep 17 00:00:00 2001 From: David Brochart Date: Tue, 26 May 2026 10:04:54 +0200 Subject: [PATCH 2/2] Don't unobserve (multithreading issue) --- src/ypywidgets/comm.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/ypywidgets/comm.py b/src/ypywidgets/comm.py index be0080c..7c8cc04 100644 --- a/src/ypywidgets/comm.py +++ b/src/ypywidgets/comm.py @@ -54,8 +54,8 @@ def __init__( msg = create_sync_message(ydoc) self._comm.send(buffers=[msg]) self._comm.on_msg(self._receive) - self._subscription_id = ydoc.observe(self._bufferize) - self._updates: list[bytes] = [] + ydoc.observe(self._bufferize) + self._updates: list[bytes] | None = [] @property def awareness(self) -> Awareness: @@ -69,19 +69,20 @@ def _receive(self, msg): if reply is not None: self._comm.send(buffers=[reply]) if message[1] == YSyncMessageType.SYNC_STEP2: - self._ydoc.unobserve(self._subscription_id) + assert self._updates is not None for update in self._updates: message = create_update_message(update) self._comm.send(buffers=[message]) - self._updates.clear() - self._subscription_id = self._ydoc.observe(self._send) + self._updates = None + self._ydoc.observe(self._send) case YMessageType.AWARENESS: # Same as pycrdt.websocket.yroom: strip Y message kind, decode body. update = read_message(message[1:]) self._awareness.apply_awareness_update(update, None) def _bufferize(self, event: TransactionEvent): - self._updates.append(event.update) + if self._updates is not None: + self._updates.append(event.update) def _send(self, event: TransactionEvent): message = create_update_message(event.update)