hotdata-marimo 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hotdata_marimo-0.1.0/.github/workflows/publish.yml +70 -0
- hotdata_marimo-0.1.0/.gitignore +22 -0
- hotdata_marimo-0.1.0/CHANGELOG.md +14 -0
- hotdata_marimo-0.1.0/PKG-INFO +124 -0
- hotdata_marimo-0.1.0/README.md +114 -0
- hotdata_marimo-0.1.0/TODO.md +40 -0
- hotdata_marimo-0.1.0/examples/demo.py +85 -0
- hotdata_marimo-0.1.0/hotdata_marimo/__init__.py +80 -0
- hotdata_marimo-0.1.0/hotdata_marimo/_options.py +107 -0
- hotdata_marimo-0.1.0/hotdata_marimo/display.py +209 -0
- hotdata_marimo-0.1.0/hotdata_marimo/sql_editor.py +201 -0
- hotdata_marimo-0.1.0/hotdata_marimo/sql_engine.py +371 -0
- hotdata_marimo-0.1.0/hotdata_marimo/table_browser.py +221 -0
- hotdata_marimo-0.1.0/hotdata_marimo/workspace_selector.py +93 -0
- hotdata_marimo-0.1.0/pyproject.toml +39 -0
- hotdata_marimo-0.1.0/tests/conftest.py +35 -0
- hotdata_marimo-0.1.0/tests/test_hotdata_marimo.py +205 -0
- hotdata_marimo-0.1.0/tests/test_package.py +71 -0
- hotdata_marimo-0.1.0/uv.lock +1366 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- 'v[0-9]*'
|
|
7
|
+
|
|
8
|
+
concurrency:
|
|
9
|
+
group: pypi-publish-${{ github.ref_name }}
|
|
10
|
+
cancel-in-progress: false
|
|
11
|
+
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
build:
|
|
17
|
+
name: Build distribution
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
|
21
|
+
|
|
22
|
+
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
|
|
23
|
+
with:
|
|
24
|
+
python-version: '3.12'
|
|
25
|
+
|
|
26
|
+
- name: Install build tooling
|
|
27
|
+
run: python -m pip install --upgrade build twine
|
|
28
|
+
|
|
29
|
+
- name: Verify tag matches pyproject version
|
|
30
|
+
run: |
|
|
31
|
+
# Release tags must start with `v` followed by a PEP 440 version (e.g. v1.2.3, v1.2.3a1).
|
|
32
|
+
if [[ ! "$GITHUB_REF_NAME" =~ ^v[0-9] ]]; then
|
|
33
|
+
echo "Release tag '$GITHUB_REF_NAME' must start with 'v' followed by a digit (e.g. v1.0.0)" >&2
|
|
34
|
+
exit 1
|
|
35
|
+
fi
|
|
36
|
+
tag="${GITHUB_REF_NAME#v}"
|
|
37
|
+
pkg_version=$(python -c "import tomllib,pathlib; print(tomllib.loads(pathlib.Path('pyproject.toml').read_text())['project']['version'])")
|
|
38
|
+
if [ "$tag" != "$pkg_version" ]; then
|
|
39
|
+
echo "Release tag ($tag) does not match pyproject.toml version ($pkg_version)" >&2
|
|
40
|
+
exit 1
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
- name: Build sdist and wheel
|
|
44
|
+
run: python -m build
|
|
45
|
+
|
|
46
|
+
- name: Check distribution metadata
|
|
47
|
+
run: python -m twine check --strict dist/*
|
|
48
|
+
|
|
49
|
+
- uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
|
|
50
|
+
with:
|
|
51
|
+
name: dist
|
|
52
|
+
path: dist/
|
|
53
|
+
|
|
54
|
+
publish:
|
|
55
|
+
name: Publish to PyPI
|
|
56
|
+
needs: build
|
|
57
|
+
runs-on: ubuntu-latest
|
|
58
|
+
environment:
|
|
59
|
+
name: pypi
|
|
60
|
+
url: https://pypi.org/p/hotdata-marimo
|
|
61
|
+
permissions:
|
|
62
|
+
id-token: write
|
|
63
|
+
steps:
|
|
64
|
+
- uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5
|
|
65
|
+
with:
|
|
66
|
+
name: dist
|
|
67
|
+
path: dist/
|
|
68
|
+
|
|
69
|
+
- name: Publish via Trusted Publishing
|
|
70
|
+
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
.DS_Store
|
|
2
|
+
|
|
3
|
+
# Python
|
|
4
|
+
__pycache__/
|
|
5
|
+
*.py[cod]
|
|
6
|
+
*.pyd
|
|
7
|
+
.pytest_cache/
|
|
8
|
+
.mypy_cache/
|
|
9
|
+
.ruff_cache/
|
|
10
|
+
|
|
11
|
+
# Virtualenvs
|
|
12
|
+
.venv/
|
|
13
|
+
venv/
|
|
14
|
+
|
|
15
|
+
# Build artifacts
|
|
16
|
+
dist/
|
|
17
|
+
build/
|
|
18
|
+
*.egg-info/
|
|
19
|
+
|
|
20
|
+
# Marimo editor session (local UI state; keep notebook source as examples/*.py only)
|
|
21
|
+
**/__marimo__/session/
|
|
22
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.1.0] - 2026-05-06
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Initial release: `HotdataClient`, `sql_editor`, `table_browser`, `query_result`, and `from_env`.
|
|
13
|
+
- `connection_status` callout and `mo.ui.hotdata_*` aliases (via `register_mo_ui_hotdata_aliases` on import).
|
|
14
|
+
- SQL editor: run button with cached results, spinner while executing, and prompt to re-run after SQL edits.
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hotdata-marimo
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Marimo integration for Hotdata runtime
|
|
5
|
+
License: MIT
|
|
6
|
+
Requires-Python: >=3.10
|
|
7
|
+
Requires-Dist: hotdata-runtime>=0.1.0
|
|
8
|
+
Requires-Dist: marimo>=0.10.0
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
|
|
11
|
+
# hotdata-marimo
|
|
12
|
+
|
|
13
|
+
Marimo UI helpers for [Hotdata](https://hotdata.dev): run SQL from a notebook, browse catalog metadata, and render results as tables.
|
|
14
|
+
|
|
15
|
+
## Features
|
|
16
|
+
|
|
17
|
+
- **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.
|
|
18
|
+
- **Connection health** — show a compact status callout with API, workspace, and optional sandbox context.
|
|
19
|
+
- **Catalog browsing** — browse Hotdata connections, schemas, tables, and columns from Marimo UI controls.
|
|
20
|
+
- **SQL editor widget** — run SQL against Hotdata, cache the latest successful result, and render results in downstream reactive cells.
|
|
21
|
+
- **Native `mo.sql` engine** — register `HotdataMarimoEngine` so Marimo SQL cells can execute through a live `HotdataClient` with `engine=client`.
|
|
22
|
+
- **Result display helpers** — render query results, recent results, and run history as notebook-friendly UI.
|
|
23
|
+
- **Marimo UI aliases** — importing `hotdata_marimo` attaches helpers such as `mo.ui.hotdata_sql_editor` and `mo.ui.hotdata_table_browser` for discoverability.
|
|
24
|
+
|
|
25
|
+
## Install
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
uv pip install hotdata-marimo
|
|
29
|
+
# or: pip install hotdata-marimo
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
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`).
|
|
33
|
+
|
|
34
|
+
## Environment
|
|
35
|
+
|
|
36
|
+
| Variable | Required | Description |
|
|
37
|
+
|----------|----------|-------------|
|
|
38
|
+
| `HOTDATA_API_KEY` | Yes | API key for the Hotdata API |
|
|
39
|
+
| `HOTDATA_API_URL` | No | API base URL (default: `https://api.hotdata.dev`) |
|
|
40
|
+
| `HOTDATA_WORKSPACE` | No | Workspace id; if unset, the first active workspace is used |
|
|
41
|
+
| `HOTDATA_SANDBOX` | No | Sandbox session id, passed through to the SDK |
|
|
42
|
+
|
|
43
|
+
## Minimal notebook
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
import marimo as mo
|
|
47
|
+
import hotdata_marimo as hm
|
|
48
|
+
|
|
49
|
+
client = hm.from_env()
|
|
50
|
+
editor = hm.sql_editor(client, default_sql="SELECT 1 AS ok")
|
|
51
|
+
return editor.ui
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
return hm.query_result(editor.result)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
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`.
|
|
59
|
+
|
|
60
|
+
Use `hm.connection_status(client)` (or `mo.ui.hotdata_connection_status(client)`) for a small API/workspace health callout.
|
|
61
|
+
|
|
62
|
+
## Marimo SQL Cells
|
|
63
|
+
|
|
64
|
+
Register the Hotdata SQL engine once during setup, then pass a `HotdataClient` to Marimo SQL cells:
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
import hotdata_marimo as hm
|
|
68
|
+
|
|
69
|
+
hm.register_hotdata_sql_engine()
|
|
70
|
+
client = hm.from_env()
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
_df = mo.sql(
|
|
75
|
+
"""
|
|
76
|
+
SELECT 1 AS example_value
|
|
77
|
+
""",
|
|
78
|
+
engine=client,
|
|
79
|
+
)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
The engine also exposes Hotdata catalog metadata to Marimo's data-source UI. Hotdata connections are labeled **Hotdata** in the SQL connection picker.
|
|
83
|
+
|
|
84
|
+
## Two-cell pattern
|
|
85
|
+
|
|
86
|
+
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.
|
|
87
|
+
|
|
88
|
+
Marimo only shows **what you `return` from a cell**. Calling `mo.vstack(...)` or `hm.query_result(...)` without returning it produces no visible output.
|
|
89
|
+
|
|
90
|
+
See `examples/demo.py` for a full runnable notebook flow.
|
|
91
|
+
|
|
92
|
+
## Examples
|
|
93
|
+
|
|
94
|
+
- `examples/demo.py` — tabbed explorer with workspace selection, connection health, recent results (selectable table), run history, and a native `mo.sql` cell.
|
|
95
|
+
|
|
96
|
+
Run locally (single-user machine):
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
uv run marimo edit examples/demo.py --no-token
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
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.
|
|
103
|
+
|
|
104
|
+
## Layout
|
|
105
|
+
|
|
106
|
+
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`**.
|
|
107
|
+
|
|
108
|
+
## Development
|
|
109
|
+
|
|
110
|
+
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`).
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
uv sync --locked
|
|
114
|
+
uv run pytest
|
|
115
|
+
marimo edit examples/demo.py --no-token
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
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.
|
|
119
|
+
|
|
120
|
+
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`.
|
|
121
|
+
|
|
122
|
+
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`**.
|
|
123
|
+
|
|
124
|
+
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.
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# hotdata-marimo
|
|
2
|
+
|
|
3
|
+
Marimo UI helpers for [Hotdata](https://hotdata.dev): run SQL from a notebook, browse catalog metadata, and render results as tables.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
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.
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
uv pip install hotdata-marimo
|
|
19
|
+
# or: pip install hotdata-marimo
|
|
20
|
+
```
|
|
21
|
+
|
|
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`).
|
|
23
|
+
|
|
24
|
+
## Environment
|
|
25
|
+
|
|
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 |
|
|
32
|
+
|
|
33
|
+
## Minimal notebook
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
import marimo as mo
|
|
37
|
+
import hotdata_marimo as hm
|
|
38
|
+
|
|
39
|
+
client = hm.from_env()
|
|
40
|
+
editor = hm.sql_editor(client, default_sql="SELECT 1 AS ok")
|
|
41
|
+
return editor.ui
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
return hm.query_result(editor.result)
|
|
46
|
+
```
|
|
47
|
+
|
|
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`.
|
|
49
|
+
|
|
50
|
+
Use `hm.connection_status(client)` (or `mo.ui.hotdata_connection_status(client)`) for a small API/workspace health callout.
|
|
51
|
+
|
|
52
|
+
## Marimo SQL Cells
|
|
53
|
+
|
|
54
|
+
Register the Hotdata SQL engine once during setup, then pass a `HotdataClient` to Marimo SQL cells:
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
import hotdata_marimo as hm
|
|
58
|
+
|
|
59
|
+
hm.register_hotdata_sql_engine()
|
|
60
|
+
client = hm.from_env()
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
_df = mo.sql(
|
|
65
|
+
"""
|
|
66
|
+
SELECT 1 AS example_value
|
|
67
|
+
""",
|
|
68
|
+
engine=client,
|
|
69
|
+
)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The engine also exposes Hotdata catalog metadata to Marimo's data-source UI. Hotdata connections are labeled **Hotdata** in the SQL connection picker.
|
|
73
|
+
|
|
74
|
+
## Two-cell pattern
|
|
75
|
+
|
|
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.
|
|
77
|
+
|
|
78
|
+
Marimo only shows **what you `return` from a cell**. Calling `mo.vstack(...)` or `hm.query_result(...)` without returning it produces no visible output.
|
|
79
|
+
|
|
80
|
+
See `examples/demo.py` for a full runnable notebook flow.
|
|
81
|
+
|
|
82
|
+
## Examples
|
|
83
|
+
|
|
84
|
+
- `examples/demo.py` — tabbed explorer with workspace selection, connection health, recent results (selectable table), run history, and a native `mo.sql` cell.
|
|
85
|
+
|
|
86
|
+
Run locally (single-user machine):
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
uv run marimo edit examples/demo.py --no-token
|
|
90
|
+
```
|
|
91
|
+
|
|
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.
|
|
93
|
+
|
|
94
|
+
## Layout
|
|
95
|
+
|
|
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`**.
|
|
97
|
+
|
|
98
|
+
## Development
|
|
99
|
+
|
|
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
|
+
|
|
102
|
+
```bash
|
|
103
|
+
uv sync --locked
|
|
104
|
+
uv run pytest
|
|
105
|
+
marimo edit examples/demo.py --no-token
|
|
106
|
+
```
|
|
107
|
+
|
|
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.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# hotdata-marimo — next steps
|
|
2
|
+
|
|
3
|
+
## Near term (build on MVP)
|
|
4
|
+
|
|
5
|
+
- [x] **Workspace selector** — When `HOTDATA_WORKSPACE` is unset and multiple workspaces exist, expose `mo.ui.dropdown` (or similar) and rebuild the client when the choice changes.
|
|
6
|
+
- [x] **Connection status** — Small status chip (API reachable, workspace id, optional sandbox) using a lightweight health or `workspaces`/`connections` probe.
|
|
7
|
+
- [ ] **Query cancel** — Wire cancel to the query-run API if/when exposed in the OpenAPI client; surface a Cancel control next to Run.
|
|
8
|
+
- [x] **`mo.ui.hotdata_*` aliases** — Re-export or thin wrappers: `hotdata_sql_editor`, `hotdata_table_browser`, `hotdata_query_result`, `hotdata_connection_picker` for discoverability.
|
|
9
|
+
- [x] **Tests** — Unit tests with mocked SDK responses; optional integration tests gated on `HOTDATA_API_KEY` (mirror sdk-python patterns).
|
|
10
|
+
|
|
11
|
+
## SQL editor & execution
|
|
12
|
+
|
|
13
|
+
- [ ] **Schema-aware autocomplete** — anywidget (or CodeMirror) + Hotdata `information_schema` / column metadata for table/column suggestions.
|
|
14
|
+
- [x] **Async UX** — Progress text or spinner while polling query runs; optional configurable timeouts.
|
|
15
|
+
- [x] **Run history panel** — `QueryRunsApi.list_query_runs` + metadata (latency, touched tables, `result_id`) in a side panel or collapsible.
|
|
16
|
+
- [x] **Rerun / clear** — Explicit rerun without relying only on `run_button` semantics; optional “clear result” action.
|
|
17
|
+
|
|
18
|
+
## Results
|
|
19
|
+
|
|
20
|
+
- [x] **Pagination / LIMIT guidance** — Surface row counts vs limit; warn when result is truncated if the API exposes it.
|
|
21
|
+
- [ ] **Export** — CSV / Parquet download links or buttons (align with Hotdata results/export APIs when available).
|
|
22
|
+
- [x] **Cached result reuse** — Prefer `get_result(result_id)` over re-running identical SQL when `result_id` is known.
|
|
23
|
+
- [ ] **Materialization / cache status** — Show persistence state when the API returns it (`processing` / `ready` / errors).
|
|
24
|
+
|
|
25
|
+
## Data discovery
|
|
26
|
+
|
|
27
|
+
- [x] **Connection picker** — Filter catalog by connection; map display names to ids consistently.
|
|
28
|
+
- [x] **Schema search** — Text filter over tables/columns without loading full dropdowns for huge catalogs.
|
|
29
|
+
- [ ] **Table preview** — `LIMIT` preview query from browser selection (optional second query).
|
|
30
|
+
- [ ] **Generated queries** — Starters beyond `SELECT *`: profile/join templates from selected tables.
|
|
31
|
+
|
|
32
|
+
## App mode
|
|
33
|
+
|
|
34
|
+
- [ ] **Deployable Marimo app** — Layout recipe (sidebar explorer + editor + result) and docs for `marimo run` / WASM constraints.
|
|
35
|
+
- [ ] **Auth in apps** — Document env injection for deployed apps; avoid embedding API keys in notebooks.
|
|
36
|
+
|
|
37
|
+
## Docs & packaging
|
|
38
|
+
|
|
39
|
+
- [x] **README** — Install, env vars, minimal notebook example, and “two-cell” pattern for `editor.result` + `mo.stop`.
|
|
40
|
+
- [x] **Changelog** — Keep `CHANGELOG.md` once versions ship.
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import marimo
|
|
2
|
+
|
|
3
|
+
__generated_with = "0.23.5"
|
|
4
|
+
app = marimo.App()
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@app.cell
|
|
8
|
+
def _():
|
|
9
|
+
import os
|
|
10
|
+
|
|
11
|
+
import marimo as mo
|
|
12
|
+
|
|
13
|
+
import hotdata_marimo as hm
|
|
14
|
+
|
|
15
|
+
hm.register_hotdata_sql_engine()
|
|
16
|
+
return hm, mo, os
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@app.cell
|
|
20
|
+
def _(hm, mo, os):
|
|
21
|
+
mo.stop(
|
|
22
|
+
not os.environ.get("HOTDATA_API_KEY"),
|
|
23
|
+
mo.callout(
|
|
24
|
+
mo.md(
|
|
25
|
+
"Add **HOTDATA_API_KEY** to your environment "
|
|
26
|
+
"to run this example."
|
|
27
|
+
),
|
|
28
|
+
kind="warn",
|
|
29
|
+
),
|
|
30
|
+
)
|
|
31
|
+
workspace = hm.workspace_selector_from_env()
|
|
32
|
+
return (workspace,)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@app.cell
|
|
36
|
+
def _(hm, workspace):
|
|
37
|
+
client = workspace.client
|
|
38
|
+
status = hm.connections_panel(client)
|
|
39
|
+
recent = hm.recent_results(client, limit=20)
|
|
40
|
+
history = hm.run_history(client, limit=10)
|
|
41
|
+
return client, history, recent, status
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@app.cell
|
|
45
|
+
def _(mo):
|
|
46
|
+
mo.md(r"""
|
|
47
|
+
## HotData explorer
|
|
48
|
+
Use the tabs below to switch between workspaces, connection status, recent results, and run history.
|
|
49
|
+
|
|
50
|
+
On a shared or networked host, run Marimo **without** `--no-token` and open the printed URL
|
|
51
|
+
with its access token so only you can use this notebook.
|
|
52
|
+
""")
|
|
53
|
+
return
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@app.cell
|
|
57
|
+
def _(recent):
|
|
58
|
+
recent_tab = recent.tab_ui
|
|
59
|
+
return (recent_tab,)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@app.cell
|
|
63
|
+
def _(history, mo, recent_tab, status, workspace):
|
|
64
|
+
mo.ui.tabs({
|
|
65
|
+
"Workspaces": workspace.ui,
|
|
66
|
+
"Connections": status,
|
|
67
|
+
"Recent results": recent_tab,
|
|
68
|
+
"Run history": history,
|
|
69
|
+
})
|
|
70
|
+
return
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@app.cell
|
|
74
|
+
def _(client, mo):
|
|
75
|
+
_df = mo.sql(
|
|
76
|
+
"""
|
|
77
|
+
SELECT 1 AS example_value
|
|
78
|
+
""",
|
|
79
|
+
engine=client
|
|
80
|
+
)
|
|
81
|
+
return
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
if __name__ == "__main__":
|
|
85
|
+
app.run()
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""Marimo-native UI and helpers for Hotdata (built on hotdata-runtime)."""
|
|
2
|
+
|
|
3
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
4
|
+
|
|
5
|
+
try:
|
|
6
|
+
__version__ = version("hotdata-marimo")
|
|
7
|
+
except PackageNotFoundError:
|
|
8
|
+
__version__ = "0.0.0+unknown"
|
|
9
|
+
|
|
10
|
+
from hotdata_runtime import HotdataClient, QueryResult, from_env
|
|
11
|
+
|
|
12
|
+
from hotdata_marimo.display import (
|
|
13
|
+
RecentResults,
|
|
14
|
+
connection_status,
|
|
15
|
+
connections_panel,
|
|
16
|
+
query_result,
|
|
17
|
+
recent_results,
|
|
18
|
+
run_history,
|
|
19
|
+
)
|
|
20
|
+
from hotdata_marimo.sql_engine import (
|
|
21
|
+
HotdataMarimoEngine,
|
|
22
|
+
register_hotdata_sql_engine,
|
|
23
|
+
unregister_hotdata_sql_engine,
|
|
24
|
+
)
|
|
25
|
+
from hotdata_marimo.sql_editor import SqlEditor, sql_editor
|
|
26
|
+
from hotdata_marimo.table_browser import TableBrowser, connection_picker, table_browser
|
|
27
|
+
from hotdata_marimo.workspace_selector import WorkspaceSelector, workspace_selector_from_env
|
|
28
|
+
|
|
29
|
+
__all__ = [
|
|
30
|
+
"__version__",
|
|
31
|
+
"HotdataClient",
|
|
32
|
+
"HotdataMarimoEngine",
|
|
33
|
+
"QueryResult",
|
|
34
|
+
"RecentResults",
|
|
35
|
+
"SqlEditor",
|
|
36
|
+
"TableBrowser",
|
|
37
|
+
"WorkspaceSelector",
|
|
38
|
+
"connection_picker",
|
|
39
|
+
"connection_status",
|
|
40
|
+
"connections_panel",
|
|
41
|
+
"from_env",
|
|
42
|
+
"hotdata_connection_picker",
|
|
43
|
+
"hotdata_query_result",
|
|
44
|
+
"hotdata_recent_results",
|
|
45
|
+
"hotdata_sql_editor",
|
|
46
|
+
"hotdata_table_browser",
|
|
47
|
+
"hotdata_workspace_selector",
|
|
48
|
+
"query_result",
|
|
49
|
+
"recent_results",
|
|
50
|
+
"register_hotdata_sql_engine",
|
|
51
|
+
"register_mo_ui_hotdata_aliases",
|
|
52
|
+
"run_history",
|
|
53
|
+
"sql_editor",
|
|
54
|
+
"table_browser",
|
|
55
|
+
"unregister_hotdata_sql_engine",
|
|
56
|
+
"workspace_selector_from_env",
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
hotdata_sql_editor = sql_editor
|
|
60
|
+
hotdata_table_browser = table_browser
|
|
61
|
+
hotdata_query_result = query_result
|
|
62
|
+
hotdata_connection_picker = connection_picker
|
|
63
|
+
hotdata_workspace_selector = workspace_selector_from_env
|
|
64
|
+
hotdata_recent_results = recent_results
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def register_mo_ui_hotdata_aliases() -> None:
|
|
68
|
+
"""Attach Hotdata helpers to ``marimo.ui`` for discoverability (``mo.ui.hotdata_*``)."""
|
|
69
|
+
import marimo as mo
|
|
70
|
+
|
|
71
|
+
mo.ui.hotdata_sql_editor = hotdata_sql_editor # type: ignore[attr-defined]
|
|
72
|
+
mo.ui.hotdata_table_browser = hotdata_table_browser # type: ignore[attr-defined]
|
|
73
|
+
mo.ui.hotdata_query_result = hotdata_query_result # type: ignore[attr-defined]
|
|
74
|
+
mo.ui.hotdata_connection_status = connection_status # type: ignore[attr-defined]
|
|
75
|
+
mo.ui.hotdata_connection_picker = hotdata_connection_picker # type: ignore[attr-defined]
|
|
76
|
+
mo.ui.hotdata_workspace_selector = hotdata_workspace_selector # type: ignore[attr-defined]
|
|
77
|
+
mo.ui.hotdata_recent_results = hotdata_recent_results # type: ignore[attr-defined]
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
register_mo_ui_hotdata_aliases()
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"""Shared dropdown option helpers for Marimo UI widgets."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Callable
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
import marimo as mo
|
|
9
|
+
|
|
10
|
+
from hotdata_runtime import HotdataClient
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def unique_label_options(
|
|
14
|
+
pairs: list[tuple[str, str]],
|
|
15
|
+
*,
|
|
16
|
+
disambiguate: Callable[[str, str, int], str] | None = None,
|
|
17
|
+
) -> dict[str, str]:
|
|
18
|
+
"""Build a label→value map, suffixing repeated labels when needed."""
|
|
19
|
+
counts: dict[str, int] = {}
|
|
20
|
+
options: dict[str, str] = {}
|
|
21
|
+
for label, value in pairs:
|
|
22
|
+
count = counts.get(label, 0)
|
|
23
|
+
counts[label] = count + 1
|
|
24
|
+
if count == 0:
|
|
25
|
+
key = label
|
|
26
|
+
elif disambiguate is not None:
|
|
27
|
+
key = disambiguate(label, value, count)
|
|
28
|
+
else:
|
|
29
|
+
key = f"{label} ({count + 1})"
|
|
30
|
+
options[key] = value
|
|
31
|
+
return options
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def empty_dropdown(
|
|
35
|
+
*,
|
|
36
|
+
label: str,
|
|
37
|
+
message: str,
|
|
38
|
+
full_width: bool = True,
|
|
39
|
+
):
|
|
40
|
+
return mo.ui.dropdown(
|
|
41
|
+
options={message: ""},
|
|
42
|
+
label=label,
|
|
43
|
+
full_width=full_width,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def connection_options(conns: list[Any]) -> dict[str, str]:
|
|
48
|
+
pairs = [(str(c.name), str(c.id)) for c in conns]
|
|
49
|
+
return unique_label_options(
|
|
50
|
+
pairs,
|
|
51
|
+
disambiguate=lambda label, value, count: f"{label} ({value})",
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def connection_picker_from_connections(
|
|
56
|
+
conns: list[Any],
|
|
57
|
+
*,
|
|
58
|
+
label: str = "Connection",
|
|
59
|
+
full_width: bool = True,
|
|
60
|
+
):
|
|
61
|
+
if not conns:
|
|
62
|
+
return empty_dropdown(
|
|
63
|
+
label=label,
|
|
64
|
+
message="(no connections)",
|
|
65
|
+
full_width=full_width,
|
|
66
|
+
)
|
|
67
|
+
return mo.ui.dropdown(
|
|
68
|
+
options=connection_options(conns),
|
|
69
|
+
label=label,
|
|
70
|
+
full_width=full_width,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def connection_picker(
|
|
75
|
+
client: HotdataClient,
|
|
76
|
+
*,
|
|
77
|
+
label: str = "Connection",
|
|
78
|
+
full_width: bool = True,
|
|
79
|
+
):
|
|
80
|
+
conns = client.connections().list_connections().connections
|
|
81
|
+
return connection_picker_from_connections(
|
|
82
|
+
conns,
|
|
83
|
+
label=label,
|
|
84
|
+
full_width=full_width,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def resolve_connection_picker(
|
|
89
|
+
client: HotdataClient,
|
|
90
|
+
*,
|
|
91
|
+
label: str = "Connection",
|
|
92
|
+
full_width: bool = True,
|
|
93
|
+
) -> tuple[Any | None, str | None]:
|
|
94
|
+
"""Return ``(dropdown_or_none, implicit_connection_id)`` for table browsers."""
|
|
95
|
+
conns = client.connections().list_connections().connections
|
|
96
|
+
if not conns:
|
|
97
|
+
return None, ""
|
|
98
|
+
if len(conns) == 1:
|
|
99
|
+
return None, conns[0].id
|
|
100
|
+
return (
|
|
101
|
+
connection_picker_from_connections(
|
|
102
|
+
conns,
|
|
103
|
+
label=label,
|
|
104
|
+
full_width=full_width,
|
|
105
|
+
),
|
|
106
|
+
None,
|
|
107
|
+
)
|