Skip to content

Commit bccf8be

Browse files
committed
Log (instead of raise) exceptions when running as a server extension
1 parent 926c4d0 commit bccf8be

File tree

5 files changed

+42
-4
lines changed

5 files changed

+42
-4
lines changed

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
encode_file_path,
2323
room_id_from_encoded_path,
2424
)
25-
from .websocketserver import JupyterWebsocketServer, RoomNotFound
25+
from .websocketserver import JupyterWebsocketServer, RoomNotFound, exception_logger
2626

2727

2828
class YDocExtension(ExtensionApp):
@@ -107,6 +107,9 @@ def initialize_handlers(self):
107107
rooms_ready=False,
108108
auto_clean_rooms=False,
109109
ystore_class=self.ystore_class,
110+
# Log exceptions, because we don't want the websocket server
111+
# to _ever_ crash permanently in a live jupyter_server.
112+
exception_handler=exception_logger,
110113
log=self.log,
111114
)
112115

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,23 @@ async def prepare(self):
104104
self.log,
105105
self._document_save_delay,
106106
)
107+
108+
def exception_logger(exception: Exception, log) -> bool:
109+
"""A function that catches any exceptions raised in the websocket
110+
server and logs them.
111+
112+
The protects the websocket server's task group from cancelling
113+
anytime an exception is raised.
114+
"""
115+
room_id = "unknown"
116+
if self.room.room_id:
117+
room_id = self.room.room_id
118+
log.error(f"Document Room Exception, (room_id={room_id}: ", exc_info=exception)
119+
return True
120+
121+
# Logging exceptions, instead of raising them here to ensure
122+
# that the y-rooms stay alive even after an exception is seen.
123+
self.room.exception_handler = exception_logger
107124

108125
else:
109126
# TransientRoom

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ def __init__(
5252
self._document.observe(self._on_document_change)
5353
self._file.observe(self.room_id, self._on_outofband_change)
5454

55+
5556
@property
5657
def room_id(self) -> str:
5758
"""

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

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import asyncio
77
from logging import Logger
8-
from typing import Any
8+
from typing import Any, Callable
99

1010
from pycrdt_websocket.websocket_server import WebsocketServer, YRoom
1111
from pycrdt_websocket.ystore import BaseYStore
@@ -16,6 +16,17 @@ class RoomNotFound(LookupError):
1616
pass
1717

1818

19+
def exception_logger(exception: Exception, log: Logger) -> bool:
20+
"""A function that catches any exceptions raised in the websocket
21+
server and logs them.
22+
23+
The protects the websocket server's task group from cancelling
24+
anytime an exception is raised.
25+
"""
26+
log.error("Jupyter Websocket Server: ", exc_info=exception)
27+
return True
28+
29+
1930
class JupyterWebsocketServer(WebsocketServer):
2031
"""Ypy websocket server.
2132
@@ -30,9 +41,15 @@ def __init__(
3041
ystore_class: BaseYStore,
3142
rooms_ready: bool = True,
3243
auto_clean_rooms: bool = True,
44+
exception_handler: Callable[[Exception, Logger], bool] | None = None,
3345
log: Logger | None = None,
3446
):
35-
super().__init__(rooms_ready, auto_clean_rooms, log)
47+
super().__init__(
48+
rooms_ready=rooms_ready,
49+
auto_clean_rooms=auto_clean_rooms,
50+
exception_handler=exception_handler,
51+
log=log,
52+
)
3653
self.ystore_class = ystore_class
3754
self.ypatch_nb = 0
3855
self.connected_users: dict[Any, Any] = {}

projects/jupyter-server-ydoc/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ authors = [
3030
dependencies = [
3131
"jupyter_server>=2.4.0,<3.0.0",
3232
"jupyter_ydoc>=2.0.0,<3.0.0",
33-
"pycrdt-websocket>=0.13.0,<0.14.0",
33+
"pycrdt-websocket>=0.13.1,<0.14.0",
3434
"jupyter_events>=0.10.0",
3535
"jupyter_server_fileid>=0.7.0,<1",
3636
"jsonschema>=4.18.0"

0 commit comments

Comments
 (0)