diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f14a6012..035d2ab2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -44,7 +44,7 @@ repos: - id: mypy exclude: "(^binder/jupyter_config\\.py$)|(^scripts/bump_version\\.py$)|(/setup\\.py$)" args: ["--config-file", "pyproject.toml"] - additional_dependencies: [tornado, pytest, 'pycrdt-websocket<0.16.0'] + additional_dependencies: [tornado, pytest, 'pycrdt-websocket>=0.16.0,<0.17.0'] stages: [manual] - repo: https://github.com/sirosen/check-jsonschema diff --git a/docs/source/configuration.md b/docs/source/configuration.md index 64a32a11..6161d298 100644 --- a/docs/source/configuration.md +++ b/docs/source/configuration.md @@ -27,7 +27,7 @@ jupyter lab --YDocExtension.file_poll_interval=2 jupyter lab --YDocExtension.document_cleanup_delay=100 # The YStore class to use for storing Y updates (default: JupyterSQLiteYStore). -jupyter lab --YDocExtension.ystore_class=pycrdt_websocket.ystore.TempFileYStore +jupyter lab --YDocExtension.ystore_class=pycrdt.store.TempFileYStore ``` There is an experimental feature that is currently only supported by the diff --git a/projects/jupyter-server-ydoc/jupyter_server_ydoc/app.py b/projects/jupyter-server-ydoc/jupyter_server_ydoc/app.py index b1d73d59..230b8496 100644 --- a/projects/jupyter-server-ydoc/jupyter_server_ydoc/app.py +++ b/projects/jupyter-server-ydoc/jupyter_server_ydoc/app.py @@ -10,7 +10,7 @@ from jupyter_ydoc import ydocs as YDOCS from jupyter_ydoc.ybasedoc import YBaseDoc from pycrdt import Doc -from pycrdt_websocket.ystore import BaseYStore +from pycrdt.store import BaseYStore from traitlets import Bool, Float, Type from .handlers import ( diff --git a/projects/jupyter-server-ydoc/jupyter_server_ydoc/handlers.py b/projects/jupyter-server-ydoc/jupyter_server_ydoc/handlers.py index 675e91b7..727cce0b 100644 --- a/projects/jupyter-server-ydoc/jupyter_server_ydoc/handlers.py +++ b/projects/jupyter-server-ydoc/jupyter_server_ydoc/handlers.py @@ -16,8 +16,8 @@ from jupyter_server.utils import ensure_async from jupyter_ydoc import ydocs as YDOCS from pycrdt import Doc, Encoder, UndoManager -from pycrdt_websocket.yroom import YRoom -from pycrdt_websocket.ystore import BaseYStore +from pycrdt.store import BaseYStore +from pycrdt.websocket import YRoom from tornado import web from tornado.websocket import WebSocketHandler @@ -120,7 +120,7 @@ def exception_logger(exception: Exception, log: Logger) -> bool: updates_file_path = f".{file_type}:{file_id}.y" ystore = self._ystore_class( path=updates_file_path, - log=self.log, # type:ignore[call-arg] + log=self.log, ) self.room = DocumentRoom( self._room_id, diff --git a/projects/jupyter-server-ydoc/jupyter_server_ydoc/pytest_plugin.py b/projects/jupyter-server-ydoc/jupyter_server_ydoc/pytest_plugin.py index a41d69d7..b26fff82 100644 --- a/projects/jupyter-server-ydoc/jupyter_server_ydoc/pytest_plugin.py +++ b/projects/jupyter-server-ydoc/jupyter_server_ydoc/pytest_plugin.py @@ -14,13 +14,13 @@ from jupyter_server_ydoc.rooms import DocumentRoom from jupyter_server_ydoc.stores import SQLiteYStore from jupyter_ydoc import YNotebook, YUnicode -from pycrdt_websocket import WebsocketProvider +from pycrdt import Provider +from pycrdt.websocket.websocket import HttpxWebsocket from .test_utils import ( FakeContentsManager, FakeEventLogger, FakeFileIDManager, - Websocket, ) @@ -231,7 +231,7 @@ def _on_document_change(target: str, e: Any) -> None: doc.observe(_on_document_change) websocket, room_name = await rtc_connect_doc_client(format, type, path) - async with websocket as ws, WebsocketProvider(doc.ydoc, Websocket(ws, room_name)): + async with websocket as ws, Provider(doc.ydoc, HttpxWebsocket(ws, room_name)): await event.wait() await sleep(0.1) @@ -243,7 +243,7 @@ async def _inner(type: str, path: str, content: str) -> DocumentRoom: db = SQLiteYStore( path=f"{type}:{path}", # `SQLiteYStore` here is a subclass of booth `LoggingConfigurable` - # and `pycrdt_websocket.ystore.SQLiteYStore`, but mypy gets lost: + # and `pycrdt.store.SQLiteYStore`, but mypy gets lost: config=jp_serverapp.config, # type:ignore[call-arg] ) _ = create_task(db.start()) diff --git a/projects/jupyter-server-ydoc/jupyter_server_ydoc/rooms.py b/projects/jupyter-server-ydoc/jupyter_server_ydoc/rooms.py index 15b11046..0708f076 100644 --- a/projects/jupyter-server-ydoc/jupyter_server_ydoc/rooms.py +++ b/projects/jupyter-server-ydoc/jupyter_server_ydoc/rooms.py @@ -9,8 +9,8 @@ from jupyter_events import EventLogger from jupyter_ydoc import ydocs as YDOCS -from pycrdt_websocket.yroom import YRoom -from pycrdt_websocket.ystore import BaseYStore, YDocNotFound +from pycrdt.websocket import YRoom +from pycrdt.store import BaseYStore, YDocNotFound from .loaders import FileLoader from .utils import JUPYTER_COLLABORATION_EVENTS_URI, LogLevel, OutOfBandChanges diff --git a/projects/jupyter-server-ydoc/jupyter_server_ydoc/stores.py b/projects/jupyter-server-ydoc/jupyter_server_ydoc/stores.py index 4f31a9de..0e17e855 100644 --- a/projects/jupyter-server-ydoc/jupyter_server_ydoc/stores.py +++ b/projects/jupyter-server-ydoc/jupyter_server_ydoc/stores.py @@ -1,8 +1,8 @@ # Copyright (c) Jupyter Development Team. # Distributed under the terms of the Modified BSD License. -from pycrdt_websocket.ystore import SQLiteYStore as _SQLiteYStore -from pycrdt_websocket.ystore import TempFileYStore as _TempFileYStore +from pycrdt.store import SQLiteYStore as _SQLiteYStore +from pycrdt.store import TempFileYStore as _TempFileYStore from traitlets import Int, Unicode from traitlets.config import LoggingConfigurable diff --git a/projects/jupyter-server-ydoc/jupyter_server_ydoc/test_utils.py b/projects/jupyter-server-ydoc/jupyter_server_ydoc/test_utils.py index 24528004..f1140322 100644 --- a/projects/jupyter-server-ydoc/jupyter_server_ydoc/test_utils.py +++ b/projects/jupyter-server-ydoc/jupyter_server_ydoc/test_utils.py @@ -6,7 +6,6 @@ from datetime import datetime from typing import Any -from anyio import Lock from jupyter_server import _tz as tz @@ -56,32 +55,3 @@ def save_content(self, model: dict[str, Any], path: str) -> dict: class FakeEventLogger: def emit(self, schema_id: str, data: dict) -> None: print(data) - - -class Websocket: - def __init__(self, websocket: Any, path: str): - self._websocket = websocket - self._path = path - self._send_lock = Lock() - - @property - def path(self) -> str: - return self._path - - def __aiter__(self): - return self - - async def __anext__(self) -> bytes: - try: - message = await self.recv() - except Exception: - raise StopAsyncIteration() - return message - - async def send(self, message: bytes) -> None: - async with self._send_lock: - await self._websocket.send_bytes(message) - - async def recv(self) -> bytes: - b = await self._websocket.receive_bytes() - return bytes(b) diff --git a/projects/jupyter-server-ydoc/jupyter_server_ydoc/websocketserver.py b/projects/jupyter-server-ydoc/jupyter_server_ydoc/websocketserver.py index 06529efa..376a4ad3 100644 --- a/projects/jupyter-server-ydoc/jupyter_server_ydoc/websocketserver.py +++ b/projects/jupyter-server-ydoc/jupyter_server_ydoc/websocketserver.py @@ -7,10 +7,9 @@ from logging import Logger from typing import Any, Callable -from pycrdt_websocket.websocket import Websocket -from pycrdt_websocket.websocket_server import WebsocketServer -from pycrdt_websocket.yroom import YRoom -from pycrdt_websocket.ystore import BaseYStore +from pycrdt import Channel +from pycrdt.store import BaseYStore +from pycrdt.websocket import WebsocketServer, YRoom class RoomNotFound(LookupError): @@ -133,7 +132,7 @@ async def get_room(self, path: str) -> YRoom: await self.start_room(room) return room - async def serve(self, websocket: Websocket) -> None: + async def serve(self, websocket: Channel) -> None: # start monitoring here as the event loop is not yet available when initializing the object if self.monitor_task is None: self.monitor_task = asyncio.create_task(self._monitor()) diff --git a/projects/jupyter-server-ydoc/pyproject.toml b/projects/jupyter-server-ydoc/pyproject.toml index f2ec5d76..1dbafad9 100644 --- a/projects/jupyter-server-ydoc/pyproject.toml +++ b/projects/jupyter-server-ydoc/pyproject.toml @@ -29,9 +29,9 @@ authors = [ ] dependencies = [ "jupyter_server>=2.15.0,<3.0.0", - "jupyter_ydoc>=2.1.2,<4.0.0,!=3.0.0,!=3.0.1", + "jupyter_ydoc>=3.0.3,<4.0.0", "pycrdt", - "pycrdt-websocket>=0.15.0,<0.16.0", + "pycrdt-websocket>=0.16.0,<0.17.0", "jupyter_events>=0.11.0", "jupyter_server_fileid>=0.7.0,<1", "jsonschema>=4.18.0" diff --git a/tests/test_documents.py b/tests/test_documents.py index 109b4c39..6e929997 100644 --- a/tests/test_documents.py +++ b/tests/test_documents.py @@ -11,8 +11,8 @@ import pytest from anyio import create_task_group, sleep -from jupyter_server_ydoc.test_utils import Websocket -from pycrdt_websocket import WebsocketProvider +from pycrdt import Provider +from pycrdt.websocket.websocket import HttpxWebsocket jupyter_ydocs = {ep.name: ep.load() for ep in entry_points(group="jupyter_ydoc")} @@ -34,7 +34,7 @@ async def test_dirty( jupyter_ydoc = jupyter_ydocs[file_type]() websocket, room_name = await rtc_connect_doc_client(file_format, file_type, file_path) - async with websocket as ws, WebsocketProvider(jupyter_ydoc.ydoc, Websocket(ws, room_name)): + async with websocket as ws, Provider(jupyter_ydoc.ydoc, HttpxWebsocket(ws, room_name)): for _ in range(2): jupyter_ydoc.dirty = True await sleep(rtc_document_save_delay * 1.5) diff --git a/tests/test_handlers.py b/tests/test_handlers.py index 6bfdd2f6..440d11b5 100644 --- a/tests/test_handlers.py +++ b/tests/test_handlers.py @@ -9,10 +9,9 @@ from dirty_equals import IsStr from jupyter_events.logger import EventLogger -from jupyter_server_ydoc.test_utils import Websocket from jupyter_ydoc import YUnicode -from pycrdt import Text -from pycrdt_websocket import WebsocketProvider +from pycrdt import Text, Provider +from pycrdt.websocket.websocket import HttpxWebsocket async def test_session_handler_should_create_session_id( @@ -81,7 +80,7 @@ def _on_document_change(target: str, e: Any) -> None: doc.observe(_on_document_change) websocket, room_name = await rtc_connect_doc_client("text", "file", path) - async with websocket as ws, WebsocketProvider(doc.ydoc, Websocket(ws, room_name)): + async with websocket as ws, Provider(doc.ydoc, HttpxWebsocket(ws, room_name)): await event.wait() await sleep(0.1) @@ -117,7 +116,7 @@ async def my_listener(logger: EventLogger, schema_id: str, data: dict) -> None: ) websocket, room_name = await rtc_connect_doc_client("text", "file", path) - async with websocket as ws, WebsocketProvider(doc.ydoc, Websocket(ws, room_name)): + async with websocket as ws, Provider(doc.ydoc, HttpxWebsocket(ws, room_name)): await event.wait() await sleep(0.1) @@ -149,7 +148,7 @@ def _on_document_change(target: str, e: Any) -> None: doc.observe(_on_document_change) websocket, room_name = await rtc_connect_doc_client("text", "file", path) - async with websocket as ws, WebsocketProvider(doc.ydoc, Websocket(ws, room_name)): + async with websocket as ws, Provider(doc.ydoc, HttpxWebsocket(ws, room_name)): await event.wait() await sleep(0.1) @@ -174,7 +173,7 @@ async def my_listener(logger: EventLogger, schema_id: str, data: dict) -> None: try: websocket, room_name = await rtc_connect_doc_client("text2", "file2", path2) - async with websocket as ws, WebsocketProvider(doc.ydoc, Websocket(ws, room_name)): + async with websocket as ws, Provider(doc.ydoc, HttpxWebsocket(ws, room_name)): await event.wait() await sleep(0.1) except Exception: @@ -182,7 +181,7 @@ async def my_listener(logger: EventLogger, schema_id: str, data: dict) -> None: try: websocket, room_name = await rtc_connect_doc_client("text2", "file2", path2) - async with websocket as ws, WebsocketProvider(doc.ydoc, Websocket(ws, room_name)): + async with websocket as ws, Provider(doc.ydoc, HttpxWebsocket(ws, room_name)): await event.wait() await sleep(0.1) except Exception: @@ -253,7 +252,7 @@ def _on_root_change(topic: str, event: Any) -> None: root_roomid = f"text:file:{file_id}" websocket, room_name = await rtc_connect_doc_client("text", "file", path) - async with websocket as ws, WebsocketProvider(root_ydoc.ydoc, Websocket(ws, room_name)): + async with websocket as ws, Provider(root_ydoc.ydoc, HttpxWebsocket(ws, room_name)): await root_connect_event.wait() resp = await rtc_create_fork_client(root_roomid, False, "my fork0", "is awesome0") @@ -316,8 +315,8 @@ def _on_fork_change(topic: str, event: Any) -> None: fork_ydoc.observe(_on_fork_change) fork_text = fork_ydoc.ydoc.get("source", type=Text) - async with await rtc_connect_fork_client(fork_roomid1) as ws, WebsocketProvider( - fork_ydoc.ydoc, Websocket(ws, fork_roomid1) + async with await rtc_connect_fork_client(fork_roomid1) as ws, Provider( + fork_ydoc.ydoc, HttpxWebsocket(ws, fork_roomid1) ): await fork_connect_event.wait() root_text = root_ydoc.ydoc.get("source", type=Text)