Skip to content

Commit 724c01c

Browse files
maartenbreddelsSAKSHI-DOT693claude
committed
fix: support Altair 6+ Vega-Lite MIME types in FigureAltair
Altair 6 introduces a new MIME type format (application/vnd.vegalite.v6.json) that differs from v4/v5 (+json suffix). Update the MIME type detection to find any vegalite MIME type instead of hardcoding specific versions. Based on the issue identified in #1135. Co-Authored-By: SAKSHI-DOT693 <[email protected]> Co-Authored-By: Claude Opus 4.5 <[email protected]>
1 parent 84a539b commit 724c01c

File tree

2 files changed

+84
-5
lines changed

2 files changed

+84
-5
lines changed

solara/components/figure_altair.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,15 @@ def FigureAltair(
2525

2626
with alt.renderers.enable("mimetype"):
2727
bundle = chart._repr_mimebundle_()[0]
28-
key4 = "application/vnd.vegalite.v4+json"
29-
key5 = "application/vnd.vegalite.v5+json"
30-
if key4 not in bundle and key5 not in bundle:
31-
raise KeyError(f"{key4} and {key5} not in mimebundle:\n\n{bundle}")
32-
spec = bundle.get(key5, bundle.get(key4))
28+
# Find the Vega-Lite spec from the bundle (supports v4, v5, v6+)
29+
# MIME type format varies: v4/v5 use "+json" suffix, v6 uses ".json"
30+
spec = None
31+
for key in bundle:
32+
if key.startswith("application/vnd.vegalite.v"):
33+
spec = bundle[key]
34+
break
35+
if spec is None:
36+
raise KeyError(f"No Vega-Lite MIME type found in mimebundle: {list(bundle.keys())}")
3337
return solara.widgets.VegaLite.element(
3438
spec=spec, on_click=on_click, listen_to_click=on_click is not None, on_hover=on_hover, listen_to_hover=on_hover is not None
3539
)

tests/unit/figure_altair_test.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import unittest.mock
2+
3+
import pytest
4+
5+
import solara
6+
import solara.widgets
7+
8+
9+
class MockChart:
10+
"""Mock Altair chart that returns a configurable mimebundle."""
11+
12+
def __init__(self, bundle):
13+
self._bundle = bundle
14+
15+
def _repr_mimebundle_(self):
16+
return (self._bundle, {})
17+
18+
19+
@pytest.fixture
20+
def mock_altair_renderers():
21+
"""Mock altair.renderers.enable to be a no-op context manager."""
22+
import contextlib
23+
24+
import altair as alt
25+
26+
@contextlib.contextmanager
27+
def mock_enable(renderer):
28+
yield
29+
30+
with unittest.mock.patch.object(alt.renderers, "enable", mock_enable):
31+
yield
32+
33+
34+
def test_figure_altair_v4(mock_altair_renderers):
35+
spec = {"$schema": "https://vega.github.io/schema/vega-lite/v4.json", "data": {"values": []}}
36+
chart = MockChart({"application/vnd.vegalite.v4+json": spec})
37+
38+
el = solara.FigureAltair(chart)
39+
_, rc = solara.render(el, handle_error=False)
40+
widget = rc.find(solara.widgets.VegaLite).widget
41+
assert widget.spec == spec
42+
rc.close()
43+
44+
45+
def test_figure_altair_v5(mock_altair_renderers):
46+
spec = {"$schema": "https://vega.github.io/schema/vega-lite/v5.json", "data": {"values": []}}
47+
chart = MockChart({"application/vnd.vegalite.v5+json": spec})
48+
49+
el = solara.FigureAltair(chart)
50+
_, rc = solara.render(el, handle_error=False)
51+
widget = rc.find(solara.widgets.VegaLite).widget
52+
assert widget.spec == spec
53+
rc.close()
54+
55+
56+
def test_figure_altair_v6(mock_altair_renderers):
57+
"""Test that Altair 6+ with v6 MIME type works (uses .json suffix instead of +json)."""
58+
spec = {"$schema": "https://vega.github.io/schema/vega-lite/v6.json", "data": {"values": []}}
59+
# Altair 6 uses ".json" suffix instead of "+json"
60+
chart = MockChart({"application/vnd.vegalite.v6.json": spec})
61+
62+
el = solara.FigureAltair(chart)
63+
_, rc = solara.render(el, handle_error=False)
64+
widget = rc.find(solara.widgets.VegaLite).widget
65+
assert widget.spec == spec
66+
rc.close()
67+
68+
69+
def test_figure_altair_no_vegalite_mime(mock_altair_renderers):
70+
"""Test that a KeyError is raised when no Vega-Lite MIME type is found."""
71+
chart = MockChart({"text/plain": "some text"})
72+
73+
el = solara.FigureAltair(chart)
74+
with pytest.raises(KeyError, match="No Vega-Lite MIME type found"):
75+
solara.render(el, handle_error=False)

0 commit comments

Comments
 (0)