Update plot embedded in a notebook before the end of cell execution

Hi Bokeh Community!

I’m a very fresh bokeh user, and I try it within Jupyter notebook in which I’m not an expert neither.

First, a short description of what I would like to achieve. I look for an online visualization of the data produced while the notebook cell is being executed. So, I have prepared the following notebook which does what I need, almost pefectly. The only detail is, that the plot does not appear until the cell finished execution which is a must requirement in my case.

The script obtained from the Bokeh server is as following:

<script id="1441">
  var xhr = new XMLHttpRequest()
  xhr.responseType = 'blob';
  xhr.open('GET', "http://127.0.0.1:5002/autoload.js?bokeh-autoload-element=1441&bokeh-absolute-url=http://127.0.0.1:5002", true);
  
  xhr.onload = function (event) {
    var script = document.createElement('script'),
    src = URL.createObjectURL(event.target.response);
    script.src = src;
    document.body.appendChild(script);
  };
xhr.send();
</script>

When I try to display a static HTML there is no problem - it is displayed immediatelly.

I suspect that it may have something to do with the asnycio event loop which is both used by the Tornado server and the Jupyter, but I’m not an expert in the asynchronous programming.

Could someone more experienced gieve me a hint, please?

Right, if you have blocked the notebook event loop, then that’s the same loop that the Bokeh app will run on by default, so it will not render until the loop frees up. There’s nothing that can be done to change that. It might be possible to create a new separate event loop (in another thread) to pass to Server but I am not sure this would work—you’ll just have to try.

Alternatively, if you only need uni-directional updates to push from Python to the plot (and don’t need any “feedback” e.g. tool or widget events) then you might find push_notebook simpler and more suited to this use case.

Hi Bryan,

Many thanks for your quick answer! It already helped a lot.

In my case I would need “feedback” so I have discarded already the push_notebook mode.

I have tried what you suggested - running a separate tornado event loop on a thread. Since tornado v5 their loop is just a wrapper around the asyncio loop and asyncio does not allow to nest loops, so I had to use the nest_asyncio patch. I’ve learned all this from this jupyter project issue. I have pushed the changes to the earlier attached notebook example so you could take a look.

The result is what I was looking for - the plot is being updated while the cell is being executed.

Now, what I would like to do is to keep the plot ready for user interaction e.g. using zoom, pan, etc. after the cell execution. But it fails with strange errors (not always the same) when I start interacting with it e.g.

ERROR:bokeh.protocol:Bad header with no msgtype was: {'events': [{'kind': 'ModelChanged', 'model': {'id': '1006'}, 'attr': 'start', 'new': -0.18761586820851694}, {'kind': 'ModelChanged', 'model': {'id': '1006'}, 'attr': 'end', 'new': 2.2067174651248167}], 'references': []}
ERROR:bokeh.server.views.ws:Bokeh Server protocol error: No 'msgtype' in header, closing connection

Note, before leaving the cell I remove the periodic callback.

Now, I’m not sure what is happening, but this must be either of these three options:

  1. I do something wrong with the document so the plot is not ready for user interaction after the cell execution.
  2. I made some silly mistake when running the separate event loop for the Bokeh server.
  3. The idea of running the Bokeh server on a separate event loop has some limitations (you said you were not sure about this).

Regarding option 1 I doubt so, cause if I use the original code (without the saprate event loop), which does not update initially until the cell have finished, then I do not get these errors.

Many thanks for your help!

@reszelaz I am afraid I don’t have any specific thoughts offhand. That error message is happening at the lowest level of the Bokeh websocket transport, and indicates that a badly-formed protocol message fragment was received by BokehJS. I can’t think of anything that would cause that, much less anything about the event loop. It’s not really possible to speculate without actual investigation. If you have a complete, self-contained notebook that reproduces the situation that can be run without modification, I can take a quick look.

Hi Bryan,

Again, thanks for your message!

Below I describe how to reproduce it using conda environment:

conda create -n jup-bokeh python=3.7 jupyter bokeh
conda activate jup-bokeh
pip install nest_asyncio
jupyter notebook no_plot_until_end_of_cell.ipynb

no_plot_until_end_of_cell.ipynb is the same notebook as I posted in my first message, but with the necessary changes to run the bokeh server on a dedicated event loop.

As on today this installs:

  • bokeh 2.2.3
  • jupyter core 4.6.3
  • jupyter-notebook 6.1.4
  • ipykernel 5.3.4
  • tornado 6.0.4

When you execute cells of the notebook, after cell 3, when you use the “Wheel zoom” then the error is triggered. Fo example now I got another message:

