jupyter-server-ydoc 2.0.2__tar.gz → 2.1.0b0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {jupyter_server_ydoc-2.0.2 → jupyter_server_ydoc-2.1.0b0}/PKG-INFO +1 -1
- jupyter_server_ydoc-2.1.0b0/jupyter_server_ydoc/_version.py +1 -0
- {jupyter_server_ydoc-2.0.2 → jupyter_server_ydoc-2.1.0b0}/jupyter_server_ydoc/handlers.py +15 -0
- {jupyter_server_ydoc-2.0.2 → jupyter_server_ydoc-2.1.0b0}/jupyter_server_ydoc/pytest_plugin.py +1 -1
- {jupyter_server_ydoc-2.0.2 → jupyter_server_ydoc-2.1.0b0}/jupyter_server_ydoc/rooms.py +27 -1
- {jupyter_server_ydoc-2.0.2 → jupyter_server_ydoc-2.1.0b0}/jupyter_server_ydoc/utils.py +1 -0
- {jupyter_server_ydoc-2.0.2 → jupyter_server_ydoc-2.1.0b0}/tests/test_rooms.py +45 -3
- jupyter_server_ydoc-2.0.2/jupyter_server_ydoc/_version.py +0 -1
- {jupyter_server_ydoc-2.0.2 → jupyter_server_ydoc-2.1.0b0}/.gitignore +0 -0
- {jupyter_server_ydoc-2.0.2 → jupyter_server_ydoc-2.1.0b0}/LICENSE +0 -0
- {jupyter_server_ydoc-2.0.2 → jupyter_server_ydoc-2.1.0b0}/README.md +0 -0
- {jupyter_server_ydoc-2.0.2 → jupyter_server_ydoc-2.1.0b0}/jupyter-config/jupyter_server_ydoc.json +0 -0
- {jupyter_server_ydoc-2.0.2 → jupyter_server_ydoc-2.1.0b0}/jupyter_server_ydoc/__init__.py +0 -0
- {jupyter_server_ydoc-2.0.2 → jupyter_server_ydoc-2.1.0b0}/jupyter_server_ydoc/app.py +0 -0
- {jupyter_server_ydoc-2.0.2 → jupyter_server_ydoc-2.1.0b0}/jupyter_server_ydoc/events/awareness.yaml +0 -0
- {jupyter_server_ydoc-2.0.2 → jupyter_server_ydoc-2.1.0b0}/jupyter_server_ydoc/events/fork.yaml +0 -0
- {jupyter_server_ydoc-2.0.2 → jupyter_server_ydoc-2.1.0b0}/jupyter_server_ydoc/events/session.yaml +0 -0
- {jupyter_server_ydoc-2.0.2 → jupyter_server_ydoc-2.1.0b0}/jupyter_server_ydoc/loaders.py +0 -0
- {jupyter_server_ydoc-2.0.2 → jupyter_server_ydoc-2.1.0b0}/jupyter_server_ydoc/stores.py +0 -0
- {jupyter_server_ydoc-2.0.2 → jupyter_server_ydoc-2.1.0b0}/jupyter_server_ydoc/test_utils.py +0 -0
- {jupyter_server_ydoc-2.0.2 → jupyter_server_ydoc-2.1.0b0}/jupyter_server_ydoc/websocketserver.py +0 -0
- {jupyter_server_ydoc-2.0.2 → jupyter_server_ydoc-2.1.0b0}/pyproject.toml +0 -0
- {jupyter_server_ydoc-2.0.2 → jupyter_server_ydoc-2.1.0b0}/setup.py +0 -0
- {jupyter_server_ydoc-2.0.2 → jupyter_server_ydoc-2.1.0b0}/tests/__init__.py +0 -0
- {jupyter_server_ydoc-2.0.2 → jupyter_server_ydoc-2.1.0b0}/tests/conftest.py +0 -0
- {jupyter_server_ydoc-2.0.2 → jupyter_server_ydoc-2.1.0b0}/tests/test_app.py +0 -0
- {jupyter_server_ydoc-2.0.2 → jupyter_server_ydoc-2.1.0b0}/tests/test_documents.py +0 -0
- {jupyter_server_ydoc-2.0.2 → jupyter_server_ydoc-2.1.0b0}/tests/test_handlers.py +0 -0
- {jupyter_server_ydoc-2.0.2 → jupyter_server_ydoc-2.1.0b0}/tests/test_loaders.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.1.0b0"
|
|
@@ -9,6 +9,7 @@ import uuid
|
|
|
9
9
|
from logging import Logger
|
|
10
10
|
from typing import Any
|
|
11
11
|
from uuid import uuid4
|
|
12
|
+
from typing import cast
|
|
12
13
|
|
|
13
14
|
from jupyter_server.auth import authorized
|
|
14
15
|
from jupyter_server.base.handlers import APIHandler, JupyterHandler
|
|
@@ -32,6 +33,8 @@ from .utils import (
|
|
|
32
33
|
room_id_from_encoded_path,
|
|
33
34
|
)
|
|
34
35
|
from .websocketserver import JupyterWebsocketServer, RoomNotFound
|
|
36
|
+
from .utils import MessageType
|
|
37
|
+
from pycrdt import Decoder
|
|
35
38
|
|
|
36
39
|
YFILE = YDOCS["file"]
|
|
37
40
|
|
|
@@ -291,6 +294,18 @@ class YDocWebSocketHandler(WebSocketHandler, JupyterHandler):
|
|
|
291
294
|
"""
|
|
292
295
|
On message receive.
|
|
293
296
|
"""
|
|
297
|
+
decoder = Decoder(message)
|
|
298
|
+
header = decoder.read_var_uint()
|
|
299
|
+
if header == MessageType.RAW:
|
|
300
|
+
msg = decoder.read_var_string()
|
|
301
|
+
if msg == "save":
|
|
302
|
+
try:
|
|
303
|
+
room = cast(DocumentRoom, self.room)
|
|
304
|
+
room._save_to_disc()
|
|
305
|
+
except Exception:
|
|
306
|
+
self.log.error("Couldn't save content from room: %s", self._room_id)
|
|
307
|
+
return
|
|
308
|
+
|
|
294
309
|
self._message_queue.put_nowait(message)
|
|
295
310
|
self._websocket_server.ypatch_nb += 1
|
|
296
311
|
|
{jupyter_server_ydoc-2.0.2 → jupyter_server_ydoc-2.1.0b0}/jupyter_server_ydoc/pytest_plugin.py
RENAMED
|
@@ -276,7 +276,7 @@ def rtc_create_mock_document_room():
|
|
|
276
276
|
last_modified: datetime | None = None,
|
|
277
277
|
save_delay: float | None = None,
|
|
278
278
|
store: SQLiteYStore | None = None,
|
|
279
|
-
writable: bool =
|
|
279
|
+
writable: bool = True,
|
|
280
280
|
) -> tuple[FakeContentsManager, FileLoader, DocumentRoom]:
|
|
281
281
|
paths = {id: path}
|
|
282
282
|
|
|
@@ -247,6 +247,33 @@ class DocumentRoom(YRoom):
|
|
|
247
247
|
document. This tasks are debounced (60 seconds by default) so we
|
|
248
248
|
need to cancel previous tasks before creating a new one.
|
|
249
249
|
"""
|
|
250
|
+
# Collect autosave values from all clients
|
|
251
|
+
autosave_states = [
|
|
252
|
+
state.get("autosave", True)
|
|
253
|
+
for state in self.awareness.states.values()
|
|
254
|
+
if state # skip empty states
|
|
255
|
+
]
|
|
256
|
+
|
|
257
|
+
# If no states exist (e.g., during tests), force autosave to be True
|
|
258
|
+
if not autosave_states:
|
|
259
|
+
autosave_states = [True]
|
|
260
|
+
|
|
261
|
+
# Enable autosave if at least one client has it turned on
|
|
262
|
+
autosave = any(autosave_states)
|
|
263
|
+
|
|
264
|
+
if not autosave:
|
|
265
|
+
return
|
|
266
|
+
if self._update_lock.locked():
|
|
267
|
+
return
|
|
268
|
+
|
|
269
|
+
self._saving_document = asyncio.create_task(
|
|
270
|
+
self._maybe_save_document(self._saving_document)
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
def _save_to_disc(self):
|
|
274
|
+
"""
|
|
275
|
+
Called when manual save is triggered. Helpful when autosave is turned off.
|
|
276
|
+
"""
|
|
250
277
|
if self._update_lock.locked():
|
|
251
278
|
return
|
|
252
279
|
|
|
@@ -265,7 +292,6 @@ class DocumentRoom(YRoom):
|
|
|
265
292
|
"""
|
|
266
293
|
if self._save_delay is None:
|
|
267
294
|
return
|
|
268
|
-
|
|
269
295
|
if saving_document is not None and not saving_document.done():
|
|
270
296
|
# the document is being saved, cancel that
|
|
271
297
|
saving_document.cancel()
|
|
@@ -51,9 +51,7 @@ async def test_defined_save_delay_should_save_content_after_document_change(
|
|
|
51
51
|
rtc_create_mock_document_room,
|
|
52
52
|
):
|
|
53
53
|
content = "test"
|
|
54
|
-
cm, _, room = rtc_create_mock_document_room(
|
|
55
|
-
"test-id", "test.txt", content, save_delay=0.01, writable=True
|
|
56
|
-
)
|
|
54
|
+
cm, _, room = rtc_create_mock_document_room("test-id", "test.txt", content, save_delay=0.01)
|
|
57
55
|
|
|
58
56
|
await room.initialize()
|
|
59
57
|
room._document.source = "Test 2"
|
|
@@ -79,6 +77,50 @@ async def test_undefined_save_delay_should_not_save_content_after_document_chang
|
|
|
79
77
|
assert "save" not in cm.actions
|
|
80
78
|
|
|
81
79
|
|
|
80
|
+
async def test_should_not_save_content_when_all_clients_have_autosave_disabled(
|
|
81
|
+
rtc_create_mock_document_room,
|
|
82
|
+
):
|
|
83
|
+
content = "test"
|
|
84
|
+
cm, _, room = rtc_create_mock_document_room("test-id", "test.txt", content, save_delay=0.01)
|
|
85
|
+
|
|
86
|
+
# Disable autosave for all existing clients
|
|
87
|
+
for state in room.awareness._states.values():
|
|
88
|
+
if state is not None:
|
|
89
|
+
state["autosave"] = False
|
|
90
|
+
|
|
91
|
+
# Inject a dummy client with autosave disabled
|
|
92
|
+
room.awareness._states[9999] = {"autosave": False}
|
|
93
|
+
|
|
94
|
+
await room.initialize()
|
|
95
|
+
room._document.source = "Test 2"
|
|
96
|
+
|
|
97
|
+
await asyncio.sleep(0.15)
|
|
98
|
+
|
|
99
|
+
assert "save" not in cm.actions
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
async def test_should_save_content_when_at_least_one_client_has_autosave_enabled(
|
|
103
|
+
rtc_create_mock_document_room,
|
|
104
|
+
):
|
|
105
|
+
content = "test"
|
|
106
|
+
cm, _, room = rtc_create_mock_document_room("test-id", "test.txt", content, save_delay=0.01)
|
|
107
|
+
|
|
108
|
+
# Disable autosave for all existing clients
|
|
109
|
+
for state in room.awareness._states.values():
|
|
110
|
+
if state is not None:
|
|
111
|
+
state["autosave"] = False
|
|
112
|
+
|
|
113
|
+
# Inject a dummy client with autosave enabled
|
|
114
|
+
room.awareness._states[10000] = {"autosave": True}
|
|
115
|
+
|
|
116
|
+
await room.initialize()
|
|
117
|
+
room._document.source = "Test 2"
|
|
118
|
+
|
|
119
|
+
await asyncio.sleep(0.15)
|
|
120
|
+
|
|
121
|
+
assert "save" in cm.actions
|
|
122
|
+
|
|
123
|
+
|
|
82
124
|
# The following test should be restored when package versions are fixed.
|
|
83
125
|
|
|
84
126
|
# async def test_document_path(rtc_create_mock_document_room):
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "2.0.2"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{jupyter_server_ydoc-2.0.2 → jupyter_server_ydoc-2.1.0b0}/jupyter-config/jupyter_server_ydoc.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{jupyter_server_ydoc-2.0.2 → jupyter_server_ydoc-2.1.0b0}/jupyter_server_ydoc/events/awareness.yaml
RENAMED
|
File without changes
|
{jupyter_server_ydoc-2.0.2 → jupyter_server_ydoc-2.1.0b0}/jupyter_server_ydoc/events/fork.yaml
RENAMED
|
File without changes
|
{jupyter_server_ydoc-2.0.2 → jupyter_server_ydoc-2.1.0b0}/jupyter_server_ydoc/events/session.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{jupyter_server_ydoc-2.0.2 → jupyter_server_ydoc-2.1.0b0}/jupyter_server_ydoc/websocketserver.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|