vega / altair

Declarative statistical visualization library for Python
https://altair-viz.github.io/
BSD 3-Clause "New" or "Revised" License
9.21k stars 783 forks source link

Support for JupyterLite is missing for `JupyterChart` component #3409

Open mattijn opened 4 months ago

mattijn commented 4 months ago

What happened?

I tried to run the following code in JupyterLite, https://jupyter.org/try-jupyter/lab/:

import micropip
await micropip.install(['altair', 'anywidget'])
import altair as alt
import pandas as pd

source = pd.DataFrame({
    'a': ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I'],
    'b': [28, 55, 43, 91, 81, 53, 19, 87, 52]
})

chart = alt.Chart(source).mark_bar().encode(
    x='a',
    y='b'
)

jchart = alt.JupyterChart(chart)
jchart

But it returns the following error:

[Open Browser Console for more detailed log - Double click to close this message]
Failed to load model class 'AnyModel' from module 'anywidget'
Error: No version of module anywidget is registered
    at f.loadClass (https://jupyter.org/try-jupyter/extensions/@jupyter-widgets/jupyterlab-manager/static/134.fe2572ece3b7955c89bb.js?v=fe2572ece3b7955c89bb:1:74933)
    at f.loadModelClass (https://jupyter.org/try-jupyter/extensions/@jupyter-widgets/jupyterlab-manager/static/336.0a90bd910629a565bb7e.js?v=0a90bd910629a565bb7e:1:10728)
    at f._make_model (https://jupyter.org/try-jupyter/extensions/@jupyter-widgets/jupyterlab-manager/static/336.0a90bd910629a565bb7e.js?v=0a90bd910629a565bb7e:1:7516)
    at f.new_model (https://jupyter.org/try-jupyter/extensions/@jupyter-widgets/jupyterlab-manager/static/336.0a90bd910629a565bb7e.js?v=0a90bd910629a565bb7e:1:5136)
    at f.handle_comm_open (https://jupyter.org/try-jupyter/extensions/@jupyter-widgets/jupyterlab-manager/static/336.0a90bd910629a565bb7e.js?v=0a90bd910629a565bb7e:1:3893)
    at _handleCommOpen (https://jupyter.org/try-jupyter/extensions/@jupyter-widgets/jupyterlab-manager/static/134.fe2572ece3b7955c89bb.js?v=fe2572ece3b7955c89bb:1:73470)
    at C._handleCommOpen (https://jupyter.org/try-jupyter/build/jlab_core.cb9a852.js:1:1067392)
    at async C._handleMessage (https://jupyter.org/try-jupyter/build/jlab_core.cb9a852.js:1:1069286)

I can't remember if this is broken now or if it was always like this.

What would you like to happen instead?

The same as what is being shown here: https://altair-viz.github.io/user_guide/jupyter_chart.html#basic-usage

Which version of Altair are you using?

5.3.0

jonmmease commented 4 months ago

Huh, I had never tried JupyerChart in JupyterLite, so I'm not sure whether this is a regression.

@manzt, would you expect an AnyWidget to work in JupyterLite?

manzt commented 4 months ago

Hi there, this is a fundamental challenge with traditional Jupyter widgets (which anywidget is). In order to use a widget, the JS code must discoverable in the Jupyter environment which running the Jupyter server. In your case, JupyterLite was compiled with one version of anywidget, and then micropip.install installs a never version.

Since there is a version mismatch between the Python anywidget version (user Python env) and the JS anywidget version (env running Jupyter server), this opaque error appears. I've been scratching my head on this for a bit because it is by far the most frustrating thing for end users, but is so fundamentally tied to a design decision to have widget JS assets in Jupyter discoverable as extensions rather than the userland kernel. For Google Colab and VS Code, the anywidget JS is loaded from a CDN. I personally think this should also be the case in JupyterLite. However, a better solution would be to allow widget code to be discovered from the kernel.

TL;DR

await micropip.install(["altair", "anywidget==<whatever-versionn-jupyterlite-is-using>"])

as a workaround, but this is something I'm hoping to address maybe at the SciPy sprints this summer.

mattijn commented 4 months ago

Thanks @manzt! I understand this can be frustrating. Is there anything we can do within the JupyerChart js code? We already have a showError function (here) that returns the javascript error as a readable error, without a cell crash.

We might be able to extend this to assist users with a readable code explaining the context around these anywidget-jupyterlite peculiarities (less is better, but nothing wrong with it for the time being!).

So if I understand right, in JupyterLite if we get this:

If I run %pip install anywidget=0.9.2 all is working fine within https://jtp.io/anywidget-lite/lab/index.html, because JupyterLite was compiled with version 0.9.2 and the user installs 0.9.2.

manzt commented 4 months ago

Is there anything we can do within the JupyerChart js code? We already have a showError function (here) that returns the javascript error as a readable error, without a cell crash.

Unfortunately, not that I'm aware of. This error comes up when either anywidget isn't installed in the jupyter env or there is a version mismatch. Either way, anywidget never gets a chance to run any code, and therefore the JupyterChart ESM can't execute anything else (since anywidget is responsible for loading and executing it).

jonmmease commented 4 months ago

Thanks for the context @manzt. We always appreciate it!