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

Question: append_stdout and output widgets -- bad complexity? #2385

Closed
williamstein opened this issue Apr 19, 2019 · 11 comments
Closed

Question: append_stdout and output widgets -- bad complexity? #2385

williamstein opened this issue Apr 19, 2019 · 11 comments
Labels
resolved-locked Closed issues are locked after 30 days inactivity. Please open a new issue for related discussion.
Milestone

Comments

@williamstein
Copy link
Contributor

Suppose you run the following code:

from ipywidgets import *
out = Output()
display(out)
out.append_stdout("interactive")
out.append_stdout("jupyter")
out.append_stdout("widgets")

Then some of the IOPUB messages look like this:

{
  "header": {
    "version": "5.3",
    "date": "2019-04-19T01:36:30.879825Z",
    "session": "8a2252e5-74d113e744b54f28ccbc592d",
    "username": "user",
    "msg_type": "comm_msg",
    "msg_id": "0ddc9f49-f347f34c7748f96f50c579fe"
  },
  "parent_header": {
    "msg_id": "execute_6d9d00b1-1884-45b6-bfef-46e887c38200",
    "username": "",
    "session": "",
    "msg_type": "execute_request",
    "version": "5.3",
    "date": "2019-04-19T01:36:30.864853Z"
  },
  "metadata": {},
  "content": {
    "data": {
      "method": "update",
      "state": {
        "outputs": [
          { "output_type": "stream", "name": "stdout", "text": "interactive" }
        ]
      },
      "buffer_paths": []
    },
    "comm_id": "0aa12891adec40d78b6969443cbb4b0f"
  },
  "buffers": []
}

and

{
  "header": {
    "version": "5.3",
    "date": "2019-04-19T01:36:30.880616Z",
    "session": "8a2252e5-74d113e744b54f28ccbc592d",
    "username": "user",
    "msg_type": "comm_msg",
    "msg_id": "63613a13-a8d069e3f0df7bcff445a458"
  },
  "parent_header": {
    "msg_id": "execute_6d9d00b1-1884-45b6-bfef-46e887c38200",
    "username": "",
    "session": "",
    "msg_type": "execute_request",
    "version": "5.3",
    "date": "2019-04-19T01:36:30.864853Z"
  },
  "metadata": {},
  "content": {
    "data": {
      "method": "update",
      "state": {
        "outputs": [
          { "output_type": "stream", "name": "stdout", "text": "interactive" },
          { "output_type": "stream", "name": "stdout", "text": "jupyter" }
        ]
      },
      "buffer_paths": []
    },
    "comm_id": "0aa12891adec40d78b6969443cbb4b0f"
  },
  "buffers": []
}

and finally

{
  "header": {
    "version": "5.3",
    "date": "2019-04-19T01:36:30.881454Z",
    "session": "8a2252e5-74d113e744b54f28ccbc592d",
    "username": "user",
    "msg_type": "comm_msg",
    "msg_id": "72e74899-54b6747525980b54946907bf"
  },
  "parent_header": {
    "msg_id": "execute_6d9d00b1-1884-45b6-bfef-46e887c38200",
    "username": "",
    "session": "",
    "msg_type": "execute_request",
    "version": "5.3",
    "date": "2019-04-19T01:36:30.864853Z"
  },
  "metadata": {},
  "content": {
    "data": {
      "method": "update",
      "state": {
        "outputs": [
          { "output_type": "stream", "name": "stdout", "text": "interactive" },
          { "output_type": "stream", "name": "stdout", "text": "jupyter" },
          { "output_type": "stream", "name": "stdout", "text": "widgets" }
        ]
      },
      "buffer_paths": []
    },
    "comm_id": "0aa12891adec40d78b6969443cbb4b0f"
  },
  "buffers": []
}

QUESTION: Ok that works, but what if you did

out.append_stdout('something HUGE')
# some clears
out.append_stdout('something even more HUGE')
# etc.
...

