Skip to content

Commit 889e4cf

Browse files
jzhang20133Jialin Zhang
andauthored
Add support for global awareness (#63)
Co-authored-by: Jialin Zhang <[email protected]>
1 parent 804b52b commit 889e4cf

File tree

6 files changed

+54
-56
lines changed

6 files changed

+54
-56
lines changed

jupyter_rtc_core/app.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
from traitlets import Instance, Type
66
from .handlers import RouteHandler, FileIDIndexHandler
7-
from .websockets import GlobalAwarenessWebsocket, YRoomWebsocket
7+
from .websockets import YRoomWebsocket
88
from .rooms.yroom_manager import YRoomManager
99
from .outputs import OutputsManager, outputs_handlers
1010

@@ -17,8 +17,6 @@ class RtcExtensionApp(ExtensionApp):
1717
# dummy handler that verifies the server extension is installed;
1818
# # this can be deleted prior to initial release.
1919
(r"jupyter-rtc-core/get-example/?", RouteHandler),
20-
# global awareness websocket
21-
# (r"api/collaboration/room/JupyterLab:globalAwareness/?", GlobalAwarenessWebsocket),
2220
# # ydoc websocket
2321
(r"api/collaboration/room/(.*)", YRoomWebsocket),
2422
# # handler that just adds compatibility with Jupyter Collaboration's frontend

jupyter_rtc_core/rooms/yroom.py

Lines changed: 43 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class YRoom:
2929
room_id := "{file_type}:{file_format}:{file_id}"
3030
"""
3131

32-
_jupyter_ydoc: YBaseDoc
32+
_jupyter_ydoc: YBaseDoc | None
3333
"""JupyterYDoc"""
3434
_ydoc: pycrdt.Doc
3535
"""Ydoc"""
@@ -65,23 +65,34 @@ def __init__(
6565
self._client_group = YjsClientGroup(room_id=room_id, log=self.log, loop=self._loop)
6666
self._ydoc = pycrdt.Doc()
6767
self._awareness = pycrdt.Awareness(ydoc=self._ydoc)
68-
_, file_type, _ = self.room_id.split(":")
69-
JupyterYDocClass = cast(
70-
type[YBaseDoc],
71-
jupyter_ydoc_classes.get(file_type, jupyter_ydoc_classes["file"])
72-
)
73-
self._jupyter_ydoc = JupyterYDocClass(ydoc=self._ydoc, awareness=self._awareness)
74-
75-
# Initialize YRoomFileAPI and begin loading content
76-
self.file_api = YRoomFileAPI(
77-
room_id=self.room_id,
78-
jupyter_ydoc=self._jupyter_ydoc,
79-
log=self.log,
80-
loop=self._loop,
81-
fileid_manager=fileid_manager,
82-
contents_manager=contents_manager
83-
)
84-
self.file_api.load_ydoc_content()
68+
69+
# If this room is providing global awareness, set
70+
# `file_api` and `jupyter_ydoc` to `None` as this room
71+
# will never read/write via the `ContentsManager`.
72+
if self.room_id == "JupyterLab:globalAwareness":
73+
self.file_api = None
74+
self._jupyter_ydoc = None
75+
else:
76+
# Otherwise, initialize `jupyter_ydoc` and `file_api`
77+
_, file_type, _ = self.room_id.split(":")
78+
JupyterYDocClass = cast(
79+
type[YBaseDoc],
80+
jupyter_ydoc_classes.get(file_type, jupyter_ydoc_classes["file"])
81+
)
82+
self._jupyter_ydoc = JupyterYDocClass(ydoc=self._ydoc, awareness=self._awareness)
83+
84+
# Initialize YRoomFileAPI and begin loading content
85+
self.file_api = YRoomFileAPI(
86+
room_id=self.room_id,
87+
jupyter_ydoc=self._jupyter_ydoc,
88+
log=self.log,
89+
loop=self._loop,
90+
fileid_manager=fileid_manager,
91+
contents_manager=contents_manager
92+
)
93+
self.file_api.load_ydoc_content()
94+
self._jupyter_ydoc.observe(self._on_jupyter_ydoc_update)
95+
8596

8697
# Start observers on `self.ydoc` and `self.awareness` to ensure new
8798
# updates are broadcast to all clients and saved to disk.
@@ -91,7 +102,6 @@ def __init__(
91102
self._ydoc_subscription = self._ydoc.observe(
92103
self._on_ydoc_update
93104
)
94-
self._jupyter_ydoc.observe(self._on_jupyter_ydoc_update)
95105

96106
# Initialize message queue and start background task that routes new
97107
# messages in the message queue to the appropriate handler method.
@@ -118,7 +128,12 @@ async def get_jupyter_ydoc(self):
118128
(`jupyter_ydoc.ybasedoc.YBaseDoc`) after waiting for its content to be
119129
loaded from the ContentsManager.
120130
"""
121-
await self.file_api.ydoc_content_loaded
131+
if self.file_api:
132+
await self.file_api.ydoc_content_loaded
133+
if self.room_id == "JupyterLab:globalAwareness":
134+
message = "There is no Jupyter ydoc for global awareness scenario"
135+
self.log.error(message)
136+
raise Exception(message)
122137
return self._jupyter_ydoc
123138

124139

@@ -127,7 +142,8 @@ async def get_ydoc(self):
127142
Returns a reference to the room's YDoc (`pycrdt.Doc`) after
128143
waiting for its content to be loaded from the ContentsManager.
129144
"""
130-
await self.file_api.ydoc_content_loaded
145+
if self.file_api:
146+
await self.file_api.ydoc_content_loaded
131147
return self._ydoc
132148

133149

@@ -155,7 +171,8 @@ async def _process_message_queue(self) -> None:
155171
"""
156172
# Wait for content to be loaded before processing any messages in the
157173
# message queue
158-
await self.file_api.ydoc_content_loaded
174+
if self.file_api:
175+
await self.file_api.ydoc_content_loaded
159176

160177
# Begin processing messages from the message queue
161178
while True:
@@ -448,7 +465,8 @@ async def stop(self) -> None:
448465
# Remove all observers, as updates no longer need to be broadcast
449466
self._ydoc.unobserve(self._ydoc_subscription)
450467
self._awareness.unobserve(self._awareness_subscription)
451-
self._jupyter_ydoc.unobserve()
468+
if self._jupyter_ydoc:
469+
self._jupyter_ydoc.unobserve()
452470

453471
# Finish processing all messages, then stop the queue to end the
454472
# `_process_message_queue()` background task.
@@ -457,4 +475,5 @@ async def stop(self) -> None:
457475

458476
# Finally, stop FileAPI and return. This saves the final content of the
459477
# JupyterYDoc in the process.
460-
await self.file_api.stop()
478+
if self.file_api:
479+
await self.file_api.stop()
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
1-
from .global_awareness_ws import GlobalAwarenessWebsocket
21
from .clients import YjsClient, YjsClientGroup
32
from .yroom_ws import YRoomWebsocket

jupyter_rtc_core/websockets/global_awareness_ws.py

Lines changed: 0 additions & 4 deletions
This file was deleted.

jupyter_rtc_core/websockets/yroom_ws.py

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -40,24 +40,15 @@ def prepare(self):
4040
request_path: str = self.request.path
4141
self.room_id = request_path.strip("/").split("/")[-1]
4242

43-
# TODO: remove this once globalawareness is implemented
44-
if self.room_id == "JupyterLab:globalAwareness":
45-
self.close(1011)
46-
return
47-
4843
# Verify the file ID contained in the room ID points to a valid file.
49-
fileid = self.room_id.split(":")[-1]
50-
path = self.fileid_manager.get_path(fileid)
51-
if not path:
52-
raise HTTPError(404, f"No file with ID '{fileid}'.")
44+
if self.room_id != "JupyterLab:globalAwareness":
45+
fileid = self.room_id.split(":")[-1]
46+
path = self.fileid_manager.get_path(fileid)
47+
if not path:
48+
raise HTTPError(404, f"No file with ID '{fileid}'.")
5349

5450

5551
def open(self, *_, **__):
56-
# TODO: remove this later
57-
if self.room_id == "JupyterLab:globalAwareness":
58-
self.close(1011)
59-
return
60-
6152
# Create the YRoom
6253
yroom = self.yroom_manager.get_room(self.room_id)
6354
if not yroom:
@@ -74,10 +65,6 @@ def open(self, *_, **__):
7465

7566

7667
def on_message(self, message: bytes):
77-
# TODO: remove this later
78-
if self.room_id == "JupyterLab:globalAwareness":
79-
return
80-
8168
if not self.client_id:
8269
self.close(code=1001)
8370
return
@@ -87,10 +74,6 @@ def on_message(self, message: bytes):
8774

8875

8976
def on_close(self):
90-
# TODO: remove this later
91-
if self.room_id == "JupyterLab:globalAwareness":
92-
return
93-
9477
if self.client_id:
9578
self.log.info(f"Closed Websocket to client '{self.client_id}'.")
9679
self.yroom.clients.remove(self.client_id)

src/index.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ import { IAwareness } from '@jupyter/ydoc';
4646
import {
4747
AwarenessKernelStatus
4848
} from './kernelstatus';
49+
import { ServerConnection } from '@jupyterlab/services';
50+
import { WebSocketAwarenessProvider } from './docprovider/awareness';
51+
import { URLExt } from '@jupyterlab/coreutils';
4952
/**
5053
* Initialization data for the @jupyter/rtc-core extension.
5154
*/
@@ -102,15 +105,15 @@ export const rtcGlobalAwarenessPlugin: JupyterFrontEndPlugin<IAwareness> = {
102105
const awareness = new Awareness(ydoc);
103106

104107
// TODO: Uncomment once global awareness is working
105-
/*const server = ServerConnection.makeSettings();
108+
const server = ServerConnection.makeSettings();
106109
const url = URLExt.join(server.wsUrl, 'api/collaboration/room');
107110

108111
new WebSocketAwarenessProvider({
109112
url: url,
110113
roomID: 'JupyterLab:globalAwareness',
111114
awareness: awareness,
112115
user: user
113-
});*/
116+
});
114117

115118
state.changed.connect(async () => {
116119
const data: any = await state.toJSON();

0 commit comments

Comments
 (0)