Skip to content

Commit 350424c

Browse files
cpsievertclaude
andcommitted
fix(python): update custom reader tests to use DuckDB backend
The execution pipeline now generates SQL with column renames (e.g., SELECT x AS "__ggsql_aes_x__") that custom readers must execute. Static readers returning hardcoded DataFrames no longer work. Updated test readers to use in-memory DuckDB connections so they properly execute the SQL they receive. Added duckdb and pyarrow as test dependencies. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent e79da48 commit 350424c

2 files changed

Lines changed: 34 additions & 18 deletions

File tree

ggsql-python/pyproject.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ dependencies = [
2121
]
2222

2323
[project.optional-dependencies]
24-
test = ["pytest>=7.0"]
24+
test = ["pytest>=7.0", "duckdb>=1.0", "pyarrow>=14.0"]
2525
dev = ["maturin>=1.4"]
2626

2727
[tool.maturin]
@@ -31,6 +31,8 @@ module-name = "ggsql._ggsql"
3131

3232
[dependency-groups]
3333
dev = [
34+
"duckdb>=1.4.4",
3435
"maturin>=1.11.5",
36+
"pyarrow>=23.0.1",
3537
"pytest>=9.0.2",
3638
]

ggsql-python/tests/test_ggsql.py

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
import json
1212

13+
import duckdb
1314
import pytest
1415
import polars as pl
1516
import altair
@@ -399,8 +400,16 @@ def test_simple_custom_reader(self):
399400
"""Custom reader with execute_sql() method works."""
400401

401402
class SimpleReader:
403+
def __init__(self):
404+
self.conn = duckdb.connect()
405+
self.conn.execute(
406+
"CREATE TABLE data AS SELECT * FROM ("
407+
"VALUES (1, 10), (2, 20), (3, 30)"
408+
") AS t(x, y)"
409+
)
410+
402411
def execute_sql(self, sql: str) -> pl.DataFrame:
403-
return pl.DataFrame({"x": [1, 2, 3], "y": [10, 20, 30]})
412+
return self.conn.execute(sql).pl()
404413

405414
reader = SimpleReader()
406415
spec = ggsql.execute("SELECT * FROM data VISUALISE x, y DRAW point", reader)
@@ -411,19 +420,16 @@ def test_custom_reader_with_register(self):
411420

412421
class RegisterReader:
413422
def __init__(self):
414-
self.tables = {}
423+
self.conn = duckdb.connect()
415424

416425
def execute_sql(self, sql: str) -> pl.DataFrame:
417-
# Simple: just return the first registered table
418-
if self.tables:
419-
return next(iter(self.tables.values()))
420-
return pl.DataFrame({"x": [1], "y": [2]})
426+
return self.conn.execute(sql).pl()
421427

422428
def supports_register(self) -> bool:
423429
return True
424430

425431
def register(self, name: str, df: pl.DataFrame) -> None:
426-
self.tables[name] = df
432+
self.conn.register(name, df)
427433

428434
reader = RegisterReader()
429435
spec = ggsql.execute("SELECT 1 AS x, 2 AS y VISUALISE x, y DRAW point", reader)
@@ -460,17 +466,20 @@ def test_native_reader_fast_path(self):
460466
def test_custom_reader_can_render(self):
461467
"""Custom reader result can be rendered to Vega-Lite."""
462468

463-
class StaticReader:
464-
def execute_sql(self, sql: str) -> pl.DataFrame:
465-
return pl.DataFrame(
466-
{
467-
"x": [1, 2, 3, 4, 5],
468-
"y": [10, 40, 20, 50, 30],
469-
"category": ["A", "B", "A", "B", "A"],
470-
}
469+
class DuckDBReader:
470+
def __init__(self):
471+
self.conn = duckdb.connect()
472+
self.conn.execute(
473+
"CREATE TABLE data AS SELECT * FROM ("
474+
"VALUES (1, 10, 'A'), (2, 40, 'B'), (3, 20, 'A'), "
475+
"(4, 50, 'B'), (5, 30, 'A')"
476+
") AS t(x, y, category)"
471477
)
472478

473-
reader = StaticReader()
479+
def execute_sql(self, sql: str) -> pl.DataFrame:
480+
return self.conn.execute(sql).pl()
481+
482+
reader = DuckDBReader()
474483
spec = ggsql.execute(
475484
"SELECT * FROM data VISUALISE x, y, category AS color DRAW point",
476485
reader,
@@ -488,11 +497,16 @@ def test_custom_reader_execute_sql_called(self):
488497

489498
class RecordingReader:
490499
def __init__(self):
500+
self.conn = duckdb.connect()
501+
self.conn.execute(
502+
"CREATE TABLE data AS SELECT * FROM ("
503+
"VALUES (1, 2)) AS t(x, y)"
504+
)
491505
self.execute_calls = []
492506

493507
def execute_sql(self, sql: str) -> pl.DataFrame:
494508
self.execute_calls.append(sql)
495-
return pl.DataFrame({"x": [1], "y": [2]})
509+
return self.conn.execute(sql).pl()
496510

497511
reader = RecordingReader()
498512
ggsql.execute(

0 commit comments

Comments
 (0)