Skip to content

Commit

Permalink
Merge pull request #19 from davidbrochart/base-class
Browse files Browse the repository at this point in the history
Create base Widget class, CommProvider and CommWidget
  • Loading branch information
davidbrochart authored Nov 10, 2023
2 parents e485a28 + 58efabf commit ffdb18b
Show file tree
Hide file tree
Showing 8 changed files with 127 additions and 112 deletions.
1 change: 0 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ authors = [
keywords = [
"widgets",
"jupyter",
"ypy",
"yjs",
]
dependencies = [
Expand Down
11 changes: 6 additions & 5 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import pytest
from pycrdt import TransactionEvent
from ypywidgets import Widget
from ypywidgets.comm import CommWidget
from ypywidgets.utils import YMessageType, YSyncMessageType, create_update_message, process_sync_message, sync


Expand Down Expand Up @@ -40,7 +41,7 @@ async def receive(self):

@pytest.fixture
def widget_factories():
return Widget, Widget
return CommWidget, Widget


@pytest.fixture
Expand Down Expand Up @@ -71,17 +72,17 @@ async def receive(self):
while True:
msg_type, data, metadata, buffers, target_name, target_module = await self.comm.send_queue.get()
if msg_type == "comm_open":
self.widget = self.widget_factory(primary=False)
msg = sync(self.widget._ydoc)
self.widget = self.widget_factory()
msg = sync(self.widget.ydoc)
self.comm.handle_msg(msg)
elif msg_type == "comm_msg":
message = buffers[0]
if message[0] == YMessageType.SYNC:
reply = process_sync_message(message[1:], self.widget._ydoc)
reply = process_sync_message(message[1:], self.widget.ydoc)
if reply:
self.comm.handle_msg({"buffers": [reply]})
if message[1] == YSyncMessageType.SYNC_STEP2:
self.widget._ydoc.observe(self.send)
self.widget.ydoc.observe(self.send)

async def get_widget(self, timeout=0.1):
t = time.monotonic()
Expand Down
11 changes: 6 additions & 5 deletions tests/test_attributes.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,17 @@

import pytest
from pycrdt import Text
from ypywidgets import Widget, reactive
from ypywidgets import reactive
from ypywidgets.comm import CommWidget


class Widget1(Widget):
class Widget1(CommWidget):
foo = reactive("foo1")
bar = reactive("bar1")
baz: reactive[str | None] = reactive(None)


class Widget2(Widget):
class Widget2(CommWidget):
foo = reactive("")

def watch_foo(self, old, new):
Expand All @@ -24,12 +25,12 @@ async def test_create_ydoc(synced_widgets):
local_widget, remote_widget = await synced_widgets

local_text = Text()
local_widget._ydoc["text"] = local_text
local_widget.ydoc["text"] = local_text
text = "hello world!"
local_text += text

remote_text = Text()
remote_widget._ydoc["text"] = remote_text
remote_widget.ydoc["text"] = remote_text
await asyncio.sleep(0.01)
assert str(remote_text) == text

Expand Down
4 changes: 2 additions & 2 deletions ypywidgets/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from .ypywidgets import Widget # noqa
from .reactive import reactive # noqa
from .widget import Widget as Widget
from .reactive import reactive as reactive


__version__ = "0.5.0"
89 changes: 89 additions & 0 deletions ypywidgets/comm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
from __future__ import annotations

import comm
from pycrdt import Doc, Text, TransactionEvent

from .utils import (
YMessageType,
YSyncMessageType,
create_update_message,
process_sync_message,
sync,
)
from .widget import Widget


def create_widget_comm(
data: dict | None = None,
metadata: dict | None = None,
comm_id: str | None = None,
) -> comm.base_comm.BaseComm:
_comm = comm.create_comm(
comm_id=comm_id,
target_name="ywidget",
data=data,
metadata=metadata,
)
return _comm


class CommProvider:
def __init__(
self,
ydoc: Doc,
comm: comm.base_comm.BaseComm,
) -> None:
self._ydoc = ydoc
self._comm = comm
msg = sync(ydoc)
self._comm.send(**msg)
self._comm.on_msg(self._receive)

def _receive(self, msg):
message = bytes(msg["buffers"][0])
if message[0] == YMessageType.SYNC:
reply = process_sync_message(message[1:], self._ydoc)
if reply:
self._comm.send(buffers=[reply])
if message[1] == YSyncMessageType.SYNC_STEP2:
self._ydoc.observe(self._send)

def _send(self, event: TransactionEvent):
update = event.get_update()
message = create_update_message(update)
self._comm.send(buffers=[message])


class CommWidget(Widget):
def __init__(
self,
ydoc: Doc | None = None,
comm_data: dict | None = None,
comm_metadata: dict | None = None,
comm_id: str | None = None,
):
super().__init__(ydoc)
model_name = self.__class__.__name__
_model_name = self.ydoc["_model_name"] = Text()
_model_name += model_name
if comm_metadata is None:
comm_metadata = dict(
ymodel_name=model_name,
create_ydoc=not ydoc,
)
self._comm = create_widget_comm(comm_data, comm_metadata, comm_id)
CommProvider(self.ydoc, self._comm)

def _repr_mimebundle_(self, **kwargs):
plaintext = repr(self)
if len(plaintext) > 110:
plaintext = plaintext[:110] + '…'
data = {
"text/plain": plaintext,
"application/vnd.jupyter.ywidget-view+json": {
"version_major": 2,
"version_minor": 0,
"model_id": self._comm.comm_id,
}
}
return data
3 changes: 0 additions & 3 deletions ypywidgets/reactive.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@


def set_attr(obj, name, value):
if obj._attrs is None:
return

try:
obj._attrs[name] = value
except RuntimeError:
Expand Down
24 changes: 24 additions & 0 deletions ypywidgets/widget.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from __future__ import annotations

from pycrdt import Doc, Map, Text


class Widget:
_initialized = False
_attrs: Map
ydoc: Doc

def __init__(self, ydoc: Doc | None = None) -> None:
if self._initialized:
return
self._initialized = True
self.ydoc = Doc() if ydoc is None else ydoc
self.ydoc["_attrs"] = self._attrs = Map()
self.ydoc["_model_name"] = Text()
self._attrs.observe(self._set_attr)

def _set_attr(self, event):
for k, v in event.keys.items():
new_value = v["newValue"]
if getattr(self, k) != new_value:
setattr(self, k, new_value)
96 changes: 0 additions & 96 deletions ypywidgets/ypywidgets.py

This file was deleted.

0 comments on commit ffdb18b

Please sign in to comment.