Python Query Scripts
Python query scripts are the third query script type alongside SQL and DQL. They let you write data processing logic in real Python with standard libraries: query data sources like SQL, transform/aggregate/clean data with a Polars-style DataFrame API, and pull external data via fetch().
Like other modes in the query editor, Python scripts follow the same execution workflow — select a data source, write a script, run, view results, and visualize.
DQL is based on Starlark (a Python-like language, but not actual Python). Python query scripts run real Python (interpreted by RustPython compiled to WebAssembly, executed in a sandbox), so you can import standard libraries and use full Python syntax. The data processing API uses a Polars-style design (pl.col, select/with_columns/filter/group_by().agg() with lazy expressions).
Quick Start
Python scripts must define a main() function as the entry point; its return value is the query result:
def main():
return [{"city": "Beijing", "value": 1}, {"city": "Shanghai", "value": 2}]
main() can return:
DataFrame— directly converted to result set (recommended, zero JSON round-trip)Series— converted to a single-column result setlist[dict]or other JSON-serializable values — go through JSON conversion
No import needed: DataFrame, Series, pl, as well as query, fetch, args, print are all directly injected into the script scope.
Querying Data Sources
Use the built-in query() to execute SQL, returning a DataFrame:
def main():
df = query("SELECT id, name, amount FROM orders WHERE amount > ?", 100)
return df.filter(pl.col("amount") > 500)
query() uses the same query engine pathway as regular SQL queries, supporting bound/mounted data sources and cross-source queries.
SQL timestamp / date columns are returned as strings. Convert them explicitly:
df = df.with_columns(pl.col("created_at").str.to_datetime())
Using Parameters
Caller-supplied parameters are accessed via the global args (always a dict):
def main():
threshold = args["threshold"]
df = query("SELECT * FROM sales")
return df.filter(pl.col("revenue") > threshold)
Making HTTP Requests
The built-in fetch() provides JS fetch-style HTTP access:
def main():
res = fetch("https://api.example.com/data", headers={"Authorization": "Bearer ..."})
if not res.ok:
return [{"error": res.status}]
return res.json()
bodyasdict/listis auto-serialized to JSON with defaultcontent-type: application/json- Transport errors (DNS / connection failure) raise exceptions; HTTP 4xx / 5xx do NOT raise — check
res.ok/res.status res.text()for raw text,res.json()for JSON parsing,res.headers.get(name)for response headers
Data Processing (Polars-style)
DataFrame / Series / lazy Expr provide selection, filtering, derived columns, group-by aggregation, and more. Also supported: rolling windows, exponentially weighted moving, time-window grouping, joins, conditional expressions, and cumulative operations:
def main():
df = query("SELECT category, region, amount FROM sales")
return (
df.filter(pl.col("amount").is_not_null())
.group_by("category", "region")
.agg(pl.col("amount").sum().alias("total"))
)
See the Python Language Reference for the full API.
Logging
The built-in print() is redirected to the script log (it does not pollute the result set), useful for debugging:
def main():
df = query("SELECT * FROM orders")
print("Row count:", df.height)
return df
Sandbox & Limitations
Python scripts execute in a sandboxed WebAssembly environment with the following constraints:
| Constraint | Description |
|---|---|
| Entry function | Must define a callable main(), otherwise the script errors |
| Execution timeout | Max 60 seconds per execution |
| Memory limit | Approx. 256 MiB linear memory |
| No filesystem | Scripts cannot read or write local files |
| Result truncation | Results exceeding a certain row count are truncated |
Regarding standard libraries: frozen pure-Python libraries (such as json, re, argparse) and native modules like math, datetime, struct, hashlib can all be imported normally.
Related Documents
- Python Language Reference — Complete reference for built-in types (DataFrame / Series / Expr) and functions (
query/fetch/pl) - Query Editor — Editor layout and data source binding
- DQL Query Scripts — Another scripted query approach (Starlark)