Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,17 @@ Keep the editor in one cell and consume `editor.result` in another. The editor c

Marimo only shows **what you `return` from a cell**. Calling `mo.vstack(...)` or `hm.query_result(...)` without returning it produces no visible output.

See `examples/hotdata_basic.py` for a full notebook: five Python cells (`mo.vstack` for **controls only**, then a separate cell `return hm.query_result(editor.result)` so results show immediately — **avoid** `mo.lazy` here: it only renders after the block scrolls into view, which looks like an empty cell). If Marimo shows **empty cells**, quit and remove `examples/__marimo__/` so the UI reloads from the `.py` file only.
See `examples/demo.py` for a full runnable notebook flow.

## Examples

- `examples/demo.py` — end-to-end browser + editor + result rendering flow.

Run:

```bash
uv run marimo edit examples/demo.py --no-token
```

## Layout

Expand All @@ -58,7 +68,7 @@ This package depends on [**hotdata-runtime**](https://github.com/hotdata-dev/hot
```bash
uv sync --locked
uv run pytest
marimo edit examples/hotdata_basic.py --no-token
marimo edit examples/demo.py --no-token
```

To pin **hotdata-runtime** from Git instead of the sibling path, remove the `[tool.uv.sources]` block, set the dependency line as needed, and run `uv lock` again.
Expand Down
95 changes: 95 additions & 0 deletions examples/demo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import marimo

__generated_with = "0.23.5"
app = marimo.App()


@app.cell
def _():
import os

import marimo as mo

import hotdata_marimo as hm

hm.register_hotdata_sql_engine()
return hm, mo, os


@app.cell
def _(hm, mo, os):
mo.stop(
not os.environ.get("HOTDATA_API_KEY"),
mo.callout(
mo.md(
"Add **HOTDATA_API_KEY** to your environment "
"to run this example."
),
kind="warn",
),
)
workspace = hm.workspace_selector_from_env()
return (workspace,)


@app.cell
def _(hm, workspace):
client = workspace.client
status = hm.connection_status(client)
browser = hm.table_browser(client)
editor = hm.sql_editor(
client,
default_sql="SELECT 1 AS ok",
)
recent = hm.recent_results(client, limit=20)
history = hm.run_history(client, limit=10)
return browser, client, editor, history, recent, status, workspace


@app.cell
def _(browser, editor, mo, recent, status, workspace):
return mo.vstack(
[
workspace.ui,
status,
browser.ui,
editor.ui,
recent.ui,
],
gap=2,
)


@app.cell
def _(history):
return history


@app.cell
def _(editor, hm):
# Explicitly touch nested widget values so Marimo reruns this cell on clicks.
_run = editor.run.value
_rerun = editor.rerun.value
_clear = editor.clear.value
return hm.query_result(editor.result), _clear, _rerun, _run


@app.cell
def _(hm, recent):
_selected = recent.pick.value
return hm.query_result(recent.result, label="Recent result"), _selected


@app.cell
def _(client, mo):
_df = mo.sql(
"""
SELECT 1 AS example_value
""",
engine=client,
)
return


if __name__ == "__main__":
app.run()
76 changes: 0 additions & 76 deletions examples/hotdata_basic.py

This file was deleted.

10 changes: 9 additions & 1 deletion hotdata_marimo/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,19 @@
recent_results,
run_history,
)
from hotdata_marimo.sql_engine import (
HotdataMarimoEngine,
register_hotdata_sql_engine,
unregister_hotdata_sql_engine,
)
from hotdata_marimo.sql_editor import SqlEditor, sql_editor
from hotdata_marimo.table_browser import TableBrowser, connection_picker, table_browser
from hotdata_marimo.workspace_selector import WorkspaceSelector, workspace_selector_from_env

__all__ = [
"__version__",
"HotdataClient",
"HotdataMarimoEngine",
"QueryResult",
"RecentResults",
"SqlEditor",
Expand All @@ -39,11 +45,13 @@
"hotdata_workspace_selector",
"query_result",
"recent_results",
"register_hotdata_sql_engine",
"register_mo_ui_hotdata_aliases",
"run_history",
"sql_editor",
"table_browser",
"unregister_hotdata_sql_engine",
"workspace_selector_from_env",
"register_mo_ui_hotdata_aliases",
]

hotdata_sql_editor = sql_editor
Expand Down
62 changes: 37 additions & 25 deletions hotdata_marimo/display.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,20 @@

import marimo as mo

from hotdata_runtime.client import HotdataClient
from hotdata_runtime.health import workspace_health_lines
from hotdata_runtime.result import QueryResult
from hotdata_runtime import HotdataClient, QueryResult, workspace_health_lines


def _option_map_with_unique_labels(
pairs: list[tuple[str, str]],
) -> dict[str, str]:
counts: dict[str, int] = {}
options: dict[str, str] = {}
for label, value in pairs:
count = counts.get(label, 0)
counts[label] = count + 1
key = label if count == 0 else f"{label} ({count + 1})"
options[key] = value
return options


def query_result(
Expand All @@ -27,17 +38,18 @@ def query_result(
)
else:
trunc = None
meta = result.metadata_dict()
meta_bits = []
if result.result_id:
meta_bits.append(f"**result_id** `{result.result_id}`")
if result.query_run_id:
meta_bits.append(f"**query_run_id** `{result.query_run_id}`")
if result.execution_time_ms is not None:
meta_bits.append(f"**execution_time_ms** {result.execution_time_ms}")
if result.warning:
meta_bits.append(f"**warning** {result.warning}")
if result.error_message:
meta_bits.append(f"**error** {result.error_message}")
if meta["result_id"]:
meta_bits.append(f"**result_id** `{meta['result_id']}`")
if meta["query_run_id"]:
meta_bits.append(f"**query_run_id** `{meta['query_run_id']}`")
if meta["execution_time_ms"] is not None:
meta_bits.append(f"**execution_time_ms** {meta['execution_time_ms']}")
if meta["warning"]:
meta_bits.append(f"**warning** {meta['warning']}")
if meta["error_message"]:
meta_bits.append(f"**error** {meta['error_message']}")
header = mo.md(" · ".join(meta_bits) if meta_bits else "_No metadata._")
df = result.to_pandas()
tbl = mo.ui.table(
Expand All @@ -59,11 +71,12 @@ def query_result(
class RecentResults:
def __init__(self, client: HotdataClient, *, limit: int = 50) -> None:
self._client = client
listing = client.results().list_results(limit=limit, offset=0)
self._results = listing.results
options = {
f"{r.created_at} · {r.status} · {r.id}": r.id for r in self._results
}
self._results = client.list_recent_results(limit=limit, offset=0)
option_pairs = [
(f"{r.created_at} · {r.status} · {r.result_id}", r.result_id)
for r in self._results
]
options = _option_map_with_unique_labels(option_pairs)
self.pick = mo.ui.dropdown(
options=options or {"(no results)": ""},
label="Recent results",
Expand Down Expand Up @@ -97,20 +110,19 @@ def run_history(
limit: int = 20,
label: str = "Run history",
):
runs = client.query_runs().list_query_runs(limit=limit).query_runs
runs = client.list_run_history(limit=limit)
if not runs:
return mo.md("_No query runs returned._")

rows: list[dict[str, object]] = []
for r in runs:
rows.append(
{
"created_at": getattr(r, "created_at", None),
"status": getattr(r, "status", None),
"execution_time_ms": getattr(r, "execution_time_ms", None),
"result_id": getattr(r, "result_id", None),
"query_run_id": getattr(r, "id", None)
or getattr(r, "query_run_id", None),
"created_at": r.created_at,
"status": r.status,
"execution_time_ms": r.execution_time_ms,
"result_id": r.result_id,
"query_run_id": r.query_run_id,
}
)

Expand Down
3 changes: 1 addition & 2 deletions hotdata_marimo/sql_editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@

import marimo as mo

from hotdata_runtime.client import HotdataClient
from hotdata_runtime.result import QueryResult
from hotdata_runtime import HotdataClient, QueryResult


class SqlEditor:
Expand Down
Loading
Loading