-
-
Notifications
You must be signed in to change notification settings - Fork 32
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
adding awareness event when open and close websockets #246
Conversation
Thanks for submitting your first pull request! You are awesome! 🤗 |
jupyter_collaboration/handlers.py
Outdated
@@ -284,6 +286,8 @@ def on_close(self) -> None: | |||
# keep the document for a while in case someone reconnects | |||
self.log.info("Cleaning room: %s", self._room_id) | |||
self.room.cleaner = asyncio.create_task(self._clean_room()) | |||
if isinstance(self.room, DocumentRoom): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not emitting the event for all room types?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For YdocWebsocketHandler that handle global awareness websocket, its room type will be TransientRoom. I found its room id is fixed, called "JupyterLab:globalAwareness" and its creating and closing does not indicate any user joining or leaving the collaboration on a document. Hence we only emit when a websocket close where its room type is DocumentRoom.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Global awareness being a TransientRoom
doesn't mean that clients leaving a document in a TransientRoom
should not emit an event. Maybe the event should not be emitted specifically when the room ID is JupyterLab:globalAwareness
? This room ID is a bit unfortunate, as it should probably be a standard in the Jupyter ecosystem and not particularly for JupyterLab, so a better would have been Jupyter:globalAwareness
.
@Zsailer do you think this room ID should be specified somewhere in the Jupyter schemas, since it's probably a convention we want to rely on?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We would need to rely on RoomId to identity which document that the host has left from and close all the guest websockets associated as clients in that DocumentRoom. For a TransientRoom it seems that it is not tied to a specific document and mainly used by global awareness. We only want to emit an event when open and on_close has been successful as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All I'm saying is that you could do something like that:
if isinstance(self.room, DocumentRoom): | |
if self.room.room_id != "JupyterLab:globalAwareness": |
A TransientRoom
is not just for global awareness, it could be for any other collaborative document that doesn't need to be persisted, and you probably want to emit join/leave events for them too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure.
jupyter_collaboration/handlers.py
Outdated
@@ -297,6 +301,13 @@ def _emit(self, level: LogLevel, action: str | None = None, msg: str | None = No | |||
|
|||
self.event_logger.emit(schema_id=JUPYTER_COLLABORATION_EVENTS_URI, data=data) | |||
|
|||
def _emit_awareness_event(self, user: str, action: str, msg: str | None = None) -> None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rename user
to username
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will address
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jzhang20133 Thank you opening this PR! I left a comment below, but David Brochart's review covers the areas of feedback blocking this PR from being merged. 😁
All I would like to add is that it's possible to emit events through a decorator interface. Emitting events imperatively (as done in this PR) is less preferable, since it's really easy for a developer to accidentally delete the line emitting the event. Using a decorator also allows for this feature to be added without any changes to the method definition, which I take as a sign of clean code.
To implement a decorator interface, you would want to define an @emit_awareness_event
decorator that looks something like:
def emit_awareness_event(action: str, message: Optional[str] = None):
def decorator(method):
async def method_stub(self, *args, **kwargs):
return_value = await method(self, *args, **kwargs)
data = { "action": action, ... }
self.event_logger.emit(..., data=data)
return return_value
return method_stub
return decorator
After doing so, the method declarations would look like:
@emit_awareness_event("join")
async def open(self, room_id):
...
@emit_awareness_event("leave")
def on_close(self) -> None:
...
Let me know if this all sounds like a good idea for future work. If so, I can track this in a new issue so as to not block this PR. Thanks! 🤗
jupyter_collaboration/handlers.py
Outdated
@@ -297,6 +301,13 @@ def _emit(self, level: LogLevel, action: str | None = None, msg: str | None = No | |||
|
|||
self.event_logger.emit(schema_id=JUPYTER_COLLABORATION_EVENTS_URI, data=data) | |||
|
|||
def _emit_awareness_event(self, user: str, action: str, msg: str | None = None) -> None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(no action needed) I think we should prefer declaring the type of msg
as Optional[str]
instead of str | None
, since the vertical bar syntax for type unions is only available for Python 3.10+. I thought the convention in Jupyter packages was to support every currently maintained version of Python, the lowest being Python 3.8 at present.
However, I also noticed that the rest of the source code uses the str | None
. I've opened an issue so we can discuss this as a team. No action needed on this PR, just a comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's fine to use str | None
. Jupyter collaboration supports Python 3.8 and above. Union types as X | Y
are allowed for python<3.10 with:
from __future__ import annotations
jupyter_collaboration/handlers.py
Outdated
@@ -284,6 +286,8 @@ def on_close(self) -> None: | |||
# keep the document for a while in case someone reconnects | |||
self.log.info("Cleaning room: %s", self._room_id) | |||
self.room.cleaner = asyncio.create_task(self._clean_room()) | |||
if isinstance(self.room, DocumentRoom): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1
I really love this idea! I just tried it out. In this use case, we only emit when websocket serving a DocumentRoom is successfully opened and closed and in open(self, room_id) method, when initialize room fails, it handles certain type exception by closing websocket with error message and swallows other type of exceptions. And for that failure cases, we won't emit this event. We would need to change original logic in open method in order to leverage decorator which defeat the purpose. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @jzhang20133, I think this is good to go, but it would be great if you could add a test.
@davidbrochart Thank you for reviewing the PR. I have added a test to cover emit awareness event. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good, @jzhang20133!
I think we need to set the minimum version of Jupyter Events to the latest release, v0.10.0.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @jzhang20133.
@jzhang20133 Test |
@davidbrochart fixed here: #258 |
Adding awareness event when open and close websockets