Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ jobs:
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/hotdata-runtime
url: https://pypi.org/p/hotdata-framework
permissions:
id-token: write
steps:
Expand Down
9 changes: 7 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Changed

- **Renamed the distribution from `hotdata-runtime` to `hotdata-framework`** and the import package from `hotdata_runtime` to `hotdata_framework`. Consumers should depend on `hotdata-framework` and use `import hotdata_framework`. The GitHub repository is now `sdk-python-framework`.
- Added PyPI classifiers, keywords, and an updated description identifying the project as a Python framework.

## [0.3.0] - 2026-06-22

### Added

- Adopt the `hotdata` 0.4.1 SDK surface.
- New typed error-handling public API: `HotdataError`, `HotdataTerminalError`, `HotdataTransientError`, and `classify_sdk_error` (`hotdata_runtime/errors.py`).
- `ManagedDatabaseClient` for managed database operations (`hotdata_runtime/managed_client.py`).
- New typed error-handling public API: `HotdataError`, `HotdataTerminalError`, `HotdataTransientError`, and `classify_sdk_error` (`hotdata_framework/errors.py`).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

super nit: (not blocking) The distribution rename hotdata-runtimehotdata-framework breaks pip install hotdata-runtime for existing consumers, but the [Unreleased] section above is empty. Worth a ### Changed entry documenting the rename so downstream packages know to update their dependency name.

- `ManagedDatabaseClient` for managed database operations (`hotdata_framework/managed_client.py`).
- `py.typed` marker so downstream consumers pick up inline type information.

### Changed
Expand Down
8 changes: 4 additions & 4 deletions CONTRACT.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# hotdata-runtime Contract
# hotdata-framework Contract

`hotdata-runtime` is the framework-agnostic runtime contract for Hotdata integrations.
`hotdata-framework` is the framework-agnostic runtime contract for Hotdata integrations.

## Scope

Expand Down Expand Up @@ -36,7 +36,7 @@ The supported import surface is:
- `DEFAULT_SCHEMA`
- `is_parquet_path`

Adapters should import from `hotdata_runtime` and treat this surface as the stable API.
Adapters should import from `hotdata_framework` and treat this surface as the stable API.

## Semantic Guarantees

Expand Down Expand Up @@ -93,7 +93,7 @@ They should not duplicate runtime env/workspace/query semantics.

## Runtime Non-Goals

`hotdata-runtime` does not define framework UI primitives and does not require framework dependencies.
`hotdata-framework` does not define framework UI primitives and does not require framework dependencies.

## Versioning Policy

Expand Down
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# hotdata-runtime
# hotdata-framework

**A Python framework for building Hotdata integrations.**

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.

Expand All @@ -19,8 +21,8 @@ Runtime boundary and guarantees are defined in `CONTRACT.md`.
Install:

```bash
uv pip install hotdata-runtime
# or: pip install hotdata-runtime
uv pip install hotdata-framework
# or: pip install hotdata-framework
```

