hotdata-framework 0.7.2__tar.gz → 0.7.3__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.2 → hotdata_framework-0.7.3}/CHANGELOG.md +10 -0
- {hotdata_framework-0.7.2 → hotdata_framework-0.7.3}/PKG-INFO +1 -1
- {hotdata_framework-0.7.2 → hotdata_framework-0.7.3}/hotdata_framework/client.py +20 -68
- {hotdata_framework-0.7.2 → hotdata_framework-0.7.3}/hotdata_framework/errors.py +4 -0
- {hotdata_framework-0.7.2 → hotdata_framework-0.7.3}/pyproject.toml +1 -1
- {hotdata_framework-0.7.2 → hotdata_framework-0.7.3}/tests/test_databases.py +35 -118
- {hotdata_framework-0.7.2 → hotdata_framework-0.7.3}/uv.lock +1 -1
- {hotdata_framework-0.7.2 → hotdata_framework-0.7.3}/.github/dependabot.yml +0 -0
- {hotdata_framework-0.7.2 → hotdata_framework-0.7.3}/.github/workflows/check-release.yml +0 -0
- {hotdata_framework-0.7.2 → hotdata_framework-0.7.3}/.github/workflows/ci.yml +0 -0
- {hotdata_framework-0.7.2 → hotdata_framework-0.7.3}/.github/workflows/dependabot-automerge.yml +0 -0
- {hotdata_framework-0.7.2 → hotdata_framework-0.7.3}/.github/workflows/publish.yml +0 -0
- {hotdata_framework-0.7.2 → hotdata_framework-0.7.3}/.github/workflows/release.yml +0 -0
- {hotdata_framework-0.7.2 → hotdata_framework-0.7.3}/.gitignore +0 -0
- {hotdata_framework-0.7.2 → hotdata_framework-0.7.3}/CONTRACT.md +0 -0
- {hotdata_framework-0.7.2 → hotdata_framework-0.7.3}/README.md +0 -0
- {hotdata_framework-0.7.2 → hotdata_framework-0.7.3}/RELEASING.md +0 -0
- {hotdata_framework-0.7.2 → hotdata_framework-0.7.3}/examples/basic_usage.py +0 -0
- {hotdata_framework-0.7.2 → hotdata_framework-0.7.3}/hotdata_framework/__init__.py +0 -0
- {hotdata_framework-0.7.2 → hotdata_framework-0.7.3}/hotdata_framework/databases.py +0 -0
- {hotdata_framework-0.7.2 → hotdata_framework-0.7.3}/hotdata_framework/env.py +0 -0
- {hotdata_framework-0.7.2 → hotdata_framework-0.7.3}/hotdata_framework/health.py +0 -0
- {hotdata_framework-0.7.2 → hotdata_framework-0.7.3}/hotdata_framework/http.py +0 -0
- {hotdata_framework-0.7.2 → hotdata_framework-0.7.3}/hotdata_framework/managed_client.py +0 -0
- {hotdata_framework-0.7.2 → hotdata_framework-0.7.3}/hotdata_framework/py.typed +0 -0
- {hotdata_framework-0.7.2 → hotdata_framework-0.7.3}/hotdata_framework/result.py +0 -0
- {hotdata_framework-0.7.2 → hotdata_framework-0.7.3}/scripts/check-release.py +0 -0
- {hotdata_framework-0.7.2 → hotdata_framework-0.7.3}/scripts/extract-changelog.py +0 -0
- {hotdata_framework-0.7.2 → hotdata_framework-0.7.3}/scripts/publish-workflow.sh +0 -0
- {hotdata_framework-0.7.2 → hotdata_framework-0.7.3}/scripts/release.sh +0 -0
- {hotdata_framework-0.7.2 → hotdata_framework-0.7.3}/scripts/update_changelog.py +0 -0
- {hotdata_framework-0.7.2 → hotdata_framework-0.7.3}/tests/test_client.py +0 -0
- {hotdata_framework-0.7.2 → hotdata_framework-0.7.3}/tests/test_contract.py +0 -0
- {hotdata_framework-0.7.2 → hotdata_framework-0.7.3}/tests/test_errors.py +0 -0
- {hotdata_framework-0.7.2 → hotdata_framework-0.7.3}/tests/test_health.py +0 -0
- {hotdata_framework-0.7.2 → hotdata_framework-0.7.3}/tests/test_managed_client.py +0 -0
- {hotdata_framework-0.7.2 → hotdata_framework-0.7.3}/tests/test_request_timeout.py +0 -0
- {hotdata_framework-0.7.2 → hotdata_framework-0.7.3}/tests/test_result.py +0 -0
- {hotdata_framework-0.7.2 → hotdata_framework-0.7.3}/tests/test_update_changelog.py +0 -0
- {hotdata_framework-0.7.2 → hotdata_framework-0.7.3}/tests/test_version.py +0 -0
|
@@ -8,6 +8,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
10
|
|
|
11
|
+
## [0.7.3] - 2026-07-16
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
- `upload_parquet()` now delegates to the SDK's `hotdata.uploads.UploadsApi.upload_file()` instead of hand-rolling the session → PUT → finalize flow. Uploads gain concurrent part PUTs under a peak-memory budget, per-part retries, and ETag/size validation, making large uploads substantially faster. Errors still surface as `RuntimeError` with the underlying `ApiException` as the direct cause.
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- `classify_sdk_error` now classifies HTTP 501 (Not Implemented) as terminal instead of transient — a permanent capability gap must not burn retries.
|
|
20
|
+
|
|
11
21
|
## [0.7.2] - 2026-07-15
|
|
12
22
|
|
|
13
23
|
### Removed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hotdata-framework
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.3
|
|
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
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import functools
|
|
4
|
-
import os
|
|
5
4
|
import time
|
|
6
|
-
import urllib3
|
|
7
5
|
from collections.abc import Iterator
|
|
8
6
|
from dataclasses import asdict, dataclass
|
|
9
7
|
from typing import Any, Literal
|
|
@@ -15,16 +13,15 @@ from hotdata.api.information_schema_api import InformationSchemaApi
|
|
|
15
13
|
from hotdata.api.query_api import QueryApi
|
|
16
14
|
from hotdata.api.query_runs_api import QueryRunsApi
|
|
17
15
|
from hotdata.api.results_api import ResultsApi
|
|
18
|
-
|
|
16
|
+
# The enriched wrapper (hotdata.uploads), NOT the generated hotdata.api class:
|
|
17
|
+
# it adds the full upload_file orchestration used by upload_parquet.
|
|
18
|
+
from hotdata.uploads import UploadError, UploadsApi
|
|
19
19
|
from hotdata.exceptions import ApiException
|
|
20
20
|
from hotdata.models.add_managed_table_request import AddManagedTableRequest
|
|
21
21
|
from hotdata.models.async_query_response import AsyncQueryResponse
|
|
22
22
|
from hotdata.models.create_database_request import CreateDatabaseRequest
|
|
23
23
|
from hotdata.models.database_default_schema_decl import DatabaseDefaultSchemaDecl
|
|
24
24
|
from hotdata.models.database_default_table_decl import DatabaseDefaultTableDecl
|
|
25
|
-
from hotdata.models.create_upload_request import CreateUploadRequest
|
|
26
|
-
from hotdata.models.finalize_upload_part import FinalizeUploadPart
|
|
27
|
-
from hotdata.models.finalize_upload_request import FinalizeUploadRequest
|
|
28
25
|
from hotdata.models.load_managed_table_request import LoadManagedTableRequest
|
|
29
26
|
from hotdata.models.query_request import QueryRequest
|
|
30
27
|
from hotdata.models.query_response import QueryResponse
|
|
@@ -309,74 +306,29 @@ class HotdataClient:
|
|
|
309
306
|
return rows
|
|
310
307
|
|
|
311
308
|
def upload_parquet(self, path: str) -> str:
|
|
309
|
+
"""Upload a parquet file via the SDK's upload orchestration.
|
|
310
|
+
|
|
311
|
+
``UploadsApi.upload_file`` owns the whole session -> storage PUT ->
|
|
312
|
+
finalize flow: concurrent part uploads under a peak-memory budget,
|
|
313
|
+
per-part retries, and ETag/size validation. Errors surface with the
|
|
314
|
+
underlying ``ApiException`` as the direct cause when there is one, so
|
|
315
|
+
retry classification keeps seeing the status code.
|
|
316
|
+
"""
|
|
312
317
|
if not is_parquet_path(path):
|
|
313
318
|
raise ValueError(f"Managed table loads require a parquet file (got {path!r})")
|
|
314
|
-
file_size = os.path.getsize(path)
|
|
315
|
-
try:
|
|
316
|
-
session = self.uploads().create_upload_session_handler(
|
|
317
|
-
CreateUploadRequest(
|
|
318
|
-
declared_size_bytes=file_size,
|
|
319
|
-
content_type="application/octet-stream",
|
|
320
|
-
)
|
|
321
|
-
)
|
|
322
|
-
except ApiException as e:
|
|
323
|
-
if e.status == 501:
|
|
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
|
|
330
|
-
raise RuntimeError(api_error_message(e)) from e
|
|
331
|
-
http = urllib3.PoolManager()
|
|
332
|
-
parts: list[FinalizeUploadPart] | None = None
|
|
333
|
-
try:
|
|
334
|
-
if session.mode == "single":
|
|
335
|
-
with open(path, "rb") as f:
|
|
336
|
-
data = f.read()
|
|
337
|
-
resp = http.request(
|
|
338
|
-
"PUT",
|
|
339
|
-
session.url,
|
|
340
|
-
body=data,
|
|
341
|
-
headers={"Content-Length": str(file_size), **session.headers},
|
|
342
|
-
)
|
|
343
|
-
if resp.status not in (200, 201, 204):
|
|
344
|
-
raise RuntimeError(f"Storage PUT failed: HTTP {resp.status}")
|
|
345
|
-
else:
|
|
346
|
-
collected: list[FinalizeUploadPart] = []
|
|
347
|
-
with open(path, "rb") as f:
|
|
348
|
-
for i, part_url in enumerate(session.part_urls):
|
|
349
|
-
chunk = f.read(session.part_size)
|
|
350
|
-
resp = http.request(
|
|
351
|
-
"PUT",
|
|
352
|
-
part_url,
|
|
353
|
-
body=chunk,
|
|
354
|
-
headers={
|
|
355
|
-
"Content-Length": str(len(chunk)),
|
|
356
|
-
**session.headers,
|
|
357
|
-
},
|
|
358
|
-
)
|
|
359
|
-
if resp.status not in (200, 201, 204):
|
|
360
|
-
raise RuntimeError(
|
|
361
|
-
f"Part {i + 1} PUT failed: HTTP {resp.status}"
|
|
362
|
-
)
|
|
363
|
-
collected.append(
|
|
364
|
-
FinalizeUploadPart(
|
|
365
|
-
part_number=i + 1,
|
|
366
|
-
e_tag=resp.headers["ETag"],
|
|
367
|
-
)
|
|
368
|
-
)
|
|
369
|
-
parts = collected
|
|
370
|
-
finally:
|
|
371
|
-
http.clear()
|
|
372
319
|
try:
|
|
373
|
-
finalized = self.uploads().
|
|
374
|
-
|
|
375
|
-
x_upload_finalize_token=session.finalize_token,
|
|
376
|
-
finalize_upload_request=FinalizeUploadRequest(parts=parts),
|
|
320
|
+
finalized = self.uploads().upload_file(
|
|
321
|
+
path, content_type="application/octet-stream"
|
|
377
322
|
)
|
|
378
323
|
except ApiException as e:
|
|
379
324
|
raise RuntimeError(api_error_message(e)) from e
|
|
325
|
+
except UploadError as e:
|
|
326
|
+
node: BaseException | None = e
|
|
327
|
+
while node is not None and not isinstance(node, ApiException):
|
|
328
|
+
node = node.__cause__
|
|
329
|
+
if isinstance(node, ApiException):
|
|
330
|
+
raise RuntimeError(api_error_message(node)) from node
|
|
331
|
+
raise RuntimeError(str(e)) from e
|
|
380
332
|
return finalized.upload_id
|
|
381
333
|
|
|
382
334
|
def load_managed_table(
|
|
@@ -30,6 +30,10 @@ def classify_sdk_error(error: Exception) -> HotdataError:
|
|
|
30
30
|
message = f"{message} — {' '.join(str(body).split())[:500]}"
|
|
31
31
|
if status_code in (408, 409, 425, 429):
|
|
32
32
|
return HotdataTransientError(message)
|
|
33
|
+
if status_code == 501:
|
|
34
|
+
# Not Implemented is a permanent capability gap (e.g. the storage
|
|
35
|
+
# backend cannot issue presigned URLs) — retrying cannot succeed.
|
|
36
|
+
return HotdataTerminalError(message)
|
|
33
37
|
if 500 <= status_code <= 599:
|
|
34
38
|
return HotdataTransientError(message)
|
|
35
39
|
return HotdataTerminalError(message)
|
|
@@ -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.3"
|
|
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"
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import io
|
|
4
3
|
from types import SimpleNamespace
|
|
5
|
-
from unittest.mock import
|
|
4
|
+
from unittest.mock import patch
|
|
6
5
|
|
|
7
6
|
import pytest
|
|
8
7
|
from hotdata.exceptions import ApiException
|
|
@@ -195,143 +194,61 @@ def test_upload_parquet_rejects_non_parquet():
|
|
|
195
194
|
client.upload_parquet("/tmp/data.csv")
|
|
196
195
|
|
|
197
196
|
|
|
198
|
-
def
|
|
199
|
-
"""Return an open() mock whose file handle supports read(n) via BytesIO."""
|
|
200
|
-
bio = io.BytesIO(data)
|
|
201
|
-
m = MagicMock()
|
|
202
|
-
m.__enter__ = lambda s: bio
|
|
203
|
-
m.__exit__ = MagicMock(return_value=False)
|
|
204
|
-
return MagicMock(return_value=m)
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
def _session(mode: str, **kw) -> SimpleNamespace:
|
|
208
|
-
defaults = dict(
|
|
209
|
-
upload_id="upl_sess",
|
|
210
|
-
finalize_token="tok",
|
|
211
|
-
headers={},
|
|
212
|
-
part_size=None,
|
|
213
|
-
part_urls=None,
|
|
214
|
-
url=None,
|
|
215
|
-
)
|
|
216
|
-
return SimpleNamespace(mode=mode, **{**defaults, **kw})
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
def _http_resp(status: int = 200, etag: str = '"abc"') -> SimpleNamespace:
|
|
220
|
-
return SimpleNamespace(status=status, headers={"ETag": etag})
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
def test_upload_parquet_multipart():
|
|
197
|
+
def test_upload_parquet_delegates_to_sdk_upload_file():
|
|
224
198
|
client = _client()
|
|
225
|
-
data = b"PAR1" + b"\x00" * 6 # 10 bytes -> 2 parts of 5
|
|
226
|
-
session = _session("multipart", part_size=5, part_urls=["https://s/1", "https://s/2"])
|
|
227
199
|
finalized = SimpleNamespace(upload_id="upl_final")
|
|
228
200
|
|
|
229
|
-
with (
|
|
230
|
-
|
|
231
|
-
patch("os.path.getsize", return_value=len(data)),
|
|
232
|
-
patch.object(client, "uploads") as uploads,
|
|
233
|
-
patch("hotdata_framework.client.urllib3.PoolManager") as MockPool,
|
|
234
|
-
):
|
|
235
|
-
pool = MockPool.return_value
|
|
236
|
-
pool.request.return_value = _http_resp()
|
|
237
|
-
pool.clear.return_value = None
|
|
238
|
-
uploads.return_value.create_upload_session_handler.return_value = session
|
|
239
|
-
uploads.return_value.finalize_upload_handler.return_value = finalized
|
|
240
|
-
|
|
201
|
+
with patch.object(client, "uploads") as uploads:
|
|
202
|
+
uploads.return_value.upload_file.return_value = finalized
|
|
241
203
|
upload_id = client.upload_parquet("/tmp/data.parquet")
|
|
242
204
|
|
|
243
205
|
assert upload_id == "upl_final"
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
parts = finalize_call.kwargs["finalize_upload_request"].parts
|
|
249
|
-
assert len(parts) == 2
|
|
250
|
-
assert parts[0].part_number == 1
|
|
251
|
-
assert parts[1].part_number == 2
|
|
206
|
+
uploads.return_value.upload_file.assert_called_once_with(
|
|
207
|
+
"/tmp/data.parquet", content_type="application/octet-stream"
|
|
208
|
+
)
|
|
209
|
+
|
|
252
210
|
|
|
211
|
+
def test_upload_parquet_surfaces_api_cause_from_upload_error():
|
|
212
|
+
# SessionCreateError et al. wrap the ApiException as __cause__; the wrapper
|
|
213
|
+
# must re-raise with that ApiException as the DIRECT cause so
|
|
214
|
+
# classify_sdk_error keeps seeing the status code.
|
|
215
|
+
from hotdata.uploads import SessionCreateError
|
|
253
216
|
|
|
254
|
-
def test_upload_parquet_single_put():
|
|
255
217
|
client = _client()
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
218
|
+
api_exc = ApiException(status=501)
|
|
219
|
+
sdk_err = SessionCreateError("opening the upload session failed")
|
|
220
|
+
sdk_err.__cause__ = api_exc
|
|
259
221
|
|
|
260
|
-
with (
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
patch("hotdata_framework.client.urllib3.PoolManager") as MockPool,
|
|
265
|
-
):
|
|
266
|
-
pool = MockPool.return_value
|
|
267
|
-
pool.request.return_value = _http_resp()
|
|
268
|
-
pool.clear.return_value = None
|
|
269
|
-
uploads.return_value.create_upload_session_handler.return_value = session
|
|
270
|
-
uploads.return_value.finalize_upload_handler.return_value = finalized
|
|
222
|
+
with patch.object(client, "uploads") as uploads:
|
|
223
|
+
uploads.return_value.upload_file.side_effect = sdk_err
|
|
224
|
+
with pytest.raises(RuntimeError) as exc_info:
|
|
225
|
+
client.upload_parquet("/tmp/data.parquet")
|
|
271
226
|
|
|
272
|
-
|
|
227
|
+
assert exc_info.value.__cause__ is api_exc
|
|
273
228
|
|
|
274
|
-
assert upload_id == "upl_single"
|
|
275
|
-
pool.request.assert_called_once()
|
|
276
|
-
call_args = pool.request.call_args
|
|
277
|
-
assert call_args.args[0] == "PUT"
|
|
278
|
-
assert call_args.args[1] == "https://s/put"
|
|
279
229
|
|
|
230
|
+
def test_upload_parquet_wraps_plain_upload_error():
|
|
231
|
+
from hotdata.uploads import StorageError
|
|
280
232
|
|
|
281
|
-
def test_upload_parquet_raises_on_501():
|
|
282
233
|
client = _client()
|
|
234
|
+
sdk_err = StorageError(status=503, part_number=3, body="upstream unavailable")
|
|
283
235
|
|
|
284
|
-
with (
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
patch.object(client, "uploads") as uploads,
|
|
288
|
-
):
|
|
289
|
-
err = ApiException(status=501)
|
|
290
|
-
uploads.return_value.create_upload_session_handler.side_effect = err
|
|
291
|
-
|
|
292
|
-
with pytest.raises(RuntimeError, match="presigned uploads"):
|
|
236
|
+
with patch.object(client, "uploads") as uploads:
|
|
237
|
+
uploads.return_value.upload_file.side_effect = sdk_err
|
|
238
|
+
with pytest.raises(RuntimeError, match="503"):
|
|
293
239
|
client.upload_parquet("/tmp/data.parquet")
|
|
294
240
|
|
|
295
|
-
uploads.return_value.upload_file.assert_not_called()
|
|
296
|
-
|
|
297
241
|
|
|
298
|
-
def
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
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"]
|
|
242
|
+
def test_classify_501_is_terminal():
|
|
243
|
+
from hotdata_framework.errors import (
|
|
244
|
+
HotdataTerminalError,
|
|
245
|
+
HotdataTransientError,
|
|
246
|
+
classify_sdk_error,
|
|
316
247
|
)
|
|
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")
|
|
332
248
|
|
|
333
|
-
assert
|
|
334
|
-
|
|
249
|
+
assert isinstance(classify_sdk_error(ApiException(status=501)), HotdataTerminalError)
|
|
250
|
+
# The rest of 5xx stays transient.
|
|
251
|
+
assert isinstance(classify_sdk_error(ApiException(status=503)), HotdataTransientError)
|
|
335
252
|
|
|
336
253
|
|
|
337
254
|
def test_load_managed_table_with_upload_id():
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hotdata_framework-0.7.2 → hotdata_framework-0.7.3}/.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
|