Skip to content

Commit af43b9f

Browse files
authored
Merge pull request #5 from hotdata-dev/feat/managed-databases-marimo
feat: managed database widgets for Marimo
2 parents 1c26e06 + 954177f commit af43b9f

10 files changed

Lines changed: 684 additions & 226 deletions

File tree

README.md

Lines changed: 84 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,57 @@
11
# hotdata-marimo
22

3-
Marimo UI helpers for [Hotdata](https://hotdata.dev): run SQL from a notebook, browse catalog metadata, and render results as tables.
3+
Marimo widgets for [Hotdata](https://hotdata.dev): run SQL, browse catalogs, load managed databases, and display results in notebooks.
44

5-
## Features
5+
Requires Python 3.10+, [Marimo](https://marimo.io/), and [hotdata-runtime](https://github.com/hotdata-dev/hotdata-runtime) (installed automatically).
66

7-
- **Workspace-aware setup** — build a `HotdataClient` from environment variables, or use `workspace_selector_from_env()` to choose a workspace interactively when no workspace is pinned.
8-
- **Connection health** — show a compact status callout with API, workspace, and optional sandbox context.
9-
- **Catalog browsing** — browse Hotdata connections, schemas, tables, and columns from Marimo UI controls.
10-
- **SQL editor widget** — run SQL against Hotdata, cache the latest successful result, and render results in downstream reactive cells.
11-
- **Native `mo.sql` engine** — register `HotdataMarimoEngine` so Marimo SQL cells can execute through a live `HotdataClient` with `engine=client`.
12-
- **Result display helpers** — render query results, recent results, and run history as notebook-friendly UI.
13-
- **Marimo UI aliases** — importing `hotdata_marimo` attaches helpers such as `mo.ui.hotdata_sql_editor` and `mo.ui.hotdata_table_browser` for discoverability.
7+
## Supported widgets
8+
9+
Importing `hotdata_marimo` registers `mo.ui.hotdata_*` aliases for discoverability.
10+
11+
| Widget | Function | Notes |
12+
|--------|----------|-------|
13+
| SQL editor | `hm.sql_editor(client)` | Returns `.ui` and `.result` |
14+
| Table browser | `hm.table_browser(client)` | Browse connections, schemas, tables, columns |
15+
| Managed databases panel | `hm.databases_panel(client)` | Create catalogs and load parquet files |
16+
| Managed database writer | `hm.managed_database_writer(client)` | Lower-level create/load UI |
17+
| Workspace selector | `hm.workspace_selector_from_env()` | Pick workspace when `HOTDATA_WORKSPACE` is unset |
18+
| Connection picker | `hm.connection_picker(client)` | Dropdown of workspace connections |
19+
| Connection status | `hm.connection_status(client)` | API / workspace health callout |
20+
| Connections panel | `hm.connections_panel(client)` | Status callout plus connection list |
21+
| Query result | `hm.query_result(result)` | Render a `QueryResult` as a table |
22+
| Recent results | `hm.recent_results(client)` | Browse past query results |
23+
| Run history | `hm.run_history(client)` | Recent query runs |
24+
25+
Each widget also has a `mo.ui.hotdata_*` alias (e.g. `mo.ui.hotdata_sql_editor`). Native Marimo SQL cells are supported via `hm.register_hotdata_sql_engine()` and `mo.sql(..., engine=client)`.
1426

1527
## Install
1628

1729
```bash
18-
uv pip install hotdata-marimo
19-
# or: pip install hotdata-marimo
30+
pip install hotdata-marimo
31+
```
32+
33+
Set `HOTDATA_API_KEY`. Optionally set `HOTDATA_WORKSPACE`, `HOTDATA_API_URL`, or `HOTDATA_SANDBOX`.
34+
35+
## Connect
36+
37+
```python
38+
import hotdata_marimo as hm
39+
40+
client = hm.from_env()
2041
```
2142

22-
Requires Python 3.10+, **Marimo**, and [**hotdata-runtime**](https://github.com/hotdata-dev/hotdata-runtime) (Hotdata SDK + runtime/session semantics — pulled in automatically when you `pip install hotdata-marimo`).
43+
If `HOTDATA_WORKSPACE` is unset, pick a workspace interactively:
44+
45+
```python
46+
ws = hm.workspace_selector_from_env()
47+
client = ws.client
48+
```
2349

24-
## Environment
50+
## SQL editor widget
2551

26-
| Variable | Required | Description |
27-
|----------|----------|-------------|
28-
| `HOTDATA_API_KEY` | Yes | API key for the Hotdata API |
29-
| `HOTDATA_API_URL` | No | API base URL (default: `https://api.hotdata.dev`) |
30-
| `HOTDATA_WORKSPACE` | No | Workspace id; if unset, the first active workspace is used |
31-
| `HOTDATA_SANDBOX` | No | Sandbox session id, passed through to the SDK |
52+
Run SQL in one cell; show results in the next. Marimo only renders what you **`return`**.
3253

33-
## Minimal notebook
54+
**Cell 1 — editor**
3455

3556
```python
3657
import marimo as mo
@@ -41,25 +62,30 @@ editor = hm.sql_editor(client, default_sql="SELECT 1 AS ok")
4162
return editor.ui
4263
```
4364

65+
**Cell 2 — result**
66+
4467
```python
4568
return hm.query_result(editor.result)
4669
```
4770

48-
Importing `hotdata_marimo` registers discoverability aliases on Marimo’s UI namespace, so you can also use `mo.ui.hotdata_sql_editor`, `mo.ui.hotdata_table_browser`, `mo.ui.hotdata_query_result`, and `mo.ui.hotdata_connection_status`.
71+
Click **Run on Hotdata** after changing SQL. The editor caches the last successful result so downstream cells do not re-query on every refresh.
4972

50-
Use `hm.connection_status(client)` (or `mo.ui.hotdata_connection_status(client)`) for a small API/workspace health callout.
73+
## Native Marimo SQL cells
5174

52-
## Marimo SQL Cells
75+
Register the Hotdata engine once, then pass `engine=client` to `mo.sql`. Hotdata appears as **Hotdata** in the SQL connection picker.
5376

54-
Register the Hotdata SQL engine once during setup, then pass a `HotdataClient` to Marimo SQL cells:
77+
**Setup cell**
5578

5679
```python
80+
import marimo as mo
5781
import hotdata_marimo as hm
5882

5983
hm.register_hotdata_sql_engine()
6084
client = hm.from_env()
6185
```
6286

87+
**SQL cell**
88+
6389
```python
6490
_df = mo.sql(
6591
"""
@@ -69,46 +95,57 @@ _df = mo.sql(
6995
)
7096
```
7197

72-
The engine also exposes Hotdata catalog metadata to Marimo's data-source UI. Hotdata connections are labeled **Hotdata** in the SQL connection picker.
98+
![Marimo SQL cell with Hotdata selected in the database connections picker](docs/images/mo-sql-hotdata-connection.png)
99+
100+
## Browse tables
101+
102+
```python
103+
browser = hm.table_browser(client)
104+
return browser.ui
105+
```
106+
107+
Pick a connection, schema, and table to inspect columns. Use `browser.selected_table` in downstream cells.
73108

74-
## Two-cell pattern
109+
## Managed databases
75110

76-
Keep the editor in one cell and consume `editor.result` in another. The editor caches the last successful run so downstream cells do not re-query the API on every refresh; click **Run on Hotdata** again after you change SQL. While a query is running, a Marimo status spinner is shown.
111+
Create a Hotdata-owned catalog and load a parquet file from the notebook:
77112

78-
Marimo only shows **what you `return` from a cell**. Calling `mo.vstack(...)` or `hm.query_result(...)` without returning it produces no visible output.
113+
```python
114+
panel = hm.databases_panel(client)
115+
return panel
116+
```
79117

80-
See `examples/demo.py` for a full runnable notebook flow.
118+
Or use the lower-level writer API:
81119

82-
## Examples
120+
```python
121+
writer = hm.managed_database_writer(client)
122+
return writer.ui
123+
```
83124

84-
- `examples/demo.py` — tabbed explorer with workspace selection, connection health, recent results (selectable table), run history, and a native `mo.sql` cell.
125+
## Other helpers
85126

86-
Run locally (single-user machine):
127+
See [Supported widgets](#supported-widgets) for the full list. Quick examples:
87128

88-
```bash
89-
uv run marimo edit examples/demo.py --no-token
129+
```python
130+
return hm.connection_status(client)
131+
return hm.connections_panel(client)
132+
return hm.recent_results(client).ui
133+
return hm.run_history(client)
90134
```
91135

92-
On a **shared or networked host**, omit `--no-token` and use the access token printed in the terminal URL. Without it, anyone who can reach the Marimo port can run queries against your Hotdata workspace.
136+
## Demo notebook
93137

94-
## Layout
138+
```bash
139+
uv run marimo edit examples/demo.py --no-token
140+
```
95141

96-
This repo is intentionally thin: **API client, env helpers, and result models** live in **hotdata-runtime**; **hotdata-marimo** only adds Marimo widgets (`sql_editor`, `table_browser`, `display` for tables/status/history, `workspace_selector`). Import `HotdataClient` / `QueryResult` / `from_env` from **`hotdata_marimo`** or directly from **`hotdata_runtime`**.
142+
`examples/demo.py` combines workspace selection, catalog browsing, managed databases, query history, and a native `mo.sql` cell.
97143

98144
## Development
99145

100-
This package depends on [**hotdata-runtime**](https://github.com/hotdata-dev/hotdata-runtime) (PyPI name `hotdata-runtime`). Development uses **uv**; keep a sibling checkout at `../hotdata-runtime` so the lockfile resolves the runtime from disk (see `[tool.uv.sources]` in `pyproject.toml`).
101-
102146
```bash
103147
uv sync --locked
104148
uv run pytest
105-
marimo edit examples/demo.py --no-token
106149
```
107150

108-
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.
109-
110-
For a **publishable** `uv.lock` (CI that only clones this repo), remove `[tool.uv.sources]`, point `hotdata-runtime` at PyPI or `git+https://…`, then `uv lock`.
111-
112-
The **`[project] name`** in [hotdata-runtime](https://github.com/hotdata-dev/hotdata-runtime) `pyproject.toml` is **`hotdata-runtime`** and the import package is **`hotdata_runtime`**.
113-
114-
Use **`--no-token`** for local development so the editor does not redirect to `/auth/login` (session auth is easy to hit with a global Marimo config). For a public or shared machine, omit it and use the printed URL with an access token instead.
151+
See [hotdata-runtime](https://github.com/hotdata-dev/hotdata-runtime) for the underlying API client.
48 KB
Loading

examples/demo.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import marimo
22

3-
__generated_with = "0.23.5"
3+
__generated_with = "0.23.6"
44
app = marimo.App()
55

66

@@ -36,34 +36,43 @@ def _(hm, mo, os):
3636
def _(hm, workspace):
3737
client = workspace.client
3838
status = hm.connections_panel(client)
39+
db_writer = hm.managed_database_writer(client)
3940
recent = hm.recent_results(client, limit=20)
4041
history = hm.run_history(client, limit=10)
41-
return client, history, recent, status
42+
return client, db_writer, history, recent, status
4243

4344

4445
@app.cell
4546
def _(mo):
4647
mo.md(r"""
4748
## HotData explorer
48-
Use the tabs below to switch between workspaces, connection status, recent results, and run history.
49+
Use the tabs below to switch between workspaces, connections, managed databases,
50+
recent results, and run history.
4951
5052
On a shared or networked host, run Marimo **without** `--no-token` and open the printed URL
5153
with its access token so only you can use this notebook.
5254
""")
5355
return
5456

5557

58+
@app.cell
59+
def _(db_writer):
60+
databases_tab = db_writer.tab_ui
61+
return (databases_tab,)
62+
63+
5664
@app.cell
5765
def _(recent):
5866
recent_tab = recent.tab_ui
5967
return (recent_tab,)
6068

6169

6270
@app.cell
63-
def _(history, mo, recent_tab, status, workspace):
71+
def _(databases_tab, history, mo, recent_tab, status, workspace):
6472
mo.ui.tabs({
6573
"Workspaces": workspace.ui,
6674
"Connections": status,
75+
"Databases": databases_tab,
6776
"Recent results": recent_tab,
6877
"Run history": history,
6978
})
@@ -73,7 +82,7 @@ def _(history, mo, recent_tab, status, workspace):
7382
@app.cell
7483
def _(client, mo):
7584
_df = mo.sql(
76-
"""
85+
f"""
7786
SELECT 1 AS example_value
7887
""",
7988
engine=client

hotdata_marimo/__init__.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@
99

1010
from hotdata_runtime import HotdataClient, QueryResult, from_env
1111

12+
from hotdata_marimo.databases import (
13+
ManagedDatabaseWriter,
14+
databases_panel,
15+
managed_database_writer,
16+
)
1217
from hotdata_marimo.display import (
1318
RecentResults,
1419
connection_status,
@@ -31,20 +36,25 @@
3136
"HotdataClient",
3237
"HotdataMarimoEngine",
3338
"QueryResult",
39+
"ManagedDatabaseWriter",
3440
"RecentResults",
3541
"SqlEditor",
3642
"TableBrowser",
3743
"WorkspaceSelector",
3844
"connection_picker",
3945
"connection_status",
4046
"connections_panel",
47+
"databases_panel",
4148
"from_env",
4249
"hotdata_connection_picker",
50+
"hotdata_databases_panel",
51+
"hotdata_managed_database_writer",
4352
"hotdata_query_result",
4453
"hotdata_recent_results",
4554
"hotdata_sql_editor",
4655
"hotdata_table_browser",
4756
"hotdata_workspace_selector",
57+
"managed_database_writer",
4858
"query_result",
4959
"recent_results",
5060
"register_hotdata_sql_engine",
@@ -60,6 +70,8 @@
6070
hotdata_table_browser = table_browser
6171
hotdata_query_result = query_result
6272
hotdata_connection_picker = connection_picker
73+
hotdata_databases_panel = databases_panel
74+
hotdata_managed_database_writer = managed_database_writer
6375
hotdata_workspace_selector = workspace_selector_from_env
6476
hotdata_recent_results = recent_results
6577

@@ -73,6 +85,8 @@ def register_mo_ui_hotdata_aliases() -> None:
7385
mo.ui.hotdata_query_result = hotdata_query_result # type: ignore[attr-defined]
7486
mo.ui.hotdata_connection_status = connection_status # type: ignore[attr-defined]
7587
mo.ui.hotdata_connection_picker = hotdata_connection_picker # type: ignore[attr-defined]
88+
mo.ui.hotdata_databases_panel = hotdata_databases_panel # type: ignore[attr-defined]
89+
mo.ui.hotdata_managed_database_writer = hotdata_managed_database_writer # type: ignore[attr-defined]
7690
mo.ui.hotdata_workspace_selector = hotdata_workspace_selector # type: ignore[attr-defined]
7791
mo.ui.hotdata_recent_results = hotdata_recent_results # type: ignore[attr-defined]
7892

0 commit comments

Comments
 (0)