Example:
Expand Down
4 changes: 2 additions & 2 deletions examples/basic_usage.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Basic hotdata-runtime usage."""
"""Basic hotdata-framework usage."""

from hotdata_runtime import from_env
from hotdata_framework import from_env


def main() -> None:
Expand Down
16 changes: 8 additions & 8 deletions hotdata_runtime/__init__.py → hotdata_framework/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,20 @@

from importlib.metadata import PackageNotFoundError, version

from hotdata_runtime.client import (
from hotdata_framework.client import (
HotdataClient,
ResultSummary,
RunHistoryItem,
from_env,
)
from hotdata_runtime.databases import (
from hotdata_framework.databases import (
DEFAULT_SCHEMA,
LoadManagedTableResult,
ManagedDatabase,
ManagedTable,
is_parquet_path,
)
from hotdata_runtime.env import (
from hotdata_framework.env import (
WorkspaceSelection,
default_api_key,
default_host,
Expand All @@ -26,18 +26,18 @@
pick_workspace,
resolve_workspace_selection,
)
from hotdata_runtime.errors import (
from hotdata_framework.errors import (
HotdataError,
HotdataTerminalError,
HotdataTransientError,
classify_sdk_error,
)
from hotdata_runtime.health import workspace_health_lines
from hotdata_runtime.managed_client import ManagedDatabaseClient
from hotdata_runtime.result import QueryResult
from hotdata_framework.health import workspace_health_lines
from hotdata_framework.managed_client import ManagedDatabaseClient
from hotdata_framework.result import QueryResult

try:
__version__ = version("hotdata-runtime")
__version__ = version("hotdata-framework")
except PackageNotFoundError:
__version__ = "0.0.0+unknown"

Expand Down
8 changes: 4 additions & 4 deletions hotdata_runtime/client.py → hotdata_framework/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
from urllib3.exceptions import HTTPError as Urllib3HTTPError
from urllib3.exceptions import ProtocolError

from hotdata_runtime.databases import (
from hotdata_framework.databases import (
DEFAULT_SCHEMA,
LoadManagedTableResult,
ManagedDatabase,
Expand All @@ -34,15 +34,15 @@
is_parquet_path,
managed_database_from_detail,
)
from hotdata_runtime.env import (
from hotdata_framework.env import (
default_api_key,
default_host,
default_session_id,
normalize_host,
pick_workspace,
)
from hotdata_runtime.http import default_http_retries
from hotdata_runtime.result import QueryResult
from hotdata_framework.http import default_http_retries
from hotdata_framework.result import QueryResult

_TERMINAL = frozenset({"succeeded", "failed", "cancelled"})
_RESULT_FAILURE = frozenset({"failed", "cancelled"})
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion hotdata_runtime/health.py → hotdata_framework/health.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from hotdata.exceptions import ApiException

from hotdata_runtime.client import HotdataClient
from hotdata_framework.client import HotdataClient


def workspace_health_lines(client: HotdataClient) -> tuple[bool, list[str]]:
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@
from hotdata.models.query_request import QueryRequest
from hotdata.models.query_response import QueryResponse

from hotdata_runtime.client import HotdataClient as RuntimeClient
from hotdata_runtime.databases import LoadManagedTableResult, ManagedDatabase
from hotdata_runtime.errors import (
from hotdata_framework.client import HotdataClient as RuntimeClient
from hotdata_framework.databases import LoadManagedTableResult, ManagedDatabase
from hotdata_framework.errors import (
HotdataTransientError,
classify_sdk_error,
)
Expand All @@ -31,7 +31,7 @@


class ManagedDatabaseClient:
"""Managed-database client with bounded retries over hotdata-runtime.
"""Managed-database client with bounded retries over hotdata-framework.

This is the shared client used by Hotdata adapter packages (Airflow,
dlt, etc.). It wraps the lower-level RuntimeClient with retry logic,
Expand Down
File renamed without changes.
File renamed without changes.
29 changes: 25 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,39 @@ requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "hotdata-runtime"
name = "hotdata-framework"
version = "0.3.0"
description = "Workspace/session runtime primitives for Hotdata integrations"
description = "Python framework for building Hotdata integrations: workspace/session runtime, query execution, and managed databases"
readme = "README.md"
requires-python = ">=3.10"
license = { text = "MIT" }
keywords = ["hotdata", "python", "framework", "sql", "data", "analytics", "pandas", "arrow"]
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Topic :: Software Development :: Libraries :: Application Frameworks",
"Topic :: Software Development :: Libraries :: Python Modules",
"Typing :: Typed",
]
dependencies = [
"hotdata>=0.4.1",
"pandas>=2.0",
"pyarrow>=14.0",
]

[project.urls]
Homepage = "https://www.hotdata.dev"
Documentation = "https://www.hotdata.dev/docs"
Repository = "https://github.com/hotdata-dev/sdk-python-framework"

[dependency-groups]
dev = [
"packaging>=23",
Expand All @@ -27,7 +48,7 @@ dev = [
default-groups = ["dev"]

[tool.hatch.build.targets.wheel]
packages = ["hotdata_runtime"]
packages = ["hotdata_framework"]

[tool.pytest.ini_options]
testpaths = ["tests"]
Expand All @@ -36,7 +57,7 @@ testpaths = ["tests"]
python_version = "3.10"
strict = true
files = [
"hotdata_runtime",
"hotdata_framework",
"tests",
]
namespace_packages = true
Expand Down
2 changes: 1 addition & 1 deletion scripts/publish-workflow.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
# Generate publish.yml for a package. Usage: publish-workflow.sh hotdata-runtime
# Generate publish.yml for a package. Usage: publish-workflow.sh hotdata-framework
set -euo pipefail
pkg="${1:?package name}"
cat <<EOF
Expand Down
14 changes: 7 additions & 7 deletions tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@

import pytest

from hotdata_runtime.client import HotdataClient
from hotdata_runtime.env import normalize_host, pick_workspace, resolve_workspace_selection
from hotdata_framework.client import HotdataClient
from hotdata_framework.env import normalize_host, pick_workspace, resolve_workspace_selection


def _clear_workspace_env(monkeypatch: pytest.MonkeyPatch) -> None:
Expand Down Expand Up @@ -37,7 +37,7 @@ def test_resolve_workspace_selection_prefers_env_without_listing(
monkeypatch: pytest.MonkeyPatch,
):
monkeypatch.setenv("HOTDATA_WORKSPACE", "ws_explicit")
with patch("hotdata_runtime.env.list_workspaces") as listing:
with patch("hotdata_framework.env.list_workspaces") as listing:
resolved = resolve_workspace_selection("k", "https://api.hotdata.dev", None)
listing.assert_not_called()
assert resolved.workspace_id == "ws_explicit"
Expand All @@ -55,7 +55,7 @@ def test_pick_workspace_chooses_first_active(monkeypatch: pytest.MonkeyPatch):
]
listing = SimpleNamespace(workspaces=items)

with patch("hotdata_runtime.env.WorkspacesApi") as Api:
with patch("hotdata_framework.env.WorkspacesApi") as Api:
Api.return_value.list_workspaces.return_value = listing
assert pick_workspace("k", "https://api.hotdata.dev", None) == "ws_2"

Expand All @@ -69,7 +69,7 @@ def test_pick_workspace_falls_back_to_first(monkeypatch: pytest.MonkeyPatch):
]
listing = SimpleNamespace(workspaces=items)

with patch("hotdata_runtime.env.WorkspacesApi") as Api:
with patch("hotdata_framework.env.WorkspacesApi") as Api:
Api.return_value.list_workspaces.return_value = listing
assert pick_workspace("k", "https://api.hotdata.dev", None) == "ws_1"

Expand All @@ -81,7 +81,7 @@ def test_resolve_workspace_selection_source_first(monkeypatch: pytest.MonkeyPatc
SimpleNamespace(public_id="ws_2", active=False),
]
listing = SimpleNamespace(workspaces=items)
with patch("hotdata_runtime.env.WorkspacesApi") as Api:
with patch("hotdata_framework.env.WorkspacesApi") as Api:
Api.return_value.list_workspaces.return_value = listing
resolved = resolve_workspace_selection("k", "https://api.hotdata.dev", None)
assert resolved.workspace_id == "ws_1"
Expand All @@ -100,7 +100,7 @@ def test_resolve_workspace_selection_returns_workspaces_and_source(
]
listing = SimpleNamespace(workspaces=items)

with patch("hotdata_runtime.env.WorkspacesApi") as Api:
with patch("hotdata_framework.env.WorkspacesApi") as Api:
Api.return_value.list_workspaces.return_value = listing
resolved = resolve_workspace_selection("k", "https://api.hotdata.dev", None)
assert resolved.workspace_id == "ws_2"
Expand Down
6 changes: 3 additions & 3 deletions tests/test_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
from dataclasses import fields
from unittest.mock import patch

import hotdata_runtime as hr
from hotdata_runtime.client import HotdataClient
from hotdata_runtime.result import QueryResult
import hotdata_framework as hr
from hotdata_framework.client import HotdataClient
from hotdata_framework.result import QueryResult


def test_public_exports_contract():
Expand Down
4 changes: 2 additions & 2 deletions tests/test_databases.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
import pytest
from hotdata.exceptions import ApiException

from hotdata_runtime.client import HotdataClient
from hotdata_runtime.databases import (
from hotdata_framework.client import HotdataClient
from hotdata_framework.databases import (
is_parquet_path,
managed_database_from_detail,
)
Expand Down
4 changes: 2 additions & 2 deletions tests/test_health.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

from hotdata.exceptions import ApiException

from hotdata_runtime.client import HotdataClient
from hotdata_runtime.health import workspace_health_lines
from hotdata_framework.client import HotdataClient
from hotdata_framework.health import workspace_health_lines


def test_workspace_health_ok():
Expand Down
2 changes: 1 addition & 1 deletion tests/test_result.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from hotdata_runtime.result import QueryResult
from hotdata_framework.result import QueryResult


def _result() -> QueryResult:
Expand Down
4 changes: 2 additions & 2 deletions tests/test_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

from packaging.version import Version

import hotdata_runtime as hr
import hotdata_framework as hr


def test_version_is_valid_pep440():
Version(hr.__version__)


def test_version_matches_distribution_metadata():
assert dist_version("hotdata-runtime") == hr.__version__
assert dist_version("hotdata-framework") == hr.__version__
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading