Add high-level Rust API and rework Python bindings#89
Conversation
|
This is great, thanks for this! I have some ideas sketched out in #90, mostly driven by how I'd imagine Python users wanting things. With that approach, I think we can get away with a reduced, singular API: import ggsql
import polars as pl
df = pl.DataFrame({"x": [1, 2, 3], "y": [10, 20, 30]})
reader = ggsql.readers.DuckDB("duckdb://memory")
ggspec = reader.execute(
"SELECT * FROM data VISUALISE x, y DRAW point",
{"data": df}
)
# return an altair chart
writer = ggsql.writers.VegaLite()
writer.render_chart(ggspec)
# or a string/dict for more control
writer.render(ggspec)That said, feel free to merge here if you like, and we can iterate on #90 as a follow up (it's very much a WIP at this point). BTW, here are a few notes about the current API that seem suboptimal:
|
|
Since something has gone wrong with the
The Rust side of the API is consumed not just by the Python package, but also a collection of other Rust things (and a future R package). I am very happy for you to make the interface exposed by the Python package to users as Pythonic as possible, but we must be sure the nature does not leak too much into the Rust backend. That said, these do look like good suggestions in general. Let me work through them, and we'll see where we land. |
This is the only part I'm not sure about also reflecting onto the Rust side. I think for this PR I'm going to keep a generic On the other hand, I am happy for a re-worked #90 to expand this on the Python side so that writers are given extra methods like |
Oh, totally -- that change specifically is for Python. BTW, I'm good with merging this now. It's probably better that I break my stuff into multiple PRs so it's easier to review. |
|
Your suggestions made enough sense to me that I've already decided to make changes :) I'll tag you for a re-review when I'm done, then we can merge and work from there. |
|
Okay! So I've flipped the API in the way you suggest, renaming I think I've caught everything in this PR's scope. I also added a little example at the end of Please can you take another look so that I'm sure that this is along the direction you were thinking of, before I merge? |
|
Awesome, thanks for this! Just a couple minor notes that might impact the Rust API:
A couple other, more Python-specific things I've been considering that I'm happy to do as follow-up if you're on board:
Also, much longer term, I think we may also want the |
All of these sound like good ideas. Please feel free to take them on in follow-up PRs!
I am happy for this to be the case on the Python side, but I'd like to keep the Rust API I think for the Python side you could do it in the bridge function: #[pyfunction]
fn execute(query: &str, reader: &Bound<'_, PyAny>) -> PyResult<PySpec> {
// Register data if provided
if let Some(data_dict) = data {
for (name, df) in data_dict.iter() {
let name_str: String = name.extract()?;
reader.call_method1("register", (name_str.as_str(), df))?;
}
}
[... existing function...]
}
Sounds good. When we think it is time for distributing on PyPI can I get back to you and for assistance deploying/maintaining this? |
Yep! |
src/api.rswith a two-stage API:prepare()->render()execute(sql) -> DataFrameinterface (see docs in PR)split_queryfrom public APIsRust API:
Python API: