-
Notifications
You must be signed in to change notification settings - Fork 14
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
Add awareness features to handle server state #170
Conversation
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 @brichet!
Now that pycrdt has an API reference documentation, all public classes and methods should have docstrings.
Also, we'll need 100% test coverage. You can check with:
coverage run -m pytest tests
coverage report --show-missing --fail-under=100
It should be OK now |
…ome features to it
for more information, see https://pre-commit.ci
Co-authored-by: David Brochart <[email protected]>
1b74f22
to
98540a5
Compare
@brichet I took the liberty to make some changes in 98540a5, please let me know if you agree with them. |
Thanks for the suggestion, I can take a look at it.. |
@davidbrochart I updated the class according to your suggestions, that should be closer to |
Thanks, do you mind if I take it from now? It will be quicker than requesting changes, and you will still be able to accept/reject my changes afterwards. |
e531376
to
982970a
Compare
4ad3ccb
to
c75c8f9
Compare
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 @davidbrochart.
Does it work on your side ? I have errors when I try to use it:
File "/home/brichet/projects/pycrdt/python/pycrdt/_awareness.py", line 136, in apply_awareness_update
state_str = decoder.read_var_string()
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/brichet/projects/pycrdt/python/pycrdt/_sync.py", line 227, in read_var_string
return message.decode("utf-8")
^^^^^^^^^^^^^^^^^^^^^^^
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc0 in position 0: invalid start byte
python/pycrdt/_awareness.py
Outdated
def _update_states( | ||
self, client_id: int, clock: int, state: Any, states_changes: dict[str, list[int]] | ||
) -> 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.
I think this function could be kept, using an origin
to know if we should remove the local state or not.
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.
But it's slightly different between setLocalState and applyAwarenessUpdate, right? In particular setLocalState
doesn't handle the clock.
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.
Not sure to understand "handle the clock". It does update it too, no ?
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.
But what about this, that _update_states()
was doing inconditionally?
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.
I agree that it was wrong in _update_states()
, it should also have tested that origin is not "local".
Anyway, the current implementation works, it was just to avoid duplicating code.
Do you have a reproducible example? |
Not really, I have an environment with dev install of pycrdt, pycrdt_websocket, jupyter_collaboration and jupyter_ydoc. All these packages need to be updated to test it properly. |
python/pycrdt/_awareness.py
Outdated
if state is None: | ||
del self._states[client_id] |
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 can probably raise an error if the local state has not been defined and we try to set it as 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.
Raising an exception is a feature IMO (rather than keep it silent), but maybe you mean that we should raise a more meaningful exception?
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.
I don't think we need to raise an exception, if we want to set it to null but it is already the case, it might be silent, like here
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.
But in set_local_state
we are responsible for what we do, in apply_awareness_update
less so. But I don't have a strong opinion on this, I will add the check back 👍
# Remote client removed the local state. Do not remove state. | ||
# Broadcast a message indicating that this client still exists by increasing | ||
# the clock. | ||
clock += 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.
Shouldn't it be
clock = curr_clock + 1
to update the local clock ?
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 mimics the JavaScript implementation. Should it be different?
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.
Maybe I misunderstood the clock, but I understood that each client has its own.
If this is the case, why should we rely on a value coming from a client to update the local clock ?
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.
Do you want to open an issue in https://github.com/yjs/y-protocols to discuss this, or maybe @dmonad can comment here?
python/pycrdt/_awareness.py
Outdated
elif client_meta is not None and state is None: | ||
removed.append(client_id) |
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 we not fallback here if a remote client try to remove the local state ?
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 the translation of this. Do you mean that there is an issue in the JavaScript implementation?
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.
Maybe...
We should test it, but it seems to me that if a remote client try to set the local awareness to null, this condition is fulfilled.
The client_meta
is the local meta (and may be not null), and the state
is the state to apply, which is null in that case.
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.
But a remote client cannot remove the local state, right?
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.
I agree with that, but it will send a wrong information to other client, that the local state has been removed, no ?
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.
I don't know, maybe open a PR in https://github.com/yjs/y-protocols? It should be fixed there too, and they will confirm if it's a bug.
@davidbrochart it seems that we do not take into account the full length of the message (first integer), when encoding and decoding. |
Good catch! |
An alternative would be to pass |
I can't see a use case where we need an 'incomplete' encoded awareness (without the length). Also the |
An update and a message are two different things, the latter encapsulates the former. For instance, Doc.get_update() is a document update that also has to be encapsulated in a message, using create_message(). |
4f721ab
to
b02eb3b
Compare
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.
I think it's good to go, any objection to merge @brichet?
Yes, it looks good to me too. |
Thanks @brichet. |
Thanks for helping a lot on it @davidbrochart. |
Released in v0.10.0. |
This PR adds the awareness class, extracted from https://github.com/jupyter-server/pycrdt-websocket.This PR adds some features to that awareness to allow the server to update and observe it.