Python Client

The Python SDK supports both remote mode (connects to a running server) and local mode (no server needed).

Installation

pip install motley-slayer[client]   # httpx + pandas

Usage

Remote Mode

from slayer.client.slayer_client import SlayerClient

client = SlayerClient(url="http://localhost:5143")

query = {
    "source_model": "orders",
    "measures": ["*:count", "revenue:sum"],
    "dimensions": ["status"],
    "limit": 10,
}

# Get raw data
data = client.query(query)
# [{"orders.status": "completed", "orders._count": 42, ...}, ...]

# Get pandas DataFrame
df = client.query_df(query)
print(df)

Local Mode

No server needed — queries execute directly against the storage backend:

from slayer.client.slayer_client import SlayerClient
from slayer.storage.yaml_storage import YAMLStorage

client = SlayerClient(storage=YAMLStorage(base_dir="./slayer_data"))

# Same query API as remote mode
data = client.query(query)
df = client.query_df(query)

Accepted Input Shapes

client.query / query_sync / sql / sql_sync / explain / explain_sync / query_df all accept the same input union (mirroring engine.execute):

  • A dict — a single query.
  • A SlayerQuery instance.
  • A list of dicts or SlayerQuery — a multi-stage DAG. Earlier stages are named sub-queries; the last entry is the root. Order doesn't matter (the engine auto-sorts). See Query Lists.
  • A string — runs the backing query of a query-backed model by name.
# Multi-stage DAG
client.query_sync([
    {"name": "by_customer", "source_model": "orders", "measures": [{"formula": "amount:sum"}], "dimensions": [{"name": "customer_id"}]},
    {"source_model": "by_customer", "measures": [{"formula": "amount_sum:avg"}]},
])

# Run-by-name (query-backed model)
client.query_sync("rev_by_region")

Other Methods

# List models
models = client.list_models()

# Get model definition
model = client.get_model("orders")

# Create a model
client.create_model({"name": "orders", "sql_table": "public.orders", ...})

# List datasources
datasources = client.list_datasources()

# Create a datasource
client.create_datasource({"name": "mydb", "type": "postgres", ...})

Direct Engine Access

For maximum control, use the query engine directly:

from slayer.engine.query_engine import SlayerQueryEngine
from slayer.storage.yaml_storage import YAMLStorage

storage = YAMLStorage(base_dir="./slayer_data")
engine = SlayerQueryEngine(storage=storage)

result = engine.execute(query=query)
# result.data      — list of row dicts
# result.columns   — list of column names
# result.attributes — ResponseAttributes with .dimensions and .measures dicts (column → FieldMetadata)
#
# client.query() returns SlayerResponse with all fields above
# client.sql(query) returns just the generated SQL string
# client.explain(query) returns SlayerResponse with EXPLAIN ANALYZE output
# result.row_count — number of rows
# result.sql       — generated SQL string