Skip to content

Commit 67cc299

Browse files
cpsievertclaude
andcommitted
refactor(python): rename render() to render_altair()
- Rename render() to render_altair() for clearer API - Remove writer parameter (always renders to Altair) - Add **kwargs pass-through to altair.Chart.from_json() - Update tests and README Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent abc2ff6 commit 67cc299

3 files changed

Lines changed: 35 additions & 71 deletions

File tree

ggsql-python/README.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ sql, viz = ggsql.split_query("""
5858
df = duckdb.sql(sql).pl()
5959

6060
# Render DataFrame + VISUALISE spec to Altair chart
61-
chart = ggsql.render(df, viz)
61+
chart = ggsql.render_altair(df, viz)
6262

6363
# Display or save the chart
6464
chart.display() # In Jupyter
@@ -67,22 +67,22 @@ chart.save("chart.html") # Save to file
6767

6868
### Mapping styles
6969

70-
The `render()` function supports various mapping styles:
70+
The `render_altair()` function supports various mapping styles:
7171

7272
```python
7373
df = pl.DataFrame({"x": [1, 2, 3], "y": [10, 20, 30], "category": ["A", "B", "A"]})
7474

7575
# Explicit mapping
76-
ggsql.render(df, "VISUALISE x AS x, y AS y DRAW point")
76+
ggsql.render_altair(df, "VISUALISE x AS x, y AS y DRAW point")
7777

7878
# Implicit mapping (column name = aesthetic name)
79-
ggsql.render(df, "VISUALISE x, y DRAW point")
79+
ggsql.render_altair(df, "VISUALISE x, y DRAW point")
8080

8181
# Wildcard mapping (map all matching columns)
82-
ggsql.render(df, "VISUALISE * DRAW point")
82+
ggsql.render_altair(df, "VISUALISE * DRAW point")
8383

8484
# With color encoding
85-
ggsql.render(df, "VISUALISE x, y, category AS color DRAW point")
85+
ggsql.render_altair(df, "VISUALISE x, y, category AS color DRAW point")
8686
```
8787

8888
## API
@@ -100,14 +100,14 @@ Split a ggSQL query into SQL and VISUALISE portions.
100100
**Raises:**
101101
- `ValueError`: If the query cannot be parsed
102102

103-
### `render(df, viz, *, writer="vegalite") -> altair.Chart`
103+
### `render_altair(df, viz, **kwargs) -> altair.Chart`
104104

105-
Render a DataFrame with a VISUALISE specification.
105+
Render a DataFrame with a VISUALISE specification to an Altair chart.
106106

107107
**Parameters:**
108108
- `df`: Any narwhals-compatible DataFrame (polars, pandas, etc.). LazyFrames are collected automatically.
109109
- `viz`: The VISUALISE specification string
110-
- `writer`: Output format, currently only `"vegalite"` is supported
110+
- `**kwargs`: Additional keyword arguments passed to `altair.Chart.from_json()`. Common options include `validate=False` to skip schema validation.
111111

112112
**Returns:**
113113
- An `altair.Chart` object that can be displayed, saved, or further customized
Lines changed: 11 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,23 @@
11
from __future__ import annotations
2-
from typing import Literal, overload
2+
3+
from typing import Any
34

45
import altair
56
import narwhals as nw
67
from narwhals.typing import IntoFrame
78

89
from ggsql._ggsql import split_query, render as _render
910

10-
__all__ = ["split_query", "render"]
11+
__all__ = ["split_query", "render_altair"]
1112
__version__ = "0.1.0"
1213

13-
_SUPPORTED_WRITERS = {"vegalite"}
14-
1514

16-
@overload
17-
def render(
15+
def render_altair(
1816
df: IntoFrame,
1917
viz: str,
20-
*,
21-
writer: Literal["vegalite"] = ...,
22-
) -> altair.Chart: ...
23-
24-
25-
def render(
26-
df: IntoFrame,
27-
viz: str,
28-
*,
29-
writer: Literal["vegalite"] = "vegalite",
18+
**kwargs: Any,
3019
) -> altair.Chart:
31-
"""Render a DataFrame with a VISUALISE spec.
20+
"""Render a DataFrame with a VISUALISE spec to an Altair chart.
3221
3322
Parameters
3423
----------
@@ -37,19 +26,15 @@ def render(
3726
DataFrame. LazyFrames are collected automatically.
3827
viz
3928
VISUALISE spec string (e.g., "VISUALISE x, y DRAW point")
40-
writer
41-
Output format. Currently only "vegalite" supported.
29+
**kwargs
30+
Additional keyword arguments passed to `altair.Chart.from_json()`.
31+
Common options include `validate=False` to skip schema validation.
4232
4333
Returns
4434
-------
4535
altair.Chart
4636
An Altair chart object.
4737
"""
48-
if writer not in _SUPPORTED_WRITERS:
49-
raise ValueError(
50-
f"Unknown writer: {writer!r}. Supported writers: {', '.join(sorted(_SUPPORTED_WRITERS))}"
51-
)
52-
5338
df = nw.from_native(df, pass_through=True)
5439

5540
if isinstance(df, nw.LazyFrame):
@@ -58,9 +43,8 @@ def render(
5843
if not isinstance(df, nw.DataFrame):
5944
raise TypeError("df must be a narwhals DataFrame or compatible type")
6045

61-
# Should be safe as long as we take polars dependency
6246
pl_df = df.to_polars()
6347

64-
vegalite_json = _render(pl_df, viz, writer=writer)
48+
vegalite_json = _render(pl_df, viz, writer="vegalite")
6549

66-
return altair.Chart.from_json(vegalite_json)
50+
return altair.Chart.from_json(vegalite_json, **kwargs)

ggsql-python/tests/test_ggsql.py

Lines changed: 15 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
33
These tests focus on Python-specific logic:
44
- DataFrame conversion via narwhals
5-
- Writer parameter validation
65
- Return type handling
76
87
Rust logic (parsing, Vega-Lite generation) is tested in the Rust test suite.
@@ -31,17 +30,17 @@ def test_no_visualise_returns_empty_viz(self):
3130
assert viz == ""
3231

3332

34-
class TestRenderDataFrameConversion:
35-
"""Tests for DataFrame handling in render()."""
33+
class TestRenderAltairDataFrameConversion:
34+
"""Tests for DataFrame handling in render_altair()."""
3635

3736
def test_accepts_polars_dataframe(self):
3837
df = pl.DataFrame({"x": [1, 2, 3], "y": [10, 20, 30]})
39-
chart = ggsql.render(df, "VISUALISE x, y DRAW point")
38+
chart = ggsql.render_altair(df, "VISUALISE x, y DRAW point")
4039
assert isinstance(chart, altair.TopLevelMixin)
4140

4241
def test_accepts_polars_lazyframe(self):
4342
lf = pl.LazyFrame({"x": [1, 2, 3], "y": [10, 20, 30]})
44-
chart = ggsql.render(lf, "VISUALISE x, y DRAW point")
43+
chart = ggsql.render_altair(lf, "VISUALISE x, y DRAW point")
4544
assert isinstance(chart, altair.TopLevelMixin)
4645

4746
def test_accepts_narwhals_dataframe(self):
@@ -50,67 +49,48 @@ def test_accepts_narwhals_dataframe(self):
5049
pl_df = pl.DataFrame({"x": [1, 2, 3], "y": [10, 20, 30]})
5150
nw_df = nw.from_native(pl_df)
5251

53-
chart = ggsql.render(nw_df, "VISUALISE x, y DRAW point")
52+
chart = ggsql.render_altair(nw_df, "VISUALISE x, y DRAW point")
5453
assert isinstance(chart, altair.TopLevelMixin)
5554

5655
def test_accepts_pandas_dataframe(self):
5756
pd = pytest.importorskip("pandas")
5857

5958
pd_df = pd.DataFrame({"x": [1, 2, 3], "y": [10, 20, 30]})
60-
chart = ggsql.render(pd_df, "VISUALISE x, y DRAW point")
59+
chart = ggsql.render_altair(pd_df, "VISUALISE x, y DRAW point")
6160
assert isinstance(chart, altair.TopLevelMixin)
6261

6362
def test_rejects_invalid_dataframe_type(self):
6463
with pytest.raises(TypeError, match="must be a narwhals DataFrame"):
65-
ggsql.render({"x": [1, 2, 3]}, "VISUALISE x, y DRAW point")
64+
ggsql.render_altair({"x": [1, 2, 3]}, "VISUALISE x, y DRAW point")
6665

6766

68-
class TestRenderWriterValidation:
69-
"""Tests for writer parameter validation."""
70-
71-
def test_default_writer_is_vegalite(self):
72-
df = pl.DataFrame({"x": [1, 2, 3], "y": [10, 20, 30]})
73-
chart = ggsql.render(df, "VISUALISE x, y DRAW point")
74-
assert isinstance(chart, altair.TopLevelMixin)
75-
76-
def test_explicit_vegalite_writer(self):
77-
df = pl.DataFrame({"x": [1, 2, 3], "y": [10, 20, 30]})
78-
chart = ggsql.render(df, "VISUALISE x, y DRAW point", writer="vegalite")
79-
assert isinstance(chart, altair.TopLevelMixin)
80-
81-
def test_unknown_writer_raises(self):
82-
df = pl.DataFrame({"x": [1, 2, 3], "y": [10, 20, 30]})
83-
with pytest.raises(ValueError, match="Unknown writer.*Supported writers"):
84-
ggsql.render(df, "VISUALISE x, y DRAW point", writer="ggplot2")
85-
86-
87-
class TestRenderReturnType:
88-
"""Tests for render() return type."""
67+
class TestRenderAltairReturnType:
68+
"""Tests for render_altair() return type."""
8969

9070
def test_returns_altair_chart(self):
9171
df = pl.DataFrame({"x": [1, 2, 3], "y": [10, 20, 30]})
92-
chart = ggsql.render(df, "VISUALISE x, y DRAW point")
72+
chart = ggsql.render_altair(df, "VISUALISE x, y DRAW point")
9373
assert isinstance(chart, altair.TopLevelMixin)
9474

9575
def test_chart_has_data(self):
9676
df = pl.DataFrame({"x": [1, 2, 3], "y": [10, 20, 30]})
97-
chart = ggsql.render(df, "VISUALISE x, y DRAW point")
77+
chart = ggsql.render_altair(df, "VISUALISE x, y DRAW point")
9878
spec = chart.to_dict()
9979
# Data should be embedded in datasets
10080
assert "datasets" in spec
10181

10282
def test_chart_can_be_serialized(self):
10383
df = pl.DataFrame({"x": [1, 2, 3], "y": [10, 20, 30]})
104-
chart = ggsql.render(df, "VISUALISE x, y DRAW point")
84+
chart = ggsql.render_altair(df, "VISUALISE x, y DRAW point")
10585
# Should not raise
10686
json_str = chart.to_json()
10787
assert len(json_str) > 0
10888

10989

110-
class TestRenderErrorHandling:
111-
"""Tests for error handling in render()."""
90+
class TestRenderAltairErrorHandling:
91+
"""Tests for error handling in render_altair()."""
11292

11393
def test_invalid_viz_raises(self):
11494
df = pl.DataFrame({"x": [1, 2, 3], "y": [10, 20, 30]})
11595
with pytest.raises(ValueError):
116-
ggsql.render(df, "NOT VALID SYNTAX")
96+
ggsql.render_altair(df, "NOT VALID SYNTAX")

0 commit comments

Comments
 (0)