Skip to content
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

Hitting Command(Ctrl) + S on in an RTC notebook results in a "File Changed" dialog #312

Closed
ibdafna opened this issue May 9, 2024 · 4 comments · Fixed by #337
Closed
Labels
bug Something isn't working

Comments

@ibdafna
Copy link
Member

ibdafna commented May 9, 2024

See attached gif

rtc-ctrl-s

@ibdafna ibdafna added the bug Something isn't working label May 9, 2024
@andypfau
Copy link

andypfau commented May 20, 2024

Same here. I have a docker container (based on python:bullseye) with only Jupyter Lab (v. 4.2.0) and the jupyter-collaboration extension.

Observations:

  • You can either click "Revert" or "Overwrite", and it seems to work.
  • It is nevertheless annoying.
  • You may also close the editor, and rename it on the file browser, in which case it works as expected (no error dialog).
  • You may click "Do not ask me again", in which case you are in the future forced to rename it via the file browser.

One workaround seems to be to add this snippet to /usr/local/share/jupyter/lab/settings/overrides.json (see https://jupyterlab.readthedocs.io/en/latest/user/directories.html#jupyterlab-user-settings-directory), which essentially does the same as "do not ask me again". Users will be forced to rename using the file browser, which at least might be less confusing than seeing an error everytime they want to rename a new document:

{
    "@jupyterlab/docmanager-extension:plugin": {
        "renameUntitledFileOnSave": false
    }
}

However, that is just an ad-hoc workaround. A cleaner solution would be nice :-)

@krassowski
Copy link
Member

Without collaboration I see the following contents created on disk:

{
 "cells": [],
 "metadata": {},
 "nbformat": 4,
 "nbformat_minor": 5
}

which is stable.

With collaboration I see the same initially, but after a few seconds the disk content changes to:

{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "791ff2aa-e215-452e-b171-5e7d4d3902c8",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}

the inclusion of random cell id explains why the new hash appears to be random. In other words, collaboration extension updates the disk content in the background after creating a notebook, without refreshing the last held hash for document content.

@krassowski
Copy link
Member

Currently saving to disk happens in the background on the jupyter-server side via loader approximately once every second (document_save_delay). This means that the logic which updates the most recent contents model on frontend gets bypassed. Because the source of truth is in the server, it indeed should be the one which is saving the file.

Some possible solutions:

  • a) do not write the content to disk if just populating the notebook scaffold (cell ID, kernel name)
    • this only helps with the case of notebook being created, not if it gets modified by a collaborator
  • b) announce the new contents model hash to the frontend and make it save it as the previous contents model hash
  • c) disable hash checks altogether when using RTC; this still could be problematic if last_modified exhibits similar behaviour (or is it already handled?)

To me (b) appears the cleanest solution.

If we want the loader to update hash on save it could be either in the same way as the dirty flag is updated here:

await self._file.maybe_save_content(
{
"format": self._file_format,
"type": self._file_type,
"content": self._document.source,
}
)
async with self._update_lock:
self._document.dirty = False

or in the way last_modified is updated here:
async def _save_content(self, model: dict[str, Any], done_saving: asyncio.Event) -> None:
try:
m = await ensure_async(self._contents_manager.save(model, self.path))
self.last_modified = m["last_modified"]
finally:
done_saving.set()

@krassowski
Copy link
Member

Hi @ibdafna would you have time to help with review of #337 and jupyterlab/jupyterlab#16695?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants