PyMalloy
Author, check, run, and share Malloy models from Python. Compose models with Python expressions or edit existing Malloy source. Malloy compiles the queries, and DuckDB executes them.
Author and run a model
pip install 'pymalloy[headless]'
import pymalloy as pm
orders = pm.sql("SELECT * FROM (VALUES ('North', 20), ('North', 22)) t(region, amount)")
candidate = (
pm.draft()
.define(orders=orders.extend(
pm.measure(revenue=pm.col("amount").sum().doc("Booked amount in USD.")),
).doc("One row per order."))
.queries(by_region=pm.ref("orders").pipe(pm.query(
pm.group_by(pm.col("region")),
pm.aggregate(pm.col("revenue")),
)))
)
print(candidate.text) # Ordinary Malloy source
print(pm.run(candidate).rows()) # [{'region': 'North', 'revenue': Decimal('42')}]
Drafts are immutable. Check them with Malloy, validate assumptions with named
counterexample queries, and package accepted models with their captured inputs.
Use pm.data(frame) to bring prepared Python data into the model as portable
Parquet. Retain the producing notebook or script for preparation logic.
Explore in a browser widget
pip install 'pymalloy'
from pymalloy import MalloyWidget
widget = MalloyWidget("run: duckdb.sql('SELECT 42 AS answer') -> { select: answer }")
widget
Display the widget in Jupyter or marimo. Malloy and DuckDB WebAssembly run in the browser. The base install includes widgets and agent guidance, without Deno. First use downloads DuckDB WebAssembly and needs browser worker support.
Learn more
- Concepts and boundaries
- Author and validate models
- Capture Python data
- Bundle models and inputs
- Run queries from Python
- Use widgets or export notebooks
- Node and browser JavaScript APIs
For the included versioned agent instructions, import
pymalloy.agent and start with help(pymalloy.agent).