Skip to content

Commit 4ca89f4

Browse files
committed
feat: add validate, height, and width as formal arguments on render_chart() and render_altair()
These were previously only accessible via **kwargs. Using None as the sentinel for height/width preserves ggsql's own defaults when unset.
1 parent 37e51c3 commit 4ca89f4

3 files changed

Lines changed: 118 additions & 8 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## UNRELEASED
4+
5+
### Changed
6+
7+
- `VegaLiteWriter.render_chart()` and `render_altair()` now accept `validate`, `height`, and `width` as explicit keyword arguments. `height` and `width` default to `None`, which preserves whatever dimensions ggsql produces. `validate` defaults to `False` (same behavior as before, now a named parameter).
8+
39
## 0.3.2
410

511
### Changed

python/ggsql/__init__.py

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -93,30 +93,55 @@ def render(self, spec: Spec) -> str:
9393
"""Render a Spec to a Vega-Lite JSON string."""
9494
return self._inner.render(spec)
9595

96-
def render_chart(self, spec: Spec, **kwargs: Any) -> AltairChart:
96+
def render_chart(
97+
self,
98+
spec: Spec,
99+
*,
100+
validate: bool = False,
101+
height: int | None = None,
102+
width: int | None = None,
103+
**kwargs: Any,
104+
) -> AltairChart:
97105
"""Render a Spec to an Altair chart object.
98106
99107
Parameters
100108
----------
101109
spec
102110
The resolved visualization specification from ``reader.execute()``.
111+
validate
112+
Whether to validate the spec against the Vega-Lite schema.
113+
height
114+
Chart height in pixels. When ``None`` (the default), the height
115+
produced by ggsql is used as-is.
116+
width
117+
Chart width in pixels. When ``None`` (the default), the width
118+
produced by ggsql is used as-is.
103119
**kwargs
104120
Additional keyword arguments passed to ``altair.Chart.from_json()``.
105-
Vega-Lite schema validation is disabled by default; pass
106-
``validate=True`` to re-enable it.
107121
108122
Returns
109123
-------
110124
AltairChart
111125
An Altair chart object (Chart, LayerChart, FacetChart, etc.).
112126
"""
113127
vegalite_json = self.render(spec)
114-
return _json_to_altair_chart(vegalite_json, **kwargs)
128+
if height is not None or width is not None:
129+
spec_dict = json.loads(vegalite_json)
130+
if height is not None:
131+
spec_dict["height"] = height
132+
if width is not None:
133+
spec_dict["width"] = width
134+
vegalite_json = json.dumps(spec_dict)
135+
return _json_to_altair_chart(vegalite_json, validate=validate, **kwargs)
115136

116137

