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.
Files changed (32) hide show
  1. {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/.github/workflows/release.yml +1 -1
  2. {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/CHANGELOG.md +14 -0
  3. {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/PKG-INFO +2 -2
  4. {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/hotdata_runtime/client.py +18 -6
  5. {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/hotdata_runtime/databases.py +1 -1
  6. {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/pyproject.toml +2 -2
  7. {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/tests/test_client.py +61 -0
  8. {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/tests/test_databases.py +2 -2
  9. {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/uv.lock +5 -5
  10. {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/.github/workflows/check-release.yml +0 -0
  11. {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/.github/workflows/ci.yml +0 -0
  12. {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/.github/workflows/publish.yml +0 -0
  13. {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/.gitignore +0 -0
  14. {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/CONTRACT.md +0 -0
  15. {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/README.md +0 -0
  16. {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/RELEASING.md +0 -0
  17. {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/examples/basic_usage.py +0 -0
  18. {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/hotdata_runtime/__init__.py +0 -0
  19. {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/hotdata_runtime/env.py +0 -0
  20. {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/hotdata_runtime/health.py +0 -0
  21. {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/hotdata_runtime/http.py +0 -0
  22. {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/hotdata_runtime/result.py +0 -0
  23. {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/scripts/check-release.py +0 -0
  24. {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/scripts/extract-changelog.py +0 -0
  25. {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/scripts/publish-workflow.sh +0 -0
  26. {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/scripts/release.sh +0 -0
  27. {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/scripts/update_changelog.py +0 -0
  28. {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/tests/test_contract.py +0 -0
  29. {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/tests/test_health.py +0 -0
  30. {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/tests/test_result.py +0 -0
  31. {hotdata_runtime-0.2.0 → hotdata_runtime-0.2.2}/tests/test_update_changelog.py +0 -0
  32. {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@1e812e8210a4a8a0b23075e5795f2a4e2b2a0b7 # v2.2.2
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.0
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.3
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.description == name_or_id:
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
- description=description,
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
- raw = q.query(QueryRequest(sql=sql))
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.description,
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.0"
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.3",
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
- description=description,
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", description="sales")
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.3"
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/4f/20/5d016d4aec39fe04eb77a6394651e3b18f6ecc701dc678563889debd79ed/hotdata-0.2.3.tar.gz", hash = "sha256:bc415af4ac475e5bd5fe3320d1c14aaac92942462a0ef9dac22b89bcc120ad55", size = 118187, upload-time = "2026-05-23T04:41:10.835Z" }
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/09/87/d3cb845ba01e5b4e9bfb1e59d0032a246b94497e470d171f2ee2a56bd850/hotdata-0.2.3-py3-none-any.whl", hash = "sha256:aed2ae884d184cf143572c84d068a9ceedbe021a6d14005332647a46aa7be11c", size = 275718, upload-time = "2026-05-23T04:41:09.355Z" },
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.0"
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.3" },
77
+ { name = "hotdata", specifier = ">=0.2.4" },
78
78
  { name = "pandas", specifier = ">=2.0" },
79
79
  ]
80
80