Bokeh show seems to interfere with display(Javascript(...)) in jupyter notebooks

Hi there, I’ve been investigating what seems to be a strange bug: immediately after running “Restart Kernel and Run All Cells…”, calls to display(Javascript(...)) in the call stack of show don’t work. However, they work before and after, and if I restart the kernel, wait a few seconds, then run all cells, display works as expected. Here’s a minimal example:

from bokeh.io import show, output_notebook
from bokeh.models import Div
from bokeh.layouts import column
from IPython.display import display, Javascript

output_notebook()


def bokeh_document(doc):
    template_html = f"""
        <p>test</p>
    """

    div = Div(text=template_html)

    layout = column(div)

    display(Javascript("alert('does this work 1')"))

    doc.add_root(layout)

    display(Javascript("alert('does this work 2')"))

    def bokeh_ready_callback(event):
        display(Javascript("alert('does this work 3')"))
        print("finished doc ready callback")
        
    from bokeh.models import CustomJS

    doc.on_event("document_ready", bokeh_ready_callback)
    
display(Javascript("alert('before')"))
show(bokeh_document)
display(Javascript("alert('after')"))

So the cases are:

  • Restart kernel and run all cells → only ‘before’, ‘after’ alerts are triggered.
  • Just run all cells → all alerts are triggered.

BTW, display without the javascript doesn’t work at all.

I can replicate this in both bokeh v2.4.3, and v3.3.4. FWIW, running javascript on “document_ready” with bokeh v3.3.4 via the js_on_event callback works. I’d like to understand what’s happening with display though, since it’s not completely straightforward to upgrade the app I’m embedding to be compatible with bokeh 3.3.4. Any suggestions?

Thanks for reading :slight_smile:

Jupyter package version:

Selected Jupyter core packages...
IPython          : 8.18.1
ipykernel        : 6.29.2
ipywidgets       : 8.1.2
jupyter_client   : 8.6.0
jupyter_core     : 5.7.1
jupyter_server   : 2.12.5
jupyterlab       : 4.1.2
nbclient         : 0.9.0
nbconvert        : 7.16.1
nbformat         : 5.9.2
notebook         : 7.1.0
qtconsole        : 5.5.1
traitlets        : 5.14.1

@Michael_Noronha Are you not using the jupyter_bokeh notebook extension? If not my first suggestion is to try things with the extension installed. Otherwise without it, loading BokehJS itself relies on running JS output cells, and in that case there are almost certainly unavoidable race conditions if cells are executed rapidly. [1] Using the extension should make sure BokehJS is already loaded and ready to go before anything else happens.


  1. There is no mechanism I am aware of that would allow us to make the next cell execution wait on JavaScript form the previous cell completing. ↩︎

Thanks for the suggestion! Does jupyter_bokeh integrate with notebooks in the browser, or only jupyterlab? I didn’t understand the README that well and tried installing it anyways, but not sure if it did nothing due to user error or it not being intended for classic notebooks.

Also, it seems like it might not be a race condition, though I could be wrong. I have an example to illustrate:

from bokeh.io import show, output_notebook
from bokeh.models import Div
from bokeh.layouts import column
from IPython.display import display, Javascript

output_notebook()


def bokeh_document(doc):
    template_html = f"""
        <p>test</p>
    """

    div = Div(text=template_html)

    layout = column(div)

    display(Javascript("alert('does this work 1')"))

    doc.add_root(layout)

    display("alert('does this work 2')")

    def bokeh_ready_callback(event):
        def cbk():
            print("periodic callback is running")
            display(Javascript("alert('does this work 3')"))

        doc.add_periodic_callback(cbk, period_milliseconds=5000), 

        print("finished doc ready callback")
        
    from bokeh.models import CustomJS

    doc.on_event("document_ready", bokeh_ready_callback)
    
display(Javascript("alert('before')"))
show(bokeh_document)
display(Javascript("alert('after')"))

If I restart and run all cells, the periodic callback runs (you can see it print to the cell output every 5 seconds), but the alert doesn’t trigger ever. If I run all cells, the alert triggers every 5 seconds as expected. Maybe there’s a race condition when some handle to the frontend is set up, and it’s re-used in subsequent calls from the callback?

Also, adding arbitrary delay between output_notebook and the rest of the cell doesn’t make a difference.

I am afraid I don’t have any other suggestions. All I can advise now is to file an issue on on the jupyter_bokeh repo with full details. Please note: there is no support at this point for any Bokeh 2.x versions, so please only report using the latest versions of everything. (We simply do not have the resources for anything beyond forward development plus point updates on one minor revision back.)

As for classic notebooks, I don’t actually know if jupyter_bokeh is expected to work with them at this point. I am not really a notebook user so I am not in that loop. I’d certainly say it’s worth checking with jupyter lab to see if it makes a difference.

Understood, thanks for your help. It’s definitely an edge-case. If I figure it out, I’ll report back :slight_smile: