hotdata-runtime 0.2.0__tar.gz → 0.2.2__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_runtime-0.2.0 → hotdata_runtime-0.2.2}/.github/workflows/release.yml +1 -1
- {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/CHANGELOG.md +14 -0
- {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/PKG-INFO +2 -2
- {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/hotdata_runtime/client.py +18 -6
- {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/hotdata_runtime/databases.py +1 -1
- {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/pyproject.toml +2 -2
- {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/tests/test_client.py +61 -0
- {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/tests/test_databases.py +2 -2
- {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/uv.lock +5 -5
- {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/.github/workflows/check-release.yml +0 -0
- {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/.github/workflows/ci.yml +0 -0
- {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/.github/workflows/publish.yml +0 -0
- {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/.gitignore +0 -0
- {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/CONTRACT.md +0 -0
- {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/README.md +0 -0
- {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/RELEASING.md +0 -0
- {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/examples/basic_usage.py +0 -0
- {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/hotdata_runtime/__init__.py +0 -0
- {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/hotdata_runtime/env.py +0 -0
- {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/hotdata_runtime/health.py +0 -0
- {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/hotdata_runtime/http.py +0 -0
- {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/hotdata_runtime/result.py +0 -0
- {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/scripts/check-release.py +0 -0
- {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/scripts/extract-changelog.py +0 -0
- {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/scripts/publish-workflow.sh +0 -0
- {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/scripts/release.sh +0 -0
- {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/scripts/update_changelog.py +0 -0
- {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/tests/test_contract.py +0 -0
- {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/tests/test_health.py +0 -0
- {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/tests/test_result.py +0 -0
- {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/tests/test_update_changelog.py +0 -0
- {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/tests/test_version.py +0 -0
|
@@ -45,7 +45,7 @@ jobs:
|
|
|
45
45
|
} >> "$GITHUB_OUTPUT"
|
|
46
46
|
|
|
47
47
|
- name: Create GitHub Release
|
|
48
|
-
uses: softprops/action-gh-release@
|
|
48
|
+
uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 # v2.2.2
|
|
49
49
|
with:
|
|
50
50
|
tag_name: ${{ github.ref_name }}
|
|
51
51
|
name: ${{ steps.meta.outputs.name }} ${{ steps.meta.outputs.version }}
|
|
@@ -7,6 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
## [0.2.2] - 2026-05-27
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- Release 0.2.2
|
|
17
|
+
|
|
18
|
+
## [0.2.1] - 2026-05-24
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- `execute_sql` accepts an optional `database` keyword argument. When provided, the database name is resolved to an ID and sent as the `X-Database-Id` header so SQL can reference managed database tables as `"default"."<schema>"."<table>"`. Behaviour is unchanged when `database` is omitted.
|
|
23
|
+
|
|
10
24
|
## [0.2.0] - 2026-05-24
|
|
11
25
|
|
|
12
26
|
### Changed
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hotdata-runtime
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Workspace/session runtime primitives for Hotdata integrations
|
|
5
5
|
License: MIT
|
|
6
6
|
Requires-Python: >=3.10
|
|
7
|
-
Requires-Dist: hotdata>=0.2.
|
|
7
|
+
Requires-Dist: hotdata>=0.2.4
|
|
8
8
|
Requires-Dist: pandas>=2.0
|
|
9
9
|
Description-Content-Type: text/markdown
|
|
10
10
|
|
|
@@ -180,7 +180,7 @@ class HotdataClient:
|
|
|
180
180
|
listing = self._databases_api().list_databases()
|
|
181
181
|
match_id: str | None = None
|
|
182
182
|
for db in listing.databases:
|
|
183
|
-
if db.
|
|
183
|
+
if db.name == name_or_id:
|
|
184
184
|
match_id = db.id
|
|
185
185
|
break
|
|
186
186
|
if match_id is None:
|
|
@@ -208,7 +208,7 @@ class HotdataClient:
|
|
|
208
208
|
)
|
|
209
209
|
]
|
|
210
210
|
request = CreateDatabaseRequest(
|
|
211
|
-
|
|
211
|
+
name=description,
|
|
212
212
|
schemas=schemas,
|
|
213
213
|
expires_at=expires_at,
|
|
214
214
|
)
|
|
@@ -475,11 +475,20 @@ class HotdataClient:
|
|
|
475
475
|
f"(last status: {getattr(last, 'status', None)})"
|
|
476
476
|
)
|
|
477
477
|
|
|
478
|
-
def execute_sql(self, sql: str) -> QueryResult:
|
|
478
|
+
def execute_sql(self, sql: str, *, database: str | None = None) -> QueryResult:
|
|
479
|
+
"""Execute SQL and return a :class:`QueryResult`.
|
|
480
|
+
|
|
481
|
+
Pass ``database`` to scope the query to a managed database. The name
|
|
482
|
+
is resolved to a database ID once before the retry loop, and the
|
|
483
|
+
``X-Database-Id`` header is sent with every attempt. Inside a managed
|
|
484
|
+
database the built-in catalog is always ``"default"``, so table
|
|
485
|
+
references should use ``"default"."<schema>"."<table>"``.
|
|
486
|
+
"""
|
|
487
|
+
database_id = self.resolve_managed_database(database).id if database else None
|
|
479
488
|
last_err: BaseException | None = None
|
|
480
489
|
for attempt in range(3):
|
|
481
490
|
try:
|
|
482
|
-
return self._execute_sql_once(sql)
|
|
491
|
+
return self._execute_sql_once(sql, database_id=database_id)
|
|
483
492
|
except (ProtocolError, ConnectionResetError, Urllib3HTTPError) as e:
|
|
484
493
|
last_err = e
|
|
485
494
|
if attempt == 2:
|
|
@@ -487,10 +496,13 @@ class HotdataClient:
|
|
|
487
496
|
time.sleep(0.2 * (2**attempt))
|
|
488
497
|
raise last_err # pragma: no cover
|
|
489
498
|
|
|
490
|
-
def _execute_sql_once(self, sql: str) -> QueryResult:
|
|
499
|
+
def _execute_sql_once(self, sql: str, *, database_id: str | None = None) -> QueryResult:
|
|
491
500
|
q = self._query_api()
|
|
492
501
|
try:
|
|
493
|
-
|
|
502
|
+
if database_id:
|
|
503
|
+
raw = q.query(QueryRequest(sql=sql), x_database_id=database_id)
|
|
504
|
+
else:
|
|
505
|
+
raw = q.query(QueryRequest(sql=sql))
|
|
494
506
|
except ApiException as e:
|
|
495
507
|
raise RuntimeError(e.reason or str(e)) from e
|
|
496
508
|
|
|
@@ -52,7 +52,7 @@ def is_parquet_path(path: str) -> bool:
|
|
|
52
52
|
def managed_database_from_detail(detail: Any) -> ManagedDatabase:
|
|
53
53
|
return ManagedDatabase(
|
|
54
54
|
id=str(detail.id),
|
|
55
|
-
description=detail.
|
|
55
|
+
description=detail.name,
|
|
56
56
|
default_connection_id=str(detail.default_connection_id),
|
|
57
57
|
)
|
|
58
58
|
|
|
@@ -4,13 +4,13 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "hotdata-runtime"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.2"
|
|
8
8
|
description = "Workspace/session runtime primitives for Hotdata integrations"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
11
11
|
license = { text = "MIT" }
|
|
12
12
|
dependencies = [
|
|
13
|
-
"hotdata>=0.2.
|
|
13
|
+
"hotdata>=0.2.4",
|
|
14
14
|
"pandas>=2.0",
|
|
15
15
|
]
|
|
16
16
|
|
|
@@ -200,6 +200,67 @@ def test_list_recent_results_returns_normalized_summaries():
|
|
|
200
200
|
assert out[0].to_dict()["created_at"] == "2026-01-01T00:00:00Z"
|
|
201
201
|
|
|
202
202
|
|
|
203
|
+
def test_execute_sql_sends_no_database_id_by_default():
|
|
204
|
+
from hotdata.models.query_response import QueryResponse as _QR
|
|
205
|
+
|
|
206
|
+
client = HotdataClient("k", "ws", host="https://api.hotdata.dev")
|
|
207
|
+
|
|
208
|
+
class FakeQueryApi:
|
|
209
|
+
def __init__(self):
|
|
210
|
+
self.calls: list[dict] = []
|
|
211
|
+
|
|
212
|
+
def query(self, request, **kwargs):
|
|
213
|
+
self.calls.append(kwargs)
|
|
214
|
+
return _QR(
|
|
215
|
+
columns=["n"],
|
|
216
|
+
rows=[[1]],
|
|
217
|
+
row_count=1,
|
|
218
|
+
nullable=[False],
|
|
219
|
+
result_id="res_1",
|
|
220
|
+
query_run_id="qrun_1",
|
|
221
|
+
execution_time_ms=1,
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
fake_q = FakeQueryApi()
|
|
225
|
+
with patch.object(client, "_query_api", return_value=fake_q):
|
|
226
|
+
client.execute_sql("SELECT 1")
|
|
227
|
+
|
|
228
|
+
assert fake_q.calls == [{}]
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def test_execute_sql_resolves_database_and_sends_x_database_id():
|
|
232
|
+
from hotdata.models.query_response import QueryResponse as _QR
|
|
233
|
+
from types import SimpleNamespace
|
|
234
|
+
|
|
235
|
+
client = HotdataClient("k", "ws", host="https://api.hotdata.dev")
|
|
236
|
+
|
|
237
|
+
class FakeQueryApi:
|
|
238
|
+
def __init__(self):
|
|
239
|
+
self.calls: list[dict] = []
|
|
240
|
+
|
|
241
|
+
def query(self, request, **kwargs):
|
|
242
|
+
self.calls.append(kwargs)
|
|
243
|
+
return _QR(
|
|
244
|
+
columns=["n"],
|
|
245
|
+
rows=[[1]],
|
|
246
|
+
row_count=1,
|
|
247
|
+
nullable=[False],
|
|
248
|
+
result_id="res_1",
|
|
249
|
+
query_run_id="qrun_1",
|
|
250
|
+
execution_time_ms=1,
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
fake_q = FakeQueryApi()
|
|
254
|
+
fake_db = SimpleNamespace(id="db_abc")
|
|
255
|
+
|
|
256
|
+
with patch.object(client, "_query_api", return_value=fake_q), \
|
|
257
|
+
patch.object(client, "resolve_managed_database", return_value=fake_db) as resolve:
|
|
258
|
+
client.execute_sql('SELECT * FROM "default"."public"."orders"', database="my_db")
|
|
259
|
+
|
|
260
|
+
resolve.assert_called_once_with("my_db")
|
|
261
|
+
assert fake_q.calls == [{"x_database_id": "db_abc"}]
|
|
262
|
+
|
|
263
|
+
|
|
203
264
|
def test_list_run_history_returns_normalized_items():
|
|
204
265
|
client = HotdataClient("k", "ws", host="https://api.hotdata.dev")
|
|
205
266
|
listing = SimpleNamespace(
|
|
@@ -20,7 +20,7 @@ def _client() -> HotdataClient:
|
|
|
20
20
|
def _detail(id="db_1", description="sales", default_connection_id="conn_1"):
|
|
21
21
|
return SimpleNamespace(
|
|
22
22
|
id=id,
|
|
23
|
-
|
|
23
|
+
name=description,
|
|
24
24
|
default_connection_id=default_connection_id,
|
|
25
25
|
)
|
|
26
26
|
|
|
@@ -86,7 +86,7 @@ def test_resolve_managed_database_by_id():
|
|
|
86
86
|
|
|
87
87
|
def test_resolve_managed_database_by_description():
|
|
88
88
|
client = _client()
|
|
89
|
-
summary = SimpleNamespace(id="db_1",
|
|
89
|
+
summary = SimpleNamespace(id="db_1", name="sales")
|
|
90
90
|
listing = SimpleNamespace(databases=[summary])
|
|
91
91
|
detail = _detail()
|
|
92
92
|
with patch.object(client, "_databases_api") as dbs:
|
|
@@ -43,7 +43,7 @@ wheels = [
|
|
|
43
43
|
|
|
44
44
|
[[package]]
|
|
45
45
|
name = "hotdata"
|
|
46
|
-
version = "0.2.
|
|
46
|
+
version = "0.2.4"
|
|
47
47
|
source = { registry = "https://pypi.org/simple" }
|
|
48
48
|
dependencies = [
|
|
49
49
|
{ name = "pydantic" },
|
|
@@ -51,14 +51,14 @@ dependencies = [
|
|
|
51
51
|
{ name = "typing-extensions" },
|
|
52
52
|
{ name = "urllib3" },
|
|
53
53
|
]
|
|
54
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
54
|
+
sdist = { url = "https://files.pythonhosted.org/packages/43/31/4b13883c5b3bc50892210dc1e3cac71b5bb08d352e70ac557c78383c1e9e/hotdata-0.2.4.tar.gz", hash = "sha256:7fc64448eaf65b822b50249c51350bf5ec05edd03c611b92ce881a495ae336ab", size = 121635, upload-time = "2026-05-27T22:05:28.424Z" }
|
|
55
55
|
wheels = [
|
|
56
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
56
|
+
{ url = "https://files.pythonhosted.org/packages/20/cb/fb9e48af1d105ff18b0732bc87e552a32f4d816ab50c093d901f9fe7e8cd/hotdata-0.2.4-py3-none-any.whl", hash = "sha256:e6652ee2f88bc214938784747e3f644a4ecc4987f9887d302ca3b8940fc52028", size = 285813, upload-time = "2026-05-27T22:05:26.766Z" },
|
|
57
57
|
]
|
|
58
58
|
|
|
59
59
|
[[package]]
|
|
60
60
|
name = "hotdata-runtime"
|
|
61
|
-
version = "0.2.
|
|
61
|
+
version = "0.2.2"
|
|
62
62
|
source = { editable = "." }
|
|
63
63
|
dependencies = [
|
|
64
64
|
{ name = "hotdata" },
|
|
@@ -74,7 +74,7 @@ dev = [
|
|
|
74
74
|
|
|
75
75
|
[package.metadata]
|
|
76
76
|
requires-dist = [
|
|
77
|
-
{ name = "hotdata", specifier = ">=0.2.
|
|
77
|
+
{ name = "hotdata", specifier = ">=0.2.4" },
|
|
78
78
|
{ name = "pandas", specifier = ">=2.0" },
|
|
79
79
|
]
|
|
80
80
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|