as you could easily imagine someone doing. Isn't this going to blow up in your face? Am I missing something? Even if you do clears, every IOPUB message contains the full history.

Just curious.

@jasongrout
Copy link
Member

Isn't this going to blow up in your face?

Yes, quite possibly if you are doing something huge.

Am I missing something?

No.

Even if you do clears, every IOPUB message contains the full history.

This is a problem with the simple syncing protocol for this outputs array - we sync the full array every time. If you do a clear using the method on the widget, it should clear the array. If you do a clear using the clear_output() function, the clearing happens on the client and we run into issues with messages crossing between the client and kernel.

@jasongrout
Copy link
Member

I've thought a little about how to do proper append-only syncing for ipywidgets attributes (or in general some sort of diff-based syncing), which would also be useful for streaming sources like browser cameras or audio. I (and others) haven't thought about it enough to sit down and figure it out, though.

@jasongrout jasongrout added this to the Reference milestone Apr 19, 2019
@williamstein
Copy link
Contributor Author

I understand -- I just wanted to make sure I wasn't missing something in the protocol.

This is a problem with the simple syncing protocol for this outputs array - we sync the full array every time

In CoCalc I have the same problem with diffing arrays, which I haven't got around to adding to the protocol (I just record the whole array in the diff). However, I have much better diffing of Maps (just send the changed key/values), so I use maps (with "integer" keys) instead of arrays for output, thus avoiding the problem. The main thing you have to watch out for is that all map keys are actually strings in Javascript, and of course the JSON representation is a tiny bit less efficient.

If you do a clear using the method on the widget, it should clear the array. If you do a clear using the clear_output() function, the clearing happens on the client

That's useful. By "the widget" you mean the frontend widget in the browser? Basically, you're saying that there is some comm message the frontend widget can send the kernel, which will actually clear that array? And, if one calls clear_output() from Python, then the frontend client should always send that comm message back to the kernel? That makes sense, and I just need to figure out what that comm message actually is. Of course, for cocalc all do this back and forth entirely on the backend.

@maartenbreddels
Copy link
Member

Maybe this PR is useful:
voila-dashboards/voila#91
I had to 'fake' an Output widget at the nbconvert level.

@jasongrout
Copy link
Member

William, the front end should just sync an empty outputs array...that's the comm message.

@williamstein
Copy link
Contributor Author

williamstein commented Apr 19, 2019

Got it. So it sends a message updating the value to be empty. I'll try that. Oh, and @maartenbreddels code makes that very clear.

@jasongrout
Copy link
Member

jasongrout commented Apr 19, 2019

Yes. Here is the code in the classic notebook output widget doing that:

that.listenTo(that, 'clear_output', function(msg) {
that.output_area.handle_clear_output(msg);
that.set('outputs', [], {newMessage: true});
that.save_changes();

@maartenbreddels
Copy link
Member

Note that code is actually wrong I believe, assuming https://github.com/jupyter-widgets/ipywidgets/pull/2351/files is correct, it does not respect wait=True.

A similar fix was done for nbconvert here: https://github.com/jupyter/nbconvert/pull/969/files

Hope that helps to clarify things.

@williamstein
Copy link
Contributor Author

Thanks for the all the help everybody. With that, it was pretty easy to implement clear_output, and here's what I came up with for CoCalc:

sagemathinc/cocalc@e3a28d1

@jasongrout
Copy link
Member

Closing as answered.

@jasongrout
Copy link
Member

CC also the other output widget discussion from William, which contains a few more insights into the output widget design decisions: #2377

@lock lock bot added the resolved-locked Closed issues are locked after 30 days inactivity. Please open a new issue for related discussion. label May 20, 2020
@lock lock bot locked as resolved and limited conversation to collaborators May 20, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
resolved-locked Closed issues are locked after 30 days inactivity. Please open a new issue for related discussion.
Projects
None yet
Development

No branches or pull requests

3 participants