bokeh / jupyter_bokeh

An extension for rendering Bokeh content in JupyterLab notebooks
BSD 3-Clause "New" or "Revised" License
249 stars 48 forks source link

`jupyter_bokeh` 3.0.6 (bokeh 3 and ipywidgets 8) #178

Closed mattpap closed 1 year ago

mattpap commented 1 year ago

This is a branch to prepare for 3.0.6 release, which is compatible with bokeh 3 and ipywidgets 8. I upgraded the build to webpack 5, as the previously used webpack 4 doesn't work with the current LTS of nodejs (18.x).

fixes #184

theeldermillenial commented 1 year ago

Is there an ETA on this? What remains to be done? Is there anything I can contribute?

theeldermillenial commented 1 year ago

Well, that was a quick turn around. Thanks!

I just installed and tried running it. In Jupyter, I get what looks like a recursion error, but the chart still renders. I'm running this in VSCode.

# test.ipynb

from math import pi

import ipywidgets as widgets
import pandas as pd

from bokeh.palettes import Category20c
from bokeh.plotting import figure
from bokeh.transform import cumsum
from bokeh.io import output_notebook
import jupyter_bokeh as jbk

output_notebook()

x = {
    'United States': 157,
    'United Kingdom': 93,
    'Japan': 89,
    'China': 63,
    'Germany': 44,
    'India': 42,
    'Italy': 40,
    'Australia': 35,
    'Brazil': 32,
    'France': 31,
    'Taiwan': 31,
    'Spain': 29
}

data = pd.Series(x).reset_index(name='value').rename(columns={'index': 'country'})
data['angle'] = data['value']/data['value'].sum() * 2*pi
data['color'] = Category20c[len(x)]

p = figure(height=350, title="Pie Chart", toolbar_location=None,
           tools="hover", tooltips="@country: @value", x_range=(-0.5, 1.0))

p.wedge(x=0, y=1, radius=0.4,
        start_angle=cumsum('angle', include_zero=True), end_angle=cumsum('angle'),
        line_color="white", fill_color='color', legend_field='country', source=data)

p.axis.axis_label = None
p.axis.visible = False
p.grid.grid_line_color = None

donut_label = widgets.Label("This is a donut chart")
donut_model = jbk.BokehModel(p)

out_donut = widgets.VBox([donut_label, donut_model])
out_donut.layout.height = "400px"

out_donut

Output: image

---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
File /.venv/lib/python3.10/site-packages/jupyter_bokeh/widgets.py:126, in BokehModel._sync_model(self, _, content, _buffers)
    123     return
    125 # Handle ModelChangedEvent
--> 126 new, old, attr = content["new"], content["old"], content["attr"]
    127 submodel = self._model.select_one({"id": content["id"]})
    128 descriptor = submodel.lookup(content['attr'])

KeyError: 'new'
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
File /.venv/lib/python3.10/site-packages/jupyter_bokeh/widgets.py:126, in BokehModel._sync_model(self, _, content, _buffers)
    123     return
    125 # Handle ModelChangedEvent
--> 126 new, old, attr = content["new"], content["old"], content["attr"]
    127 submodel = self._model.select_one({"id": content["id"]})
    128 descriptor = submodel.lookup(content['attr'])

KeyError: 'new'
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
File /.venv/lib/python3.10/site-packages/jupyter_bokeh/widgets.py:136, in BokehModel._sync_model(self, _, content, _buffers)
    134         cb(attr, old, new)
    135 elif kind == 'MessageSent':
--> 136     self._document.callbacks.trigger_json_event(content["msg_data"])

AttributeError: 'DocumentCallbackManager' object has no attribute 'trigger_json_event'

I think the best thing about this is that when I try to generate a static page with nbconvert, it looks like this newest version of your tool gets around a problem in the latest version of ipywidgets and nbconvert that had an error parsing these charts in JSON. I see the Bokeh toolbar pop up, but I do get a javascript error. I think I can debug this on my own though.

jupyter dejavu --execute --template=classic test.ipynb

Output: image

theeldermillenial commented 1 year ago

Looks like my nbconvert/dejavu error is more complicated than first expected. I thought it was a matter of adding script references, but that didn't seem to solve the problem. I was able to dump the same plot to an html file using Bokeh's file_html, but converting to html through nbconvert/dejavu does not work.

Should I open an issue or do you think this is related?

theeldermillenial commented 1 year ago

Okay, final comment. I tracked the javascript error down to @bokeh/jupyter_bokeh version 3.0.6 not on CDN yet (which makes sense). I replaced that and the javascript error went away.

However, what happens with nbconvert or dejavu is that the div gets URL encoded, and this causes it to get rendered weird.

When I changed the javascript module version 3.0.4 and changed nothing else, this is what I get: image

If you take a look at the <script type="application/vnd.jupyter.widget-state+json"> in the foot of the page, you find the div in the JSON:

&lt;div id=\\\"75ce238a-11c9-4b43-b21c-690899671cf6\\\" data-root-id=\\\"p1002\\\" style=\\\"display: contents;\\\"&gt;&lt;/div&gt;

If you decode that to:

<div id=\\\"75ce238a-11c9-4b43-b21c-690899671cf6\\\" data-root-id=\\\"p1002\\\" style=\\\"display: contents;\\\"></div>

Then the chart renders properly: image

I think this is something that's done by nbconvert/dejavu, so I don't know that there is any way of handling this. If you could confirm, I would be happy to update a couple of issues over on the nbconvert.

For now, looks like beautiful soup post processing to get Bokeh rendered on html generated by nbconvert. This is a step above what is possible for other plotting libraries though.

hoxbro commented 1 year ago

FYI getting this error running this branch in a notebook:

image

import bokeh
import jupyter_bokeh
from bokeh.models import Div
from jupyter_bokeh import BokehModel
bokeh.__version__, jupyter_bokeh.__version__
BokehModel(Div(text="example", styles={"background": "red"}))
bryevdv commented 1 year ago

cc @philippjfr @mattpap I believe https://github.com/bokeh/bokeh/pull/11960 and https://github.com/bokeh/bokeh/pull/12623 conspired to uncover this. It seems the jupyter extension should just be constructing a proper Event to call trigger_event with at this point.

philippjfr commented 1 year ago

Would be get to get this ready for release pretty soon. Happy to help if needed.

theeldermillenial commented 1 year ago

I am also patiently waiting for this to be released. I'm currently installing from the github branch.

mattpap commented 1 year ago

@philippjfr, @Hoxbro, this should be fully fixed now. I tested all combinations of triggering updates between bokeh <-> ipywidgets, also streaming and patching, and everything seems to work correctly now.

philippjfr commented 1 year ago

Thanks @mattpap!

theeldermillenial commented 1 year ago

Thanks @mattpap!