-
Notifications
You must be signed in to change notification settings - Fork 0
Feat/workspace health #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,16 +1,19 @@ | ||
| # hotdata-core-notebook | ||
| # hotdata-runtime | ||
|
|
||
| Shared **Hotdata** client and domain types for notebook UIs (Marimo, Jupyter, etc.). UI frameworks depend on this package; they do not belong here. | ||
| Shared runtime primitives for Hotdata integrations: workspace/session semantics, execution context, query state, run history, and replayable result handles. Framework packages (Marimo, Jupyter, Streamlit, LangGraph) depend on this package. | ||
|
|
||
| Install: | ||
|
|
||
| ```bash | ||
| pip install hotdata-core-notebook | ||
| uv pip install hotdata-runtime | ||
| # or: pip install hotdata-runtime | ||
| ``` | ||
|
|
||
| Development: | ||
| Development (uses **uv**; creates `.venv/` in this repo): | ||
|
|
||
| ```bash | ||
| pip install -e ".[dev]" | ||
| pytest | ||
| uv sync --locked | ||
| uv run pytest | ||
| ``` | ||
|
|
||
| `uv.lock` is checked in so CI can run `uv sync --locked`. The default **dev** group (pytest) is enabled via `[tool.uv] default-groups`. |
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| """Hotdata runtime primitives for notebook and app integrations.""" | ||
|
|
||
| from importlib.metadata import PackageNotFoundError, version | ||
|
|
||
| from hotdata_runtime.client import HotdataClient, from_env | ||
| from hotdata_runtime.env import ( | ||
| default_api_key, | ||
| default_host, | ||
| default_session_id, | ||
| explicit_workspace_id, | ||
| list_workspaces, | ||
| normalize_host, | ||
| pick_workspace, | ||
| ) | ||
| from hotdata_runtime.health import workspace_health_lines | ||
| from hotdata_runtime.result import QueryResult | ||
|
|
||
| try: | ||
| __version__ = version("hotdata-runtime") | ||
| except PackageNotFoundError: | ||
| __version__ = "0.0.0+unknown" | ||
|
|
||
| __all__ = [ | ||
| "__version__", | ||
| "HotdataClient", | ||
| "QueryResult", | ||
| "workspace_health_lines", | ||
| "default_api_key", | ||
| "default_host", | ||
| "default_session_id", | ||
| "explicit_workspace_id", | ||
| "from_env", | ||
| "list_workspaces", | ||
| "normalize_host", | ||
| "pick_workspace", | ||
| ] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| from __future__ import annotations | ||
|
|
||
| from hotdata.exceptions import ApiException | ||
|
|
||
| from hotdata_runtime.client import HotdataClient | ||
|
|
||
|
|
||
| def workspace_health_lines(client: HotdataClient) -> tuple[bool, list[str]]: | ||
| """Return ``(ok, parts)`` where ``parts`` are short markdown fragments. | ||
|
|
||
| On failure, ``ok`` is False and ``parts`` is a single-element list with the error text. | ||
| """ | ||
| try: | ||
| listing = client.connections().list_connections() | ||
| n = len(listing.connections) | ||
| lines = [ | ||
| "**API** reachable", | ||
| f"**workspace** `{client.workspace_id}`", | ||
| f"**connections** {n}", | ||
| ] | ||
| if client.session_id: | ||
| lines.append(f"**sandbox** `{client.session_id}`") | ||
| return True, lines | ||
| except ApiException as e: | ||
| return False, [e.reason or str(e)] | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: only |
||
| except Exception as e: | ||
| return False, [str(e)] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| from __future__ import annotations | ||
|
|
||
| from unittest.mock import patch | ||
|
|
||
| from hotdata.exceptions import ApiException | ||
|
|
||
| from hotdata_runtime.client import HotdataClient | ||
| from hotdata_runtime.health import workspace_health_lines | ||
|
|
||
|
|
||
| def test_workspace_health_ok(): | ||
| client = HotdataClient("k", "ws", host="https://api.hotdata.dev") | ||
| listing = type("L", (), {"connections": [object()]})() | ||
|
|
||
| class FakeConnectionsApi: | ||
| def list_connections(self): | ||
| return listing | ||
|
|
||
| with patch.object(client, "connections", return_value=FakeConnectionsApi()): | ||
| ok, parts = workspace_health_lines(client) | ||
| assert ok is True | ||
| assert any("reachable" in p for p in parts) | ||
|
|
||
|
|
||
| def test_workspace_health_ok_includes_sandbox_when_session_set(): | ||
| client = HotdataClient( | ||
| "k", "ws", host="https://api.hotdata.dev", session_id="sb_test" | ||
| ) | ||
| listing = type("L", (), {"connections": [object()]})() | ||
|
|
||
| class FakeConnectionsApi: | ||
| def list_connections(self): | ||
| return listing | ||
|
|
||
| with patch.object(client, "connections", return_value=FakeConnectionsApi()): | ||
| ok, parts = workspace_health_lines(client) | ||
| assert ok is True | ||
| assert any("sandbox" in p and "sb_test" in p for p in parts) | ||
|
|
||
|
|
||
| def test_workspace_health_api_error(): | ||
| client = HotdataClient("k", "ws", host="https://api.hotdata.dev") | ||
|
|
||
| class Boom: | ||
| def list_connections(self): | ||
| raise ApiException(status=500, reason="nope") | ||
|
|
||
| with patch.object(client, "connections", return_value=Boom()): | ||
| ok, parts = workspace_health_lines(client) | ||
| assert ok is False | ||
| assert parts == ["nope"] | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: no test exercises the |
||
|
|
||
|
|
||
| def test_workspace_health_non_api_error(): | ||
| client = HotdataClient("k", "ws", host="https://api.hotdata.dev") | ||
|
|
||
| class Boom: | ||
| def list_connections(self): | ||
| raise OSError("connection refused") | ||
|
|
||
| with patch.object(client, "connections", return_value=Boom()): | ||
| ok, parts = workspace_health_lines(client) | ||
| assert ok is False | ||
| assert parts == ["connection refused"] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| from importlib.metadata import version as dist_version | ||
|
|
||
| from packaging.version import Version | ||
|
|
||
| import hotdata_runtime as hr | ||
|
|
||
|
|
||
| def test_version_is_valid_pep440(): | ||
| Version(hr.__version__) | ||
|
|
||
|
|
||
| def test_version_matches_distribution_metadata(): | ||
| assert dist_version("hotdata-runtime") == hr.__version__ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
super nit: imports aren't alphabetical —
envis afterhealth. A ruff/isort run would reorder these toclient,env,health,result. (not blocking)