hotdata-jupyter 0.2.1__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.
Files changed (32) hide show
  1. hotdata_jupyter-0.2.1/.github/dependabot.yml +9 -0
  2. hotdata_jupyter-0.2.1/.github/workflows/check-release.yml +26 -0
  3. hotdata_jupyter-0.2.1/.github/workflows/ci.yml +33 -0
  4. hotdata_jupyter-0.2.1/.github/workflows/dependabot-automerge.yml +17 -0
  5. hotdata_jupyter-0.2.1/.github/workflows/publish.yml +69 -0
  6. hotdata_jupyter-0.2.1/.github/workflows/release.yml +54 -0
  7. hotdata_jupyter-0.2.1/.gitignore +23 -0
  8. hotdata_jupyter-0.2.1/.vscode/settings.json +5 -0
  9. hotdata_jupyter-0.2.1/CHANGELOG.md +43 -0
  10. hotdata_jupyter-0.2.1/PKG-INFO +113 -0
  11. hotdata_jupyter-0.2.1/README.md +101 -0
  12. hotdata_jupyter-0.2.1/RELEASING.md +43 -0
  13. hotdata_jupyter-0.2.1/examples/demo.ipynb +232 -0
  14. hotdata_jupyter-0.2.1/hotdata_jupyter/__init__.py +62 -0
  15. hotdata_jupyter-0.2.1/hotdata_jupyter/databases.py +237 -0
  16. hotdata_jupyter-0.2.1/hotdata_jupyter/display.py +35 -0
  17. hotdata_jupyter-0.2.1/hotdata_jupyter/env.py +40 -0
  18. hotdata_jupyter-0.2.1/hotdata_jupyter/magics.py +38 -0
  19. hotdata_jupyter-0.2.1/hotdata_jupyter/metadata.py +13 -0
  20. hotdata_jupyter-0.2.1/hotdata_jupyter/workspace.py +99 -0
  21. hotdata_jupyter-0.2.1/pyproject.toml +72 -0
  22. hotdata_jupyter-0.2.1/scripts/check-release.py +68 -0
  23. hotdata_jupyter-0.2.1/scripts/extract-changelog.py +36 -0
  24. hotdata_jupyter-0.2.1/scripts/release.sh +187 -0
  25. hotdata_jupyter-0.2.1/scripts/update_changelog.py +62 -0
  26. hotdata_jupyter-0.2.1/tests/conftest.py +31 -0
  27. hotdata_jupyter-0.2.1/tests/test_databases_jupyter.py +217 -0
  28. hotdata_jupyter-0.2.1/tests/test_demo_notebook.py +46 -0
  29. hotdata_jupyter-0.2.1/tests/test_hotdata_jupyter.py +205 -0
  30. hotdata_jupyter-0.2.1/tests/test_package.py +58 -0
  31. hotdata_jupyter-0.2.1/tests/test_update_changelog.py +48 -0
  32. hotdata_jupyter-0.2.1/uv.lock +1268 -0
