Skip to content

Commit

Permalink
Support file types (#154)
Browse files Browse the repository at this point in the history
* Support file types

* Improves error handling
  • Loading branch information
hbcarlos committed May 30, 2023
1 parent 5891988 commit fbae4ff
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 25 deletions.
17 changes: 14 additions & 3 deletions jupyter_collaboration/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,14 +153,25 @@ async def open(self, room_id):
# Close the connection if the document session expired
session_id = self.get_query_argument("sessionId", "")
if SERVER_SESSION != session_id:
self.close(1003, f"Document session {session_id} expired")
self.close(
1003,
f"Document session {session_id} expired. You need to reload this browser tab.",
)

# cancel the deletion of the room if it was scheduled
if self.room.cleaner is not None:
self.room.cleaner.cancel()

# Initialize the room
await self.room.initialize()
try:
# Initialize the room
await self.room.initialize()
except Exception as e:
_, _, file_id = decode_file_path(self._room_id)
file = self._file_loaders[file_id]
self.log.error(f"Error initializing: {file.path}\n{e!r}", exc_info=e)
self.close(
1003, f"Error initializing: {file.path}. You need to close the document."
)

self._emit(LogLevel.INFO, "initialize", "New client connected.")

Expand Down
9 changes: 6 additions & 3 deletions jupyter_collaboration/loaders.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@
from jupyter_server.utils import ensure_async
from jupyter_server_fileid.manager import BaseFileIdManager


class OutOfBandChanges(Exception):
pass
from .utils import OutOfBandChanges


class FileLoader:
Expand Down Expand Up @@ -137,6 +135,11 @@ async def save_content(self, model: dict[str, Any]) -> dict[str, Any]:
"""
async with self._lock:
path = self.path
if model["type"] not in {"directory", "file", "notebook"}:
# fall back to file if unknown type, the content manager only knows
# how to handle these types
model["type"] = "file"

m = await ensure_async(
self._contents_manager.get(
path, format=model["format"], type=model["type"], content=False
Expand Down
29 changes: 24 additions & 5 deletions jupyter_collaboration/rooms.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
from ypy_websocket.websocket_server import YRoom
from ypy_websocket.ystore import BaseYStore, YDocNotFound

from .loaders import FileLoader, OutOfBandChanges
from .utils import JUPYTER_COLLABORATION_EVENTS_URI, LogLevel
from .loaders import FileLoader
from .utils import JUPYTER_COLLABORATION_EVENTS_URI, LogLevel, OutOfBandChanges

YFILE = YDOCS["file"]

Expand Down Expand Up @@ -94,6 +94,7 @@ async def initialize(self) -> None:
return

self.log.info("Initializing room %s", self._room_id)

model = await self._file.load_content(self._file_format, self._file_type, True)

async with self._update_lock:
Expand Down Expand Up @@ -187,11 +188,17 @@ async def _on_content_change(self, event: str, args: dict[str, Any]) -> None:
args (dict): A dictionary with format, type, last_modified.
"""
if event == "metadata" and self._last_modified < args["last_modified"]:
model = await self._file.load_content(self._file_format, self._file_type, True)

self.log.info("Out-of-band changes. Overwriting the content in room %s", self._room_id)
self._emit(LogLevel.INFO, "overwrite", "Out-of-band changes. Overwriting the room.")

try:
model = await self._file.load_content(self._file_format, self._file_type, True)
except Exception as e:
msg = f"Error loading content from file: {self._file.path}\n{e!r}"
self.log.error(msg, exc_info=e)
self._emit(LogLevel.ERROR, None, msg)
return None

async with self._update_lock:
self._document.source = model["content"]
self._last_modified = model["last_modified"]
Expand Down Expand Up @@ -255,14 +262,26 @@ async def _maybe_save_document(self) -> None:

except OutOfBandChanges:
self.log.info("Out-of-band changes. Overwriting the content in room %s", self._room_id)
model = await self._file.load_content(self._file_format, self._file_type, True)
try:
model = await self._file.load_content(self._file_format, self._file_type, True)
except Exception as e:
msg = f"Error loading content from file: {self._file.path}\n{e!r}"
self.log.error(msg, exc_info=e)
self._emit(LogLevel.ERROR, None, msg)
return None

async with self._update_lock:
self._document.source = model["content"]
self._last_modified = model["last_modified"]
self._document.dirty = False

self._emit(LogLevel.INFO, "overwrite", "Out-of-band changes while saving.")

except Exception as e:
msg = f"Error saving file: {self._file.path}\n{e!r}"
self.log.error(msg, exc_info=e)
self._emit(LogLevel.ERROR, None, msg)


class TransientRoom(YRoom):
"""A Y room for sharing state (e.g. awareness)."""
Expand Down
12 changes: 12 additions & 0 deletions jupyter_collaboration/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,18 @@ class LogLevel(Enum):
CRITICAL = "CRITICAL"


class OutOfBandChanges(Exception):
pass


class ReadError(Exception):
pass


class WriteError(Exception):
pass


def decode_file_path(path: str) -> Tuple[str, str, str]:
"""
Decodes a file path. The file path is composed by the format,
Expand Down
17 changes: 3 additions & 14 deletions packages/docprovider/src/yprovider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,19 +121,9 @@ export class WebSocketProvider implements IDocumentProvider {
if (event.code === 1003) {
console.error('Document provider closed:', event.reason);

showErrorMessage(
this._trans.__('Session expired'),
this._trans.__(
'The document session expired. You need to reload this browser tab.'
),
[Dialog.okButton({ label: this._trans.__('Reload') })]
)
.then((r: any) => {
if (r.button.accept) {
window.location.reload();
}
})
.catch(e => window.location.reload());
showErrorMessage(this._trans.__('Document session error'), event.reason, [
Dialog.okButton()
]);

// Dispose shared model immediately. Better break the document model,
// than overriding data on disk.
Expand All @@ -142,7 +132,6 @@ export class WebSocketProvider implements IDocumentProvider {
};

private _onSync = (isSynced: boolean) => {
console.log(`_onSync ${isSynced}`);
if (isSynced) {
this._ready.resolve();
this._yWebsocketProvider?.off('sync', this._onSync);
Expand Down

0 comments on commit fbae4ff

Please sign in to comment.