hotdata-framework 0.7.1__tar.gz → 0.7.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_framework-0.7.1 → hotdata_framework-0.7.2}/CHANGELOG.md +6 -0
- {hotdata_framework-0.7.1 → hotdata_framework-0.7.2}/PKG-INFO +1 -1
- {hotdata_framework-0.7.1 → hotdata_framework-0.7.2}/hotdata_framework/client.py +6 -14
- {hotdata_framework-0.7.1 → hotdata_framework-0.7.2}/pyproject.toml +1 -1
- {hotdata_framework-0.7.1 → hotdata_framework-0.7.2}/tests/test_databases.py +43 -5
- {hotdata_framework-0.7.1 → hotdata_framework-0.7.2}/uv.lock +1 -1
- {hotdata_framework-0.7.1 → hotdata_framework-0.7.2}/.github/dependabot.yml +0 -0
- {hotdata_framework-0.7.1 → hotdata_framework-0.7.2}/.github/workflows/check-release.yml +0 -0
- {hotdata_framework-0.7.1 → hotdata_framework-0.7.2}/.github/workflows/ci.yml +0 -0
- {hotdata_framework-0.7.1 → hotdata_framework-0.7.2}/.github/workflows/dependabot-automerge.yml +0 -0
- {hotdata_framework-0.7.1 → hotdata_framework-0.7.2}/.github/workflows/publish.yml +0 -0
- {hotdata_framework-0.7.1 → hotdata_framework-0.7.2}/.github/workflows/release.yml +0 -0
- {hotdata_framework-0.7.1 → hotdata_framework-0.7.2}/.gitignore +0 -0
- {hotdata_framework-0.7.1 → hotdata_framework-0.7.2}/CONTRACT.md +0 -0
- {hotdata_framework-0.7.1 → hotdata_framework-0.7.2}/README.md +0 -0
- {hotdata_framework-0.7.1 → hotdata_framework-0.7.2}/RELEASING.md +0 -0
- {hotdata_framework-0.7.1 → hotdata_framework-0.7.2}/examples/basic_usage.py +0 -0
- {hotdata_framework-0.7.1 → hotdata_framework-0.7.2}/hotdata_framework/__init__.py +0 -0
- {hotdata_framework-0.7.1 → hotdata_framework-0.7.2}/hotdata_framework/databases.py +0 -0
- {hotdata_framework-0.7.1 → hotdata_framework-0.7.2}/hotdata_framework/env.py +0 -0
- {hotdata_framework-0.7.1 → hotdata_framework-0.7.2}/hotdata_framework/errors.py +0 -0
- {hotdata_framework-0.7.1 → hotdata_framework-0.7.2}/hotdata_framework/health.py +0 -0
- {hotdata_framework-0.7.1 → hotdata_framework-0.7.2}/hotdata_framework/http.py +0 -0
- {hotdata_framework-0.7.1 → hotdata_framework-0.7.2}/hotdata_framework/managed_client.py +0 -0
- {hotdata_framework-0.7.1 → hotdata_framework-0.7.2}/hotdata_framework/py.typed +0 -0
- {hotdata_framework-0.7.1 → hotdata_framework-0.7.2}/hotdata_framework/result.py +0 -0
- {hotdata_framework-0.7.1 → hotdata_framework-0.7.2}/scripts/check-release.py +0 -0
- {hotdata_framework-0.7.1 → hotdata_framework-0.7.2}/scripts/extract-changelog.py +0 -0
- {hotdata_framework-0.7.1 → hotdata_framework-0.7.2}/scripts/publish-workflow.sh +0 -0
- {hotdata_framework-0.7.1 → hotdata_framework-0.7.2}/scripts/release.sh +0 -0
- {hotdata_framework-0.7.1 → hotdata_framework-0.7.2}/scripts/update_changelog.py +0 -0
- {hotdata_framework-0.7.1 → hotdata_framework-0.7.2}/tests/test_client.py +0 -0
- {hotdata_framework-0.7.1 → hotdata_framework-0.7.2}/tests/test_contract.py +0 -0
- {hotdata_framework-0.7.1 → hotdata_framework-0.7.2}/tests/test_errors.py +0 -0
- {hotdata_framework-0.7.1 → hotdata_framework-0.7.2}/tests/test_health.py +0 -0
- {hotdata_framework-0.7.1 → hotdata_framework-0.7.2}/tests/test_managed_client.py +0 -0
- {hotdata_framework-0.7.1 → hotdata_framework-0.7.2}/tests/test_request_timeout.py +0 -0
- {hotdata_framework-0.7.1 → hotdata_framework-0.7.2}/tests/test_result.py +0 -0
- {hotdata_framework-0.7.1 → hotdata_framework-0.7.2}/tests/test_update_changelog.py +0 -0
- {hotdata_framework-0.7.1 → hotdata_framework-0.7.2}/tests/test_version.py +0 -0
|
@@ -8,6 +8,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
10
|
|
|
11
|
+
## [0.7.2] - 2026-07-15
|
|
12
|
+
|
|
13
|
+
### Removed
|
|
14
|
+
|
|
15
|
+
- The `POST /v1/files` fallback in `upload_parquet()`. Presigned upload sessions (`POST /v1/uploads`) are now required; a server that responds 501 raises a clear `RuntimeError` instead of silently falling back to the full-file-in-memory upload path.
|
|
16
|
+
|
|
11
17
|
## [0.7.1] - 2026-07-15
|
|
12
18
|
|
|
13
19
|
### Changed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hotdata-framework
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.2
|
|
4
4
|
Summary: Python framework for building Hotdata integrations: workspace/session runtime, query execution, and managed databases
|
|
5
5
|
Project-URL: Homepage, https://www.hotdata.dev
|
|
6
6
|
Project-URL: Documentation, https://www.hotdata.dev/docs
|
|
@@ -321,7 +321,12 @@ class HotdataClient:
|
|
|
321
321
|
)
|
|
322
322
|
except ApiException as e:
|
|
323
323
|
if e.status == 501:
|
|
324
|
-
|
|
324
|
+
raise RuntimeError(
|
|
325
|
+
"the server does not support presigned uploads (HTTP 501); "
|
|
326
|
+
"managed table loads require a storage backend that can issue "
|
|
327
|
+
"presigned URLs (the POST /v1/files fallback was removed in "
|
|
328
|
+
"hotdata-framework 0.7.2)"
|
|
329
|
+
) from e
|
|
325
330
|
raise RuntimeError(api_error_message(e)) from e
|
|
326
331
|
http = urllib3.PoolManager()
|
|
327
332
|
parts: list[FinalizeUploadPart] | None = None
|
|
@@ -374,19 +379,6 @@ class HotdataClient:
|
|
|
374
379
|
raise RuntimeError(api_error_message(e)) from e
|
|
375
380
|
return finalized.upload_id
|
|
376
381
|
|
|
377
|
-
def _upload_parquet_post(self, path: str) -> str:
|
|
378
|
-
"""Fallback for storage backends that do not support presigned URLs (501)."""
|
|
379
|
-
with open(path, "rb") as f:
|
|
380
|
-
data = f.read()
|
|
381
|
-
try:
|
|
382
|
-
uploaded = self.uploads().upload_file(
|
|
383
|
-
data,
|
|
384
|
-
_content_type="application/octet-stream",
|
|
385
|
-
)
|
|
386
|
-
except ApiException as e:
|
|
387
|
-
raise RuntimeError(api_error_message(e)) from e
|
|
388
|
-
return uploaded.id
|
|
389
|
-
|
|
390
382
|
def load_managed_table(
|
|
391
383
|
self,
|
|
392
384
|
database: str,
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "hotdata-framework"
|
|
7
|
-
version = "0.7.
|
|
7
|
+
version = "0.7.2"
|
|
8
8
|
description = "Python framework for building Hotdata integrations: workspace/session runtime, query execution, and managed databases"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -278,9 +278,8 @@ def test_upload_parquet_single_put():
|
|
|
278
278
|
assert call_args.args[1] == "https://s/put"
|
|
279
279
|
|
|
280
280
|
|
|
281
|
-
def
|
|
281
|
+
def test_upload_parquet_raises_on_501():
|
|
282
282
|
client = _client()
|
|
283
|
-
uploaded = SimpleNamespace(id="upl_post")
|
|
284
283
|
|
|
285
284
|
with (
|
|
286
285
|
patch("builtins.open", mock_open(read_data=b"PAR1")),
|
|
@@ -289,11 +288,50 @@ def test_upload_parquet_fallback_on_501():
|
|
|
289
288
|
):
|
|
290
289
|
err = ApiException(status=501)
|
|
291
290
|
uploads.return_value.create_upload_session_handler.side_effect = err
|
|
292
|
-
uploads.return_value.upload_file.return_value = uploaded
|
|
293
291
|
|
|
294
|
-
|
|
292
|
+
with pytest.raises(RuntimeError, match="presigned uploads"):
|
|
293
|
+
client.upload_parquet("/tmp/data.parquet")
|
|
294
|
+
|
|
295
|
+
uploads.return_value.upload_file.assert_not_called()
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
def test_upload_parquet_multipart_reads_are_part_size_bounded():
|
|
299
|
+
"""The OOM fix: multipart mode must never read more than part_size at once."""
|
|
300
|
+
client = _client()
|
|
301
|
+
part_size = 5
|
|
302
|
+
data = b"PAR1" + b"\x00" * 9 # 13 bytes -> parts of 5, 5, 3
|
|
303
|
+
read_sizes: list[int] = []
|
|
304
|
+
|
|
305
|
+
class _TrackingFile(io.BytesIO):
|
|
306
|
+
def read(self, n=-1):
|
|
307
|
+
read_sizes.append(n)
|
|
308
|
+
return super().read(n)
|
|
309
|
+
|
|
310
|
+
bio = _TrackingFile(data)
|
|
311
|
+
handle = MagicMock()
|
|
312
|
+
handle.__enter__ = lambda s: bio
|
|
313
|
+
handle.__exit__ = MagicMock(return_value=False)
|
|
314
|
+
session = _session(
|
|
315
|
+
"multipart", part_size=part_size, part_urls=["https://s/1", "https://s/2", "https://s/3"]
|
|
316
|
+
)
|
|
317
|
+
finalized = SimpleNamespace(upload_id="upl_final")
|
|
318
|
+
|
|
319
|
+
with (
|
|
320
|
+
patch("builtins.open", MagicMock(return_value=handle)),
|
|
321
|
+
patch("os.path.getsize", return_value=len(data)),
|
|
322
|
+
patch.object(client, "uploads") as uploads,
|
|
323
|
+
patch("hotdata_framework.client.urllib3.PoolManager") as MockPool,
|
|
324
|
+
):
|
|
325
|
+
pool = MockPool.return_value
|
|
326
|
+
pool.request.return_value = _http_resp()
|
|
327
|
+
pool.clear.return_value = None
|
|
328
|
+
uploads.return_value.create_upload_session_handler.return_value = session
|
|
329
|
+
uploads.return_value.finalize_upload_handler.return_value = finalized
|
|
330
|
+
|
|
331
|
+
client.upload_parquet("/tmp/data.parquet")
|
|
295
332
|
|
|
296
|
-
assert
|
|
333
|
+
assert read_sizes, "expected chunked reads"
|
|
334
|
+
assert all(n == part_size for n in read_sizes)
|
|
297
335
|
|
|
298
336
|
|
|
299
337
|
def test_load_managed_table_with_upload_id():
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hotdata_framework-0.7.1 → hotdata_framework-0.7.2}/.github/workflows/dependabot-automerge.yml
RENAMED
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|