jupyter-server-ydoc 2.0.0rc0__py3-none-any.whl → 2.0.2__py3-none-any.whl
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/_version.py +1 -1
- jupyter_server_ydoc/app.py +2 -2
- jupyter_server_ydoc/handlers.py +17 -38
- jupyter_server_ydoc/loaders.py +19 -1
- jupyter_server_ydoc/pytest_plugin.py +11 -3
- jupyter_server_ydoc/rooms.py +5 -5
- jupyter_server_ydoc/websocketserver.py +5 -4
- {jupyter_server_ydoc-2.0.0rc0.dist-info → jupyter_server_ydoc-2.0.2.dist-info}/METADATA +1 -1
- jupyter_server_ydoc-2.0.2.dist-info/RECORD +19 -0
- jupyter_server_ydoc-2.0.0rc0.dist-info/RECORD +0 -19
- {jupyter_server_ydoc-2.0.0rc0.data → jupyter_server_ydoc-2.0.2.data}/data/etc/jupyter/jupyter_server_config.d/jupyter_collaboration.json +0 -0
- {jupyter_server_ydoc-2.0.0rc0.dist-info → jupyter_server_ydoc-2.0.2.dist-info}/WHEEL +0 -0
- {jupyter_server_ydoc-2.0.0rc0.dist-info → jupyter_server_ydoc-2.0.2.dist-info}/licenses/LICENSE +0 -0
jupyter_server_ydoc/_version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "2.0.
|
|
1
|
+
__version__ = "2.0.2"
|
jupyter_server_ydoc/app.py
CHANGED
|
@@ -105,7 +105,7 @@ class YDocExtension(ExtensionApp):
|
|
|
105
105
|
page_config.setdefault("serverSideExecution", self.server_side_execution)
|
|
106
106
|
|
|
107
107
|
# Set configurable parameters to YStore class
|
|
108
|
-
ystore_class = partial(self.ystore_class, config=self.config)
|
|
108
|
+
ystore_class: type[BaseYStore] = partial(self.ystore_class, config=self.config) # type:ignore[assignment]
|
|
109
109
|
|
|
110
110
|
self.ywebsocket_server = JupyterWebsocketServer(
|
|
111
111
|
rooms_ready=False,
|
|
@@ -205,7 +205,7 @@ class YDocExtension(ExtensionApp):
|
|
|
205
205
|
if copy:
|
|
206
206
|
update = room.ydoc.get_update()
|
|
207
207
|
|
|
208
|
-
fork_ydoc = Doc()
|
|
208
|
+
fork_ydoc: Doc = Doc()
|
|
209
209
|
fork_ydoc.apply_update(update)
|
|
210
210
|
|
|
211
211
|
return YDOCS.get(room.file_type, YDOCS["file"])(fork_ydoc)
|
jupyter_server_ydoc/handlers.py
CHANGED
|
@@ -5,18 +5,17 @@ from __future__ import annotations
|
|
|
5
5
|
|
|
6
6
|
import asyncio
|
|
7
7
|
import json
|
|
8
|
-
import time
|
|
9
8
|
import uuid
|
|
10
9
|
from logging import Logger
|
|
11
|
-
from typing import Any
|
|
10
|
+
from typing import Any
|
|
12
11
|
from uuid import uuid4
|
|
13
12
|
|
|
14
13
|
from jupyter_server.auth import authorized
|
|
15
14
|
from jupyter_server.base.handlers import APIHandler, JupyterHandler
|
|
16
15
|
from jupyter_server.utils import ensure_async
|
|
17
16
|
from jupyter_ydoc import ydocs as YDOCS
|
|
18
|
-
from pycrdt import Doc, UndoManager
|
|
19
|
-
from pycrdt_websocket.
|
|
17
|
+
from pycrdt import Doc, UndoManager
|
|
18
|
+
from pycrdt_websocket.yroom import YRoom
|
|
20
19
|
from pycrdt_websocket.ystore import BaseYStore
|
|
21
20
|
from tornado import web
|
|
22
21
|
from tornado.websocket import WebSocketHandler
|
|
@@ -28,7 +27,6 @@ from .utils import (
|
|
|
28
27
|
JUPYTER_COLLABORATION_EVENTS_URI,
|
|
29
28
|
JUPYTER_COLLABORATION_FORK_EVENTS_URI,
|
|
30
29
|
LogLevel,
|
|
31
|
-
MessageType,
|
|
32
30
|
decode_file_path,
|
|
33
31
|
encode_file_path,
|
|
34
32
|
room_id_from_encoded_path,
|
|
@@ -117,7 +115,10 @@ class YDocWebSocketHandler(WebSocketHandler, JupyterHandler):
|
|
|
117
115
|
|
|
118
116
|
file = self._file_loaders[file_id]
|
|
119
117
|
updates_file_path = f".{file_type}:{file_id}.y"
|
|
120
|
-
ystore = self._ystore_class(
|
|
118
|
+
ystore = self._ystore_class(
|
|
119
|
+
path=updates_file_path,
|
|
120
|
+
log=self.log, # type:ignore[call-arg]
|
|
121
|
+
)
|
|
121
122
|
self.room = DocumentRoom(
|
|
122
123
|
self._room_id,
|
|
123
124
|
file_format,
|
|
@@ -182,7 +183,7 @@ class YDocWebSocketHandler(WebSocketHandler, JupyterHandler):
|
|
|
182
183
|
self._websocket_server = ywebsocket_server
|
|
183
184
|
self._message_queue = asyncio.Queue()
|
|
184
185
|
self._room_id = ""
|
|
185
|
-
self.room = None
|
|
186
|
+
self.room = None # type:ignore
|
|
186
187
|
|
|
187
188
|
@property
|
|
188
189
|
def path(self):
|
|
@@ -219,7 +220,7 @@ class YDocWebSocketHandler(WebSocketHandler, JupyterHandler):
|
|
|
219
220
|
raise web.HTTPError(403)
|
|
220
221
|
return await super().get(*args, **kwargs)
|
|
221
222
|
|
|
222
|
-
async def open(self, room_id):
|
|
223
|
+
async def open(self, room_id: str) -> None: # type:ignore[override]
|
|
223
224
|
"""
|
|
224
225
|
On connection open.
|
|
225
226
|
"""
|
|
@@ -259,7 +260,7 @@ class YDocWebSocketHandler(WebSocketHandler, JupyterHandler):
|
|
|
259
260
|
)
|
|
260
261
|
|
|
261
262
|
# Clean up the room and delete the file loader
|
|
262
|
-
if len(self.room.clients) == 0 or self.room.clients ==
|
|
263
|
+
if len(self.room.clients) == 0 or self.room.clients == {self}:
|
|
263
264
|
self._message_queue.put_nowait(b"")
|
|
264
265
|
self._cleanup_delay = 0
|
|
265
266
|
await self._clean_room()
|
|
@@ -290,28 +291,6 @@ class YDocWebSocketHandler(WebSocketHandler, JupyterHandler):
|
|
|
290
291
|
"""
|
|
291
292
|
On message receive.
|
|
292
293
|
"""
|
|
293
|
-
message_type = message[0]
|
|
294
|
-
|
|
295
|
-
if message_type == MessageType.CHAT:
|
|
296
|
-
msg = message[2:].decode("utf-8")
|
|
297
|
-
|
|
298
|
-
user = self.current_user
|
|
299
|
-
data = json.dumps(
|
|
300
|
-
{
|
|
301
|
-
"sender": user.username,
|
|
302
|
-
"timestamp": time.time(),
|
|
303
|
-
"content": json.loads(msg),
|
|
304
|
-
}
|
|
305
|
-
).encode("utf8")
|
|
306
|
-
|
|
307
|
-
for client in self.room.clients:
|
|
308
|
-
if client != self:
|
|
309
|
-
task = asyncio.create_task(
|
|
310
|
-
client.send(bytes([MessageType.CHAT]) + write_var_uint(len(data)) + data)
|
|
311
|
-
)
|
|
312
|
-
self._websocket_server.background_tasks.add(task)
|
|
313
|
-
task.add_done_callback(self._websocket_server.background_tasks.discard)
|
|
314
|
-
|
|
315
294
|
self._message_queue.put_nowait(message)
|
|
316
295
|
self._websocket_server.ypatch_nb += 1
|
|
317
296
|
|
|
@@ -321,7 +300,7 @@ class YDocWebSocketHandler(WebSocketHandler, JupyterHandler):
|
|
|
321
300
|
"""
|
|
322
301
|
# stop serving this client
|
|
323
302
|
self._message_queue.put_nowait(b"")
|
|
324
|
-
if isinstance(self.room, DocumentRoom) and self.room.clients ==
|
|
303
|
+
if isinstance(self.room, DocumentRoom) and self.room.clients == {self}:
|
|
325
304
|
# no client in this room after we disconnect
|
|
326
305
|
# keep the document for a while in case someone reconnects
|
|
327
306
|
self.log.info("Cleaning room: %s", self._room_id)
|
|
@@ -386,9 +365,7 @@ class YDocWebSocketHandler(WebSocketHandler, JupyterHandler):
|
|
|
386
365
|
self._emit(LogLevel.INFO, "clean", "Loader deleted.")
|
|
387
366
|
del self._room_locks[self._room_id]
|
|
388
367
|
|
|
389
|
-
def _on_global_awareness_event(
|
|
390
|
-
self, topic: Literal["change", "update"], changes: tuple[dict[str, Any], Any]
|
|
391
|
-
) -> None:
|
|
368
|
+
def _on_global_awareness_event(self, topic: str, changes: tuple[dict[str, Any], Any]) -> None:
|
|
392
369
|
"""
|
|
393
370
|
Update the users when the global awareness changes.
|
|
394
371
|
|
|
@@ -489,7 +466,7 @@ class TimelineHandler(APIHandler):
|
|
|
489
466
|
try:
|
|
490
467
|
room_id = room_id_from_encoded_path(encoded_path)
|
|
491
468
|
room: YRoom = await self.ywebsocket_server.get_room(room_id)
|
|
492
|
-
fork_ydoc = Doc()
|
|
469
|
+
fork_ydoc: Doc = Doc()
|
|
493
470
|
|
|
494
471
|
ydoc_factory = YDOCS.get(content_type)
|
|
495
472
|
if ydoc_factory is None:
|
|
@@ -505,7 +482,9 @@ class TimelineHandler(APIHandler):
|
|
|
505
482
|
FORK_DOCUMENTS[idx] = ydoc_factory(fork_ydoc)
|
|
506
483
|
undo_manager: UndoManager = FORK_DOCUMENTS[idx].undo_manager
|
|
507
484
|
|
|
508
|
-
|
|
485
|
+
ystore = room.ystore
|
|
486
|
+
assert ystore
|
|
487
|
+
updates_and_timestamps = [(item[0], item[-1]) async for item in ystore.read()]
|
|
509
488
|
|
|
510
489
|
result_timestamps = []
|
|
511
490
|
|
|
@@ -649,7 +628,7 @@ class DocForkHandler(APIHandler):
|
|
|
649
628
|
return self.finish({"code": 404, "error": "Root room not found"})
|
|
650
629
|
|
|
651
630
|
update = root_room.ydoc.get_update()
|
|
652
|
-
fork_ydoc = Doc()
|
|
631
|
+
fork_ydoc: Doc = Doc()
|
|
653
632
|
fork_ydoc.apply_update(update)
|
|
654
633
|
model = self.get_json_body()
|
|
655
634
|
synchronize = model.get("synchronize", False)
|
jupyter_server_ydoc/loaders.py
CHANGED
|
@@ -153,6 +153,9 @@ class FileLoader:
|
|
|
153
153
|
path, format=model["format"], type=model["type"], content=False
|
|
154
154
|
)
|
|
155
155
|
)
|
|
156
|
+
# Skip saving if file is not writable
|
|
157
|
+
if not m["writable"]:
|
|
158
|
+
return None
|
|
156
159
|
|
|
157
160
|
if self.last_modified == m["last_modified"]:
|
|
158
161
|
self._log.info("Saving file: %s", path)
|
|
@@ -200,13 +203,28 @@ class FileLoader:
|
|
|
200
203
|
if self._poll_interval is None:
|
|
201
204
|
return
|
|
202
205
|
|
|
206
|
+
consecutive_error_logs = 0
|
|
207
|
+
max_consecutive_logs = 3
|
|
208
|
+
suppression_logged = False
|
|
209
|
+
|
|
203
210
|
while True:
|
|
204
211
|
try:
|
|
205
212
|
await asyncio.sleep(self._poll_interval)
|
|
206
213
|
try:
|
|
207
214
|
await self.maybe_notify()
|
|
215
|
+
consecutive_error_logs = 0
|
|
216
|
+
suppression_logged = False
|
|
208
217
|
except Exception as e:
|
|
209
|
-
|
|
218
|
+
if consecutive_error_logs < max_consecutive_logs:
|
|
219
|
+
self._log.error(f"Error watching file: {self.path}\n{e!r}", exc_info=e)
|
|
220
|
+
consecutive_error_logs += 1
|
|
221
|
+
elif not suppression_logged:
|
|
222
|
+
self._log.warning(
|
|
223
|
+
"Too many errors while watching %s — suppressing further logs.",
|
|
224
|
+
self.path,
|
|
225
|
+
)
|
|
226
|
+
suppression_logged = True
|
|
227
|
+
|
|
210
228
|
except asyncio.CancelledError:
|
|
211
229
|
break
|
|
212
230
|
|
|
@@ -240,7 +240,12 @@ def rtc_add_doc_to_store(rtc_connect_doc_client):
|
|
|
240
240
|
|
|
241
241
|
def rtc_create_SQLite_store_factory(jp_serverapp):
|
|
242
242
|
async def _inner(type: str, path: str, content: str) -> DocumentRoom:
|
|
243
|
-
db = SQLiteYStore(
|
|
243
|
+
db = SQLiteYStore(
|
|
244
|
+
path=f"{type}:{path}",
|
|
245
|
+
# `SQLiteYStore` here is a subclass of booth `LoggingConfigurable`
|
|
246
|
+
# and `pycrdt_websocket.ystore.SQLiteYStore`, but mypy gets lost:
|
|
247
|
+
config=jp_serverapp.config, # type:ignore[call-arg]
|
|
248
|
+
)
|
|
244
249
|
_ = create_task(db.start())
|
|
245
250
|
await db.started.wait()
|
|
246
251
|
|
|
@@ -271,13 +276,16 @@ def rtc_create_mock_document_room():
|
|
|
271
276
|
last_modified: datetime | None = None,
|
|
272
277
|
save_delay: float | None = None,
|
|
273
278
|
store: SQLiteYStore | None = None,
|
|
279
|
+
writable: bool = False,
|
|
274
280
|
) -> tuple[FakeContentsManager, FileLoader, DocumentRoom]:
|
|
275
281
|
paths = {id: path}
|
|
276
282
|
|
|
277
283
|
if last_modified is None:
|
|
278
|
-
cm = FakeContentsManager({"content": content})
|
|
284
|
+
cm = FakeContentsManager({"content": content, "writable": writable})
|
|
279
285
|
else:
|
|
280
|
-
cm = FakeContentsManager(
|
|
286
|
+
cm = FakeContentsManager(
|
|
287
|
+
{"last_modified": datetime.now(), "content": content, "writable": writable}
|
|
288
|
+
)
|
|
281
289
|
|
|
282
290
|
loader = FileLoader(
|
|
283
291
|
id,
|
jupyter_server_ydoc/rooms.py
CHANGED
|
@@ -9,7 +9,7 @@ from typing import Any, Callable
|
|
|
9
9
|
|
|
10
10
|
from jupyter_events import EventLogger
|
|
11
11
|
from jupyter_ydoc import ydocs as YDOCS
|
|
12
|
-
from pycrdt_websocket.
|
|
12
|
+
from pycrdt_websocket.yroom import YRoom
|
|
13
13
|
from pycrdt_websocket.ystore import BaseYStore, YDocNotFound
|
|
14
14
|
|
|
15
15
|
from .loaders import FileLoader
|
|
@@ -103,7 +103,7 @@ class DocumentRoom(YRoom):
|
|
|
103
103
|
It is important to set the ready property in the parent class (`self.ready = True`),
|
|
104
104
|
this setter will subscribe for updates on the shared document.
|
|
105
105
|
"""
|
|
106
|
-
if self.ready:
|
|
106
|
+
if self.ready:
|
|
107
107
|
return
|
|
108
108
|
|
|
109
109
|
self.log.info("Initializing room %s", self._room_id)
|
|
@@ -285,9 +285,9 @@ class DocumentRoom(YRoom):
|
|
|
285
285
|
"content": self._document.source,
|
|
286
286
|
}
|
|
287
287
|
)
|
|
288
|
-
|
|
289
|
-
self.
|
|
290
|
-
|
|
288
|
+
if saved_model:
|
|
289
|
+
async with self._update_lock:
|
|
290
|
+
self._document.dirty = False
|
|
291
291
|
self._document.hash = saved_model["hash"]
|
|
292
292
|
|
|
293
293
|
self._emit(LogLevel.INFO, "save", "Content saved.")
|
|
@@ -7,9 +7,10 @@ import asyncio
|
|
|
7
7
|
from logging import Logger
|
|
8
8
|
from typing import Any, Callable
|
|
9
9
|
|
|
10
|
-
from pycrdt_websocket.
|
|
10
|
+
from pycrdt_websocket.websocket import Websocket
|
|
11
|
+
from pycrdt_websocket.websocket_server import WebsocketServer
|
|
12
|
+
from pycrdt_websocket.yroom import YRoom
|
|
11
13
|
from pycrdt_websocket.ystore import BaseYStore
|
|
12
|
-
from tornado.websocket import WebSocketHandler
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
class RoomNotFound(LookupError):
|
|
@@ -38,7 +39,7 @@ class JupyterWebsocketServer(WebsocketServer):
|
|
|
38
39
|
|
|
39
40
|
def __init__(
|
|
40
41
|
self,
|
|
41
|
-
ystore_class: BaseYStore,
|
|
42
|
+
ystore_class: type[BaseYStore],
|
|
42
43
|
rooms_ready: bool = True,
|
|
43
44
|
auto_clean_rooms: bool = True,
|
|
44
45
|
exception_handler: Callable[[Exception, Logger], bool] | None = None,
|
|
@@ -132,7 +133,7 @@ class JupyterWebsocketServer(WebsocketServer):
|
|
|
132
133
|
await self.start_room(room)
|
|
133
134
|
return room
|
|
134
135
|
|
|
135
|
-
async def serve(self, websocket:
|
|
136
|
+
async def serve(self, websocket: Websocket) -> None:
|
|
136
137
|
# start monitoring here as the event loop is not yet available when initializing the object
|
|
137
138
|
if self.monitor_task is None:
|
|
138
139
|
self.monitor_task = asyncio.create_task(self._monitor())
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
jupyter_server_ydoc/__init__.py,sha256=B8H7XLhzgrTCQD8304Lx91FYXslwabsnV9OuYu4M4Hw,346
|
|
2
|
+
jupyter_server_ydoc/_version.py,sha256=tATvJM5shAzfspHYjdVwpV2w3-gDA119NlEYi5X2lFY,22
|
|
3
|
+
jupyter_server_ydoc/app.py,sha256=6Zca0acaEFCKyUbXdmcPmVk1Dgu91Y-DFjYR16WKFlg,8161
|
|
4
|
+
jupyter_server_ydoc/handlers.py,sha256=Prsi_IlJa9wl9VszRr-2qKZ8z224u-o7lRYI5SNJBaE,27276
|
|
5
|
+
jupyter_server_ydoc/loaders.py,sha256=XUQqg2EbfQUYlQVjHY183gYKeVZ6x92VHy4EsOQz4fA,11303
|
|
6
|
+
jupyter_server_ydoc/pytest_plugin.py,sha256=MfFqL5xJLGeIuLG8GF1kr2TumRjIIQfxoeNLGjvQjY8,8742
|
|
7
|
+
jupyter_server_ydoc/rooms.py,sha256=u4umGLsudteR6oJvw4pcszzrZTDgzOZkVXZEOfBPk8M,12221
|
|
8
|
+
jupyter_server_ydoc/stores.py,sha256=_5J6eNs3R5Tv88PCc-GGuszxQstfvNoBCYABqzBzJXA,1004
|
|
9
|
+
jupyter_server_ydoc/test_utils.py,sha256=utUwB5FThc_SCQshhUbLNih9GUa5qBcmMgU6-jx0ZnA,2275
|
|
10
|
+
jupyter_server_ydoc/utils.py,sha256=EgKC15js8VOS8-5jGMs4pfHQfV9drnNT2Gew5UlyXZc,2171
|
|
11
|
+
jupyter_server_ydoc/websocketserver.py,sha256=h1yTgJcsCK17_97Ne5x-lbgIFsxylwnltxagcuAlTJY,5185
|
|
12
|
+
jupyter_server_ydoc/events/awareness.yaml,sha256=2FrCci5rZIaU4rn8pIPZJkd132YAZdzKjSNSwjOY7Dk,755
|
|
13
|
+
jupyter_server_ydoc/events/fork.yaml,sha256=3OrhQjhVyLjlBJWMiffbnZodL3GzFafLwEmSBFrK33o,1303
|
|
14
|
+
jupyter_server_ydoc/events/session.yaml,sha256=PS0MxowpRwY5QFYm-LJvHUxKHnsictV8_6VEwfhYxcQ,1596
|
|
15
|
+
jupyter_server_ydoc-2.0.2.data/data/etc/jupyter/jupyter_server_config.d/jupyter_collaboration.json,sha256=0thh2hJUxAKkZSmneJMG0U6QJRjdM6zGlwrTedEt-Jk,94
|
|
16
|
+
jupyter_server_ydoc-2.0.2.dist-info/METADATA,sha256=Wcc5iSM2ZdOp74R__1pRnFDoPYvNK0lm-y9WkQJjBX0,5115
|
|
17
|
+
jupyter_server_ydoc-2.0.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
18
|
+
jupyter_server_ydoc-2.0.2.dist-info/licenses/LICENSE,sha256=mhO0ZW9EiWOPg0dUgB-lNbJ0CGwRmTdbeAg_se1SOnY,2833
|
|
19
|
+
jupyter_server_ydoc-2.0.2.dist-info/RECORD,,
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
jupyter_server_ydoc/__init__.py,sha256=B8H7XLhzgrTCQD8304Lx91FYXslwabsnV9OuYu4M4Hw,346
|
|
2
|
-
jupyter_server_ydoc/_version.py,sha256=Cs4kVkiMsFMyfJdp74Cseb_7_XMravPFYhL_jneOp-w,25
|
|
3
|
-
jupyter_server_ydoc/app.py,sha256=JqkpijoPdo_qum0CKqbG-I6W8fpC3-v2cFA3kFxK3mg,8111
|
|
4
|
-
jupyter_server_ydoc/handlers.py,sha256=Q0A638Ke_TR-hSdAgyz8u1gIOCk3FIqWRhV-QH2PLMM,27977
|
|
5
|
-
jupyter_server_ydoc/loaders.py,sha256=TijilImdgYk9K91cXEIP_DzkOr6phSddwQFpLI5l_RA,10564
|
|
6
|
-
jupyter_server_ydoc/pytest_plugin.py,sha256=1Y-iNZnEyhajx4HU-40aZ9iRVWcC5ikC5Y8JJHCH0So,8419
|
|
7
|
-
jupyter_server_ydoc/rooms.py,sha256=szOAfMldhQIrmVpqoF75O0_KXY54X_TrzJz6vpjR6kE,12254
|
|
8
|
-
jupyter_server_ydoc/stores.py,sha256=_5J6eNs3R5Tv88PCc-GGuszxQstfvNoBCYABqzBzJXA,1004
|
|
9
|
-
jupyter_server_ydoc/test_utils.py,sha256=utUwB5FThc_SCQshhUbLNih9GUa5qBcmMgU6-jx0ZnA,2275
|
|
10
|
-
jupyter_server_ydoc/utils.py,sha256=EgKC15js8VOS8-5jGMs4pfHQfV9drnNT2Gew5UlyXZc,2171
|
|
11
|
-
jupyter_server_ydoc/websocketserver.py,sha256=7fLPJcWczD-4R_-LXtfvNxM_pUXFasZWDmT4RIrOQHE,5150
|
|
12
|
-
jupyter_server_ydoc/events/awareness.yaml,sha256=2FrCci5rZIaU4rn8pIPZJkd132YAZdzKjSNSwjOY7Dk,755
|
|
13
|
-
jupyter_server_ydoc/events/fork.yaml,sha256=3OrhQjhVyLjlBJWMiffbnZodL3GzFafLwEmSBFrK33o,1303
|
|
14
|
-
jupyter_server_ydoc/events/session.yaml,sha256=PS0MxowpRwY5QFYm-LJvHUxKHnsictV8_6VEwfhYxcQ,1596
|
|
15
|
-
jupyter_server_ydoc-2.0.0rc0.data/data/etc/jupyter/jupyter_server_config.d/jupyter_collaboration.json,sha256=0thh2hJUxAKkZSmneJMG0U6QJRjdM6zGlwrTedEt-Jk,94
|
|
16
|
-
jupyter_server_ydoc-2.0.0rc0.dist-info/METADATA,sha256=HUXA92OIcOMQmhX9TRjJaYL65EWpUlHJbCS2GUK-ufE,5118
|
|
17
|
-
jupyter_server_ydoc-2.0.0rc0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
18
|
-
jupyter_server_ydoc-2.0.0rc0.dist-info/licenses/LICENSE,sha256=mhO0ZW9EiWOPg0dUgB-lNbJ0CGwRmTdbeAg_se1SOnY,2833
|
|
19
|
-
jupyter_server_ydoc-2.0.0rc0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
{jupyter_server_ydoc-2.0.0rc0.dist-info → jupyter_server_ydoc-2.0.2.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|