ERROR:bokeh.server.views.ws:Handler or its work threw an exception: AssertionError(): Message 'PATCH-DOC' content: {'events': [{'kind': 'ModelChanged', 'model': {'id': '1006'}, 'attr': 'start', 'new': 0.10513336017804716}, {'kind': 'ModelChanged', 'model': {'id': '1006'}, 'attr': 'end', 'new': 1.9336328046224913}], 'references': []}
Traceback (most recent call last):
  File "/home/zreszela/miniconda/envs/jup-bokeh/lib/python3.7/site-packages/bokeh/server/views/ws.py", line 236, in on_message
    await self._schedule(work)
  File "/home/zreszela/miniconda/envs/jup-bokeh/lib/python3.7/site-packages/bokeh/server/views/ws.py", line 308, in _schedule
    await self.send_message(work)
  File "/home/zreszela/miniconda/envs/jup-bokeh/lib/python3.7/site-packages/bokeh/server/views/ws.py", line 263, in send_message
    await message.send(self)
  File "/home/zreszela/miniconda/envs/jup-bokeh/lib/python3.7/site-packages/bokeh/protocol/message.py", line 259, in send
    await conn.write_message(self.header_json, locked=False)
  File "/home/zreszela/miniconda/envs/jup-bokeh/lib/python3.7/site-packages/bokeh/server/views/ws.py", line 278, in write_message
    await super().write_message(message, binary)
  File "/home/zreszela/miniconda/envs/jup-bokeh/lib/python3.7/site-packages/tornado/websocket.py", line 342, in write_message
    return self.ws_connection.write_message(message, binary=binary)
  File "/home/zreszela/miniconda/envs/jup-bokeh/lib/python3.7/site-packages/tornado/websocket.py", line 1098, in write_message
    fut = self._write_frame(True, opcode, message, flags=flags)
  File "/home/zreszela/miniconda/envs/jup-bokeh/lib/python3.7/site-packages/tornado/websocket.py", line 1075, in _write_frame
    return self.stream.write(frame)
  File "/home/zreszela/miniconda/envs/jup-bokeh/lib/python3.7/site-packages/tornado/iostream.py", line 559, in write
    self._handle_write()
  File "/home/zreszela/miniconda/envs/jup-bokeh/lib/python3.7/site-packages/tornado/iostream.py", line 991, in _handle_write
    self._write_buffer.advance(num_bytes)
  File "/home/zreszela/miniconda/envs/jup-bokeh/lib/python3.7/site-packages/tornado/iostream.py", line 202, in advance
    assert 0 < size <= self._size
AssertionError
ERROR:bokeh.server.views.ws:Bokeh Server internal error: server failed to handle a message, closing connection

It would be great if you could try it as well.

Cheers!

Unfortunately @reszelaz your notebook works correctly for me without error, so I can’t really offer any suggestions :frowning_face: I am on OSX and here are my relevant package versions

bokeh                     2.2.3                      py_0    bokeh
ipykernel                 5.3.4            py38h5ca1d4c_0
ipython                   7.18.1           py38h5ca1d4c_0
ipython_genutils          0.2.0                    py38_0
jupyter_client            6.1.7                      py_0
jupyter_core              4.6.3                    py38_0
jupyterlab_pygments       0.1.2                      py_0
notebook                  6.1.4                    py38_0
python                    3.8.5                h26836e1_1
tornado                   6.0.4            py38h1de35cc_1 

I tried it with python 3.8 as you and I still get the errors (I use Linux).

In your tests, have you tried to use the “Zoom Wheel” after executing cell 3?. When I run the notebook without interacting with the plot I don’t have problems neither. The problems appear when after executing cell 3 I try to use zoom. Note, when I use zoom while the cell 3 is being executed, there are no problems.

Thanks!

Sorry I missed the “after cell finishes” part. Yes I can reproduce.

Bokeh protocol specifies expected message fragments in defined order: header, payload, buffers. Something is causing message fragments to arrive to the server in the wrong order. The server gets a new fragment expecting a header, but sees a payload fragment instead, and blows up.

I don’t have any concrete explanation, only a hunch that something about the multiple ioloops and multiple threads is allowing message fragments from different messages to become interleaved on the websocket, which is a catastrophic error condition.

At this point unfortunately my only real opinion is that that this usage is simply outside the envelope of intended, supported usage. I think your options are:

  • adapt the notebook to work for standard Bokeh usage and features
  • explore different tools besides Bokeh for meeting your needs
1 Like

Thanks for your help Bryan!
I will review if other projects could do what I need. If not I will come back to Bokeh and try to debug it deeper.