117138
def render_altair(
118139
df: IntoFrame,
119140
viz: str,
141+
*,
142+
validate: bool = False,
143+
height: int | None = None,
144+
width: int | None = None,
120145
**kwargs: Any,
121146
) -> AltairChart:
122147
"""Render a DataFrame with a VISUALISE spec to an Altair chart.
@@ -128,10 +153,16 @@ def render_altair(
128153
DataFrame. LazyFrames are collected automatically.
129154
viz
130155
VISUALISE spec string (e.g., "VISUALISE x, y DRAW point")
156+
validate
157+
Whether to validate the spec against the Vega-Lite schema.
158+
height
159+
Chart height in pixels. When ``None`` (the default), the height
160+
produced by ggsql is used as-is.
161+
width
162+
Chart width in pixels. When ``None`` (the default), the width
163+
produced by ggsql is used as-is.
131164
**kwargs
132-
Additional keyword arguments passed to `from_json()`.
133-
Vega-Lite schema validation is disabled by default; pass
134-
`validate=True` to re-enable it.
165+
Additional keyword arguments passed to ``altair.Chart.from_json()``.
135166
136167
Returns
137168
-------
@@ -160,4 +191,12 @@ def render_altair(
160191
writer = VegaLiteWriter()
161192
vegalite_json = writer.render(spec)
162193

163-
return _json_to_altair_chart(vegalite_json, **kwargs)
194+
if height is not None or width is not None:
195+
spec_dict = json.loads(vegalite_json)
196+
if height is not None:
197+
spec_dict["height"] = height
198+
if width is not None:
199+
spec_dict["width"] = width
200+
vegalite_json = json.dumps(spec_dict)
201+
202+
return _json_to_altair_chart(vegalite_json, validate=validate, **kwargs)

tests/test_ggsql.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,37 @@ def test_chart_can_be_serialized(self):
275275
json_str = chart.to_json()
276276
assert len(json_str) > 0
277277

278+
def test_height_width(self):
279+
table = pa.table({"x": [1, 2, 3], "y": [10, 20, 30]})
280+
chart = ggsql.render_altair(
281+
table, "VISUALISE x, y DRAW point", height=300, width=500
282+
)
283+
chart_dict = chart.to_dict()
284+
assert chart_dict["height"] == 300
285+
assert chart_dict["width"] == 500
286+
287+
def test_height_only(self):
288+
table = pa.table({"x": [1, 2, 3], "y": [10, 20, 30]})
289+
chart = ggsql.render_altair(table, "VISUALISE x, y DRAW point", height=400)
290+
chart_dict = chart.to_dict()
291+
assert chart_dict["height"] == 400
292+
293+
def test_default_preserves_ggsql_dimensions(self):
294+
table = pa.table({"x": [1, 2, 3], "y": [10, 20, 30]})
295+
296+
reader = ggsql.DuckDBReader("duckdb://memory")
297+
reader.register("__data__", table)
298+
spec = reader.execute("SELECT * FROM __data__ VISUALISE x, y DRAW point")
299+
raw_spec = json.loads(ggsql.VegaLiteWriter().render(spec))
300+
301+
chart = ggsql.render_altair(table, "VISUALISE x, y DRAW point")
302+
chart_dict = chart.to_dict()
303+
304+
if "height" in raw_spec:
305+
assert chart_dict["height"] == raw_spec["height"]
306+
if "width" in raw_spec:
307+
assert chart_dict["width"] == raw_spec["width"]
308+
278309

279310
class TestRenderAltairChartTypeDetection:
280311
"""Tests for correct Altair chart type detection based on spec structure."""
@@ -638,3 +669,37 @@ def test_render_chart_facet(self):
638669
writer = ggsql.VegaLiteWriter()
639670
chart = writer.render_chart(spec, validate=False)
640671
assert isinstance(chart, altair.FacetChart)
672+
673+
def test_render_chart_height_width(self):
674+
"""render_chart() injects height/width into the spec when provided."""
675+
reader = ggsql.DuckDBReader("duckdb://memory")
676+
spec = reader.execute("SELECT 1 AS x, 2 AS y VISUALISE x, y DRAW point")
677+
writer = ggsql.VegaLiteWriter()
678+
chart = writer.render_chart(spec, height=300, width=500)
679+
chart_dict = chart.to_dict()
680+
assert chart_dict["height"] == 300
681+
assert chart_dict["width"] == 500
682+
683+
def test_render_chart_height_only(self):
684+
"""render_chart() can set height without width."""
685+
reader = ggsql.DuckDBReader("duckdb://memory")
686+
spec = reader.execute("SELECT 1 AS x, 2 AS y VISUALISE x, y DRAW point")
687+
writer = ggsql.VegaLiteWriter()
688+
chart = writer.render_chart(spec, height=400)
689+
chart_dict = chart.to_dict()
690+
assert chart_dict["height"] == 400
691+
692+
def test_render_chart_default_preserves_ggsql_dimensions(self):
693+
"""When height/width are not specified, ggsql's defaults are preserved."""
694+
reader = ggsql.DuckDBReader("duckdb://memory")
695+
spec = reader.execute("SELECT 1 AS x, 2 AS y VISUALISE x, y DRAW point")
696+
writer = ggsql.VegaLiteWriter()
697+
698+
raw_spec = json.loads(writer.render(spec))
699+
chart = writer.render_chart(spec)
700+
chart_dict = chart.to_dict()
701+
702+
if "height" in raw_spec:
703+
assert chart_dict["height"] == raw_spec["height"]
704+
if "width" in raw_spec:
705+
assert chart_dict["width"] == raw_spec["width"]

0 commit comments

Comments
 (0)