Skip to content

Commit b68d3cd

Browse files
authored
Initialize and update the ydoc path property (#342)
* Initialize and update the ydoc path's property when it change * Update the path only instead of loading the document * linting and typing * Add test * Remove the test on path until package versions are fixed * linting
1 parent 0751fa1 commit b68d3cd

File tree

4 files changed

+56
-3
lines changed

4 files changed

+56
-3
lines changed

projects/jupyter-server-ydoc/jupyter_server_ydoc/loaders.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,11 @@ def __init__(
3939

4040
self._log = log or getLogger(__name__)
4141
self._subscriptions: dict[str, Callable[[], Coroutine[Any, Any, None]]] = {}
42+
self._filepath_subscriptions: dict[str, Callable[[], Coroutine[Any, Any, None] | None]] = {}
4243

4344
self._watcher = asyncio.create_task(self._watch_file()) if self._poll_interval else None
4445
self.last_modified = None
46+
self._current_path = self.path
4547

4648
@property
4749
def file_id(self) -> str:
@@ -79,7 +81,12 @@ async def clean(self) -> None:
7981
except asyncio.CancelledError:
8082
self._log.info(f"file watcher for '{self.file_id}' is cancelled now")
8183

82-
def observe(self, id: str, callback: Callable[[], Coroutine[Any, Any, None]]) -> None:
84+
def observe(
85+
self,
86+
id: str,
87+
callback: Callable[[], Coroutine[Any, Any, None]],
88+
filepath_callback: Callable[[], Coroutine[Any, Any, None] | None] | None = None,
89+
) -> None:
8390
"""
8491
Subscribe to the file to get notified about out-of-band file changes.
8592
@@ -88,6 +95,8 @@ def observe(self, id: str, callback: Callable[[], Coroutine[Any, Any, None]]) ->
8895
callback (Callable): Callback for notifying the room.
8996
"""
9097
self._subscriptions[id] = callback
98+
if filepath_callback is not None:
99+
self._filepath_subscriptions[id] = filepath_callback
91100

92101
def unobserve(self, id: str) -> None:
93102
"""
@@ -97,6 +106,8 @@ def unobserve(self, id: str) -> None:
97106
id (str): Room ID
98107
"""
99108
del self._subscriptions[id]
109+
if id in self._filepath_subscriptions.keys():
110+
del self._filepath_subscriptions[id]
100111

101112
async def load_content(self, format: str, file_type: str) -> dict[str, Any]:
102113
"""
@@ -204,15 +215,26 @@ async def maybe_notify(self) -> None:
204215
Notifies subscribed rooms about out-of-band file changes.
205216
"""
206217
do_notify = False
218+
filepath_change = False
207219
async with self._lock:
220+
path = self.path
221+
if self._current_path != path:
222+
self._current_path = path
223+
filepath_change = True
224+
208225
# Get model metadata; format and type are not need
209-
model = await ensure_async(self._contents_manager.get(self.path, content=False))
226+
model = await ensure_async(self._contents_manager.get(path, content=False))
210227

211228
if self.last_modified is not None and self.last_modified < model["last_modified"]:
212229
do_notify = True
213230

214231
self.last_modified = model["last_modified"]
215232

233+
if filepath_change:
234+
# Notify filepath change
235+
for callback in self._filepath_subscriptions.values():
236+
await ensure_async(callback())
237+
216238
if do_notify:
217239
# Notify out-of-band change
218240
# callbacks will load the file content, thus release the lock before calling them

projects/jupyter-server-ydoc/jupyter_server_ydoc/rooms.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ def __init__(
4242
self._file_type: str = file_type
4343
self._file: FileLoader = file
4444
self._document = YDOCS.get(self._file_type, YFILE)(self.ydoc)
45+
self._document.path = self._file.path
4546

4647
self._logger = logger
4748
self._save_delay = save_delay
@@ -54,7 +55,7 @@ def __init__(
5455

5556
# Listen for document changes
5657
self._document.observe(self._on_document_change)
57-
self._file.observe(self.room_id, self._on_outofband_change)
58+
self._file.observe(self.room_id, self._on_outofband_change, self._on_filepath_change)
5859

5960
@property
6061
def file_format(self) -> str:
@@ -225,6 +226,12 @@ async def _on_outofband_change(self) -> None:
225226
self._document.source = model["content"]
226227
self._document.dirty = False
227228

229+
def _on_filepath_change(self) -> None:
230+
"""
231+
Update the document path property.
232+
"""
233+
self._document.path = self._file.path
234+
228235
def _on_document_change(self, target: str, event: Any) -> None:
229236
"""
230237
Called when the shared document changes.

projects/jupyter-server-ydoc/jupyter_server_ydoc/test_utils.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ def __init__(self, mapping: dict):
1616
def get_path(self, id: str) -> str:
1717
return self.mapping[id]
1818

19+
def move(self, id: str, new_path: str) -> None:
20+
self.mapping[id] = new_path
21+
1922

2023
class FakeContentsManager:
2124
def __init__(self, model: dict):

tests/test_rooms.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,24 @@ async def test_undefined_save_delay_should_not_save_content_after_document_chang
7575
await asyncio.sleep(0.15)
7676

7777
assert "save" not in cm.actions
78+
79+
80+
# The following test should be restored when package versions are fixed.
81+
82+
# async def test_document_path(rtc_create_mock_document_room):
83+
# id = "test-id"
84+
# path = "test.txt"
85+
# new_path = "test2.txt"
86+
87+
# _, loader, room = rtc_create_mock_document_room(id, path, "")
88+
89+
# await room.initialize()
90+
# assert room._document.path == path
91+
92+
# # Update the path
93+
# loader._file_id_manager.move(id, new_path)
94+
95+
# # Wait for a bit more than the poll_interval
96+
# await asyncio.sleep(0.15)
97+
98+
# assert room._document.path == new_path

0 commit comments

Comments
 (0)