Skip to content

Commit 37e51c3

Browse files
committed
fix: fall back to altair.Chart when specific chart type can't parse the spec
Specs with properties Altair can't deserialize (e.g., boxplot layers with extra y2 encoding attributes) now gracefully degrade to altair.Chart instead of raising TypeError.
1 parent 9b469b4 commit 37e51c3

3 files changed

Lines changed: 59 additions & 8 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
- Upgraded to ggsql Rust crate v0.3.2.
88
- `render_altair()` and `VegaLiteWriter.render_chart()` now default to `validate=False` when creating Altair chart objects. This avoids `ValidationError`s for valid specs (e.g., boxplots) that use Vega-Lite features not yet reflected in Altair's schema. Pass `validate=True` to re-enable.
9+
- When Altair can't deserialize a spec into the expected chart subclass (e.g., `LayerChart`), the converter now falls back to `altair.Chart` instead of raising. The chart still displays correctly; only Altair-level round-tripping (`.to_dict()`) is lost.
910

1011
## 0.3.1
1112

python/ggsql/__init__.py

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,20 +52,27 @@ def _json_to_altair_chart(vegalite_json: str, **kwargs: Any) -> AltairChart:
5252
kwargs.setdefault("validate", False)
5353
spec = json.loads(vegalite_json)
5454

55+
chart_class: type[AltairChart] | None = None
5556
if "layer" in spec:
56-
return altair.LayerChart.from_json(vegalite_json, **kwargs)
57+
chart_class = altair.LayerChart
5758
elif "facet" in spec or "spec" in spec:
58-
return altair.FacetChart.from_json(vegalite_json, **kwargs)
59+
chart_class = altair.FacetChart
5960
elif "concat" in spec:
60-
return altair.ConcatChart.from_json(vegalite_json, **kwargs)
61+
chart_class = altair.ConcatChart
6162
elif "hconcat" in spec:
62-
return altair.HConcatChart.from_json(vegalite_json, **kwargs)
63+
chart_class = altair.HConcatChart
6364
elif "vconcat" in spec:
64-
return altair.VConcatChart.from_json(vegalite_json, **kwargs)
65+
chart_class = altair.VConcatChart
6566
elif "repeat" in spec:
66-
return altair.RepeatChart.from_json(vegalite_json, **kwargs)
67-
else:
68-
return altair.Chart.from_json(vegalite_json, **kwargs)
67+
chart_class = altair.RepeatChart
68+
69+
if chart_class is not None:
70+
try:
71+
return chart_class.from_json(vegalite_json, **kwargs)
72+
except Exception:
73+
pass
74+
75+
return altair.Chart.from_json(vegalite_json, **kwargs)
6976

7077

7178
class VegaLiteWriter:

tests/test_ggsql.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,49 @@ def test_chart_with_color_encoding(self):
351351
assert isinstance(chart, altair.LayerChart)
352352

353353

354+
class TestAltairChartFallback:
355+
"""Tests for fallback to Chart when a specific Altair type can't parse the spec."""
356+
357+
def test_layer_fallback_to_chart(self):
358+
"""Specs with 'layer' fall back to Chart when LayerChart.from_json fails."""
359+
from ggsql import _json_to_altair_chart
360+
361+
spec = json.dumps(
362+
{
363+
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
364+
"data": {"values": [{"x": 1, "y": 2}]},
365+
"layer": [
366+
{
367+
"mark": "point",
368+
"encoding": {"x": {"field": "x", "type": "quantitative"}},
369+
"not_a_real_property": True,
370+
}
371+
],
372+
}
373+
)
374+
chart = _json_to_altair_chart(spec)
375+
assert isinstance(chart, altair.Chart)
376+
377+
def test_layer_uses_specific_type_when_possible(self):
378+
"""Specs with 'layer' use LayerChart when parsing succeeds."""
379+
from ggsql import _json_to_altair_chart
380+
381+
spec = json.dumps(
382+
{
383+
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
384+
"data": {"values": [{"x": 1, "y": 2}]},
385+
"layer": [
386+
{
387+
"mark": "point",
388+
"encoding": {"x": {"field": "x", "type": "quantitative"}},
389+
}
390+
],
391+
}
392+
)
393+
chart = _json_to_altair_chart(spec)
394+
assert isinstance(chart, altair.LayerChart)
395+
396+
354397
class TestRenderAltairErrorHandling:
355398
"""Tests for error handling in render_altair()."""
356399

0 commit comments

Comments
 (0)