@@ -0,0 +1,9 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: uv
4
+ directory: "/"
5
+ schedule:
6
+ interval: daily
7
+ allow:
8
+ - dependency-name: hotdata
9
+ - dependency-name: hotdata-runtime
@@ -0,0 +1,26 @@
1
+ name: Check release metadata
2
+
3
+ on:
4
+ pull_request:
5
+ paths:
6
+ - 'pyproject.toml'
7
+ - 'CHANGELOG.md'
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ jobs:
13
+ check:
14
+ name: Verify changelog matches version bump
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
18
+ with:
19
+ fetch-depth: 0
20
+
21
+ - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
22
+ with:
23
+ python-version: '3.12'
24
+
25
+ - name: Check release metadata
26
+ run: python scripts/check-release.py
@@ -0,0 +1,33 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: ["main", "master"]
6
+ pull_request:
7
+
8
+ concurrency:
9
+ group: ci-${{ github.ref }}
10
+ cancel-in-progress: true
11
+
12
+ permissions:
13
+ contents: read
14
+
15
+ jobs:
16
+ test:
17
+ name: Test (Python 3.12)
18
+ runs-on: ubuntu-latest
19
+ steps:
20
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
21
+
22
+ - uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v6
23
+ with:
24
+ enable-cache: true
25
+
26
+ - name: Set up Python
27
+ run: uv python install 3.12
28
+
29
+ - name: Install dependencies
30
+ run: uv sync --locked
31
+
32
+ - name: Test
33
+ run: uv run pytest -v
@@ -0,0 +1,17 @@
1
+ name: Dependabot auto-merge
2
+
3
+ on: pull_request
4
+
5
+ permissions:
6
+ contents: write
7
+ pull-requests: write
8
+
9
+ jobs:
10
+ auto-merge:
11
+ runs-on: ubuntu-latest
12
+ if: github.actor == 'dependabot[bot]'
13
+ steps:
14
+ - name: Enable auto-merge
15
+ run: gh pr merge --squash --auto "${{ github.event.pull_request.number }}" --repo "${{ github.repository }}"
16
+ env:
17
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -0,0 +1,69 @@
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
+ if [[ ! "$GITHUB_REF_NAME" =~ ^v[0-9] ]]; then
32
+ echo "Release tag '$GITHUB_REF_NAME' must start with 'v' followed by a digit (e.g. v1.0.0)" >&2
33
+ exit 1
34
+ fi
35
+ tag="${GITHUB_REF_NAME#v}"
36
+ pkg_version=$(python -c "import tomllib,pathlib; print(tomllib.loads(pathlib.Path('pyproject.toml').read_text())['project']['version'])")
37
+ if [ "$tag" != "$pkg_version" ]; then
38
+ echo "Release tag ($tag) does not match pyproject.toml version ($pkg_version)" >&2
39
+ exit 1
40
+ fi
41
+
42
+ - name: Build sdist and wheel
43
+ run: python -m build
44
+
45
+ - name: Check distribution metadata
46
+ run: python -m twine check --strict dist/*
47
+
48
+ - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
49
+ with:
50
+ name: dist
51
+ path: dist/
52
+
53
+ publish:
54
+ name: Publish to PyPI
55
+ needs: build
56
+ runs-on: ubuntu-latest
57
+ environment:
58
+ name: pypi
59
+ url: https://pypi.org/p/hotdata-jupyter
60
+ permissions:
61
+ id-token: write
62
+ steps:
63
+ - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5
64
+ with:
65
+ name: dist
66
+ path: dist/
67
+
68
+ - name: Publish via Trusted Publishing
69
+ uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
@@ -0,0 +1,54 @@
1
+ name: GitHub Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - 'v[0-9]*'
7
+
8
+ permissions:
9
+ contents: write
10
+
11
+ jobs:
12
+ release:
13
+ name: Create GitHub Release
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
17
+
18
+ - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
19
+ with:
20
+ python-version: '3.12'
21
+
22
+ - name: Read package metadata
23
+ id: meta
24
+ run: |
25
+ pkg_name=$(python -c "import tomllib,pathlib; print(tomllib.loads(pathlib.Path('pyproject.toml').read_text())['project']['name'])")
26
+ pkg_version="${GITHUB_REF_NAME#v}"
27
+ echo "name=${pkg_name}" >> "$GITHUB_OUTPUT"
28
+ echo "version=${pkg_version}" >> "$GITHUB_OUTPUT"
29
+
30
+ - name: Extract changelog notes
31
+ id: notes
32
+ run: |
33
+ set -euo pipefail
34
+ version="${GITHUB_REF_NAME#v}"
35
+ if [[ -f CHANGELOG.md ]]; then
36
+ body="$(python scripts/extract-changelog.py "$version")"
37
+ else
38
+ body="Release ${version}."
39
+ fi
40
+ delimiter="EOF_${RANDOM}_${RANDOM}"
41
+ {
42
+ echo "body<<${delimiter}"
43
+ echo "$body"
44
+ echo "${delimiter}"
45
+ } >> "$GITHUB_OUTPUT"
46
+
47
+ - name: Create GitHub Release
48
+ uses: softprops/action-gh-release@1e812e8210a4a8a0b23075e5795f2a4e2b2a0b7 # v2.2.2
49
+ with:
50
+ tag_name: ${{ github.ref_name }}
51
+ name: ${{ steps.meta.outputs.name }} ${{ steps.meta.outputs.version }}
52
+ body: ${{ steps.notes.outputs.body }}
53
+ generate_release_notes: false
54
+ make_latest: true
@@ -0,0 +1,23 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+
7
+ # Virtual environments
8
+ .env
9
+ .venv
10
+ env/
11
+ venv/
12
+
13
+ # Testing
14
+ .pytest_cache/
15
+ .coverage
16
+ htmlcov/
17
+
18
+ # Packaging
19
+ *.egg-info/
20
+ dist/
21
+ build/
22
+
23
+ .DS_Store
@@ -0,0 +1,5 @@
1
+ {
2
+ "python.envFile": "${workspaceFolder}/.env",
3
+ "python.defaultInterpreterPath": "${workspaceFolder}/.venv/bin/python",
4
+ "jupyter.kernels.excludePythonEnvironments": []
5
+ }
@@ -0,0 +1,43 @@
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
+ ## [Unreleased]
9
+
10
+
11
+ ## [0.2.1] - 2026-06-22
12
+
13
+ ### Changed
14
+
15
+ - Pin `hotdata-runtime>=0.3.0` to pick up its typed-error API.
16
+ - Adopt typed error handling in the managed-database widgets: the create and
17
+ load-table handlers now catch `HotdataError` to surface a clean message and
18
+ run raw SDK exceptions through `classify_sdk_error`, adding a retry hint for
19
+ transient (retryable) failures.
20
+
21
+ ## [0.2.0] - 2026-06-22
22
+
23
+ ### Changed
24
+
25
+ - Upgrade `hotdata` SDK pin to `>=0.4.1` and `hotdata-runtime` to `>=0.2.4`.
26
+ - Raise core dependency floors to `ipython>=8.18` and `ipywidgets>=8.1`.
27
+
28
+ ### Added
29
+
30
+ - Add Ruff lint/format and strict mypy configuration, plus `ruff` and `mypy`
31
+ dev dependencies.
32
+
33
+ ## [0.1.1] - 2026-06-01
34
+
35
+ ### Changed
36
+
37
+ - Release 0.1.1
38
+
39
+ ## [0.1.0] - 2026-05-19
40
+
41
+ ### Added
42
+
43
+ - Initial release with managed database widgets for Jupyter.
@@ -0,0 +1,113 @@
1
+ Metadata-Version: 2.4
2
+ Name: hotdata-jupyter
3
+ Version: 0.2.1
4
+ Summary: Jupyter integration for Hotdata runtime
5
+ License: MIT
6
+ Requires-Python: >=3.10
7
+ Requires-Dist: hotdata-runtime>=0.3.0
8
+ Requires-Dist: hotdata>=0.4.1
9
+ Requires-Dist: ipython>=8.18
10
+ Requires-Dist: ipywidgets>=8.1
11
+ Description-Content-Type: text/markdown
12
+
13
+ # hotdata-jupyter
14
+
15
+ [Jupyter](https://jupyter.org/) helpers for [Hotdata](https://hotdata.dev) — rich query display, workspace selection, managed databases, and a `%%hotdata` cell magic for running SQL directly in cells.
16
+
17
+ ## Install
18
+
19
+ ```bash
20
+ pip install hotdata-jupyter
21
+ ```
22
+
23
+ ## Authentication
24
+
25
+ Set `HOTDATA_API_KEY` in your environment. Optionally set `HOTDATA_WORKSPACE` to pin a specific workspace (the first available workspace is used if unset).
26
+
27
+ ## Quickstart
28
+
29
+ ```python
30
+ import hotdata_jupyter as hj
31
+
32
+ client = hj.from_env()
33
+ result = client.execute_sql("SELECT 1 AS ok")
34
+ hj.display_query_result(result)
35
+ ```
36
+
37
+ ## Workspace selection
38
+
39
+ When `HOTDATA_WORKSPACE` is set, the client connects to that workspace directly. If you have multiple workspaces, use the interactive picker — it renders a dropdown and updates `ws.client` when the selection changes:
40
+
41
+ ```python
42
+ ws = hj.workspace_selector_from_env()
43
+ display(ws.ui)
44
+ client = ws.client
45
+ ```
46
+
47
+ ## Running SQL
48
+
49
+ ```python
50
+ result = client.execute_sql("SELECT * FROM orders LIMIT 10")
51
+ hj.display_query_result(result)
52
+ ```
53
+
54
+ `display_query_result` renders the row count, column names, and a pandas DataFrame inline in the notebook.
55
+
56
+ ## Cell magic
57
+
58
+ Load the extension once per session, then use `%%hotdata` cells to write SQL without wrapping it in Python strings. The last active client is picked up automatically:
59
+
60
+ ```python
61
+ %load_ext hotdata_jupyter
62
+ ```
63
+
64
+ ```
65
+ %%hotdata
66
+ SELECT
67
+ product,
68
+ SUM(amount) AS total
69
+ FROM orders
70
+ GROUP BY product
71
+ ORDER BY total DESC
72
+ ```
73
+
74
+ ## Managed databases
75
+
76
+ List the managed databases in your workspace:
77
+
78
+ ```python
79
+ hj.display_managed_databases_panel(client)
80
+ ```
81
+
82
+ Create a database and load parquet files programmatically:
83
+
84
+ ```python
85
+ db = hj.create_managed_database(client, name="sales", tables=["orders"])
86
+
87
+ with open("orders.parquet", "rb") as f:
88
+ loaded = hj.load_managed_table_from_bytes(client, "sales", "orders", f.read())
89
+
90
+ print(f"Loaded {loaded.row_count} rows into {loaded.full_name}")
91
+ ```
92
+
93
+ Or use the interactive ipywidgets form:
94
+
95
+ ```python
96
+ writer = hj.managed_database_writer(client)
97
+ writer.display()
98
+ ```
99
+
100
+ ## Open the demo notebook
101
+
102
+ ```bash
103
+ jupyter lab examples/demo.ipynb
104
+ ```
105
+
106
+ The demo covers workspace selection, connection listing, schema browsing, query history, and cell magics.
107
+
108
+ ## Development
109
+
110
+ ```bash
111
+ uv sync --locked
112
+ uv run pytest
113
+ ```
@@ -0,0 +1,101 @@
1
+ # hotdata-jupyter
2
+
3
+ [Jupyter](https://jupyter.org/) helpers for [Hotdata](https://hotdata.dev) — rich query display, workspace selection, managed databases, and a `%%hotdata` cell magic for running SQL directly in cells.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pip install hotdata-jupyter
9
+ ```
10
+
11
+ ## Authentication
12
+
13
+ Set `HOTDATA_API_KEY` in your environment. Optionally set `HOTDATA_WORKSPACE` to pin a specific workspace (the first available workspace is used if unset).
14
+
15
+ ## Quickstart
16
+
17
+ ```python
18
+ import hotdata_jupyter as hj
19
+
20
+ client = hj.from_env()
21
+ result = client.execute_sql("SELECT 1 AS ok")
22
+ hj.display_query_result(result)
23
+ ```
24
+
25
+ ## Workspace selection
26
+
27
+ When `HOTDATA_WORKSPACE` is set, the client connects to that workspace directly. If you have multiple workspaces, use the interactive picker — it renders a dropdown and updates `ws.client` when the selection changes:
28
+
29
+ ```python
30
+ ws = hj.workspace_selector_from_env()
31
+ display(ws.ui)
32
+ client = ws.client
33
+ ```
34
+
35
+ ## Running SQL
36
+
37
+ ```python
38
+ result = client.execute_sql("SELECT * FROM orders LIMIT 10")
39
+ hj.display_query_result(result)
40
+ ```
41
+
42
+ `display_query_result` renders the row count, column names, and a pandas DataFrame inline in the notebook.
43
+
44
+ ## Cell magic
45
+
46
+ Load the extension once per session, then use `%%hotdata` cells to write SQL without wrapping it in Python strings. The last active client is picked up automatically:
47
+
48
+ ```python
49
+ %load_ext hotdata_jupyter
50
+ ```
51
+
52
+ ```
53
+ %%hotdata
54
+ SELECT
55
+ product,
56
+ SUM(amount) AS total
57
+ FROM orders
58
+ GROUP BY product
59
+ ORDER BY total DESC
60
+ ```
61
+
62
+ ## Managed databases
63
+
64
+ List the managed databases in your workspace:
65
+
66
+ ```python
67
+ hj.display_managed_databases_panel(client)
68
+ ```
69
+
70
+ Create a database and load parquet files programmatically:
71
+
72
+ ```python
73
+ db = hj.create_managed_database(client, name="sales", tables=["orders"])
74
+
75
+ with open("orders.parquet", "rb") as f:
76
+ loaded = hj.load_managed_table_from_bytes(client, "sales", "orders", f.read())
77
+
78
+ print(f"Loaded {loaded.row_count} rows into {loaded.full_name}")
79
+ ```
80
+
81
+ Or use the interactive ipywidgets form:
82
+
83
+ ```python
84
+ writer = hj.managed_database_writer(client)
85
+ writer.display()
86
+ ```
87
+
88
+ ## Open the demo notebook
89
+
90
+ ```bash
91
+ jupyter lab examples/demo.ipynb
92
+ ```
93
+
94
+ The demo covers workspace selection, connection listing, schema browsing, query history, and cell magics.
95
+
96
+ ## Development
97
+
98
+ ```bash
99
+ uv sync --locked
100
+ uv run pytest
101
+ ```
@@ -0,0 +1,43 @@
1
+ # Releasing
2
+
3
+ Every release uses `./scripts/release.sh`. Do not bump versions, tag, or create GitHub Releases manually.
4
+
5
+ ## One-time setup
6
+
7
+ - Install [GitHub CLI](https://cli.github.com/) (`gh`) and authenticate.
8
+ - Ensure PyPI [trusted publishing](https://docs.pypi.org/trusted-publishers/) is configured for this repo (`publish.yml` uses the `pypi` GitHub environment).
9
+
10
+ ## Release steps
11
+
12
+ 1. Add user-facing notes under `## [Unreleased]` in `CHANGELOG.md`.
13
+ 2. Prepare the release PR:
14
+
15
+ ```bash
16
+ ./scripts/release.sh prepare patch # or minor | major | 1.2.3
17
+ ```
18
+
19
+ 3. Merge the PR after CI passes (including the changelog check).
20
+ 4. Publish from a clean default branch checkout:
21
+
22
+ ```bash
23
+ git checkout main # or master for hotdata-marimo
24
+ git pull
25
+ ./scripts/release.sh publish
26
+ ```
27
+
28
+ ## What happens automatically
29
+
30
+ Pushing a `vX.Y.Z` tag triggers two workflows:
31
+
32
+ | Workflow | Purpose |
33
+ |----------|---------|
34
+ | `publish.yml` | Build wheel/sdist and publish to PyPI |
35
+ | `release.yml` | Create the GitHub Release with notes from `CHANGELOG.md` |
36
+
37
+ ## Enforcement
38
+
39
+ - **PR check** (`check-release.yml`): if `pyproject.toml` version changes, `CHANGELOG.md` must contain a matching `## [X.Y.Z]` section.
40
+ - **Tag check** (`publish.yml`): the tag (without `v`) must match `[project].version` in `pyproject.toml`.
41
+ - **Publish guard** (`release.sh publish`): refuses to tag if the changelog section is missing.
42
+
43
+ Together, these make it hard to ship a version without changelog notes or a GitHub Release.