hotdata-framework 0.11.0__tar.gz → 0.12.1__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.12.1/.github/dependabot.yml +17 -0
- hotdata_framework-0.12.1/.github/workflows/publish.yml +105 -0
- hotdata_framework-0.12.1/.github/workflows/release.yml +92 -0
- {hotdata_framework-0.11.0 → hotdata_framework-0.12.1}/CHANGELOG.md +39 -0
- {hotdata_framework-0.11.0 → hotdata_framework-0.12.1}/CONTRACT.md +6 -1
- {hotdata_framework-0.11.0 → hotdata_framework-0.12.1}/PKG-INFO +2 -2
- {hotdata_framework-0.11.0 → hotdata_framework-0.12.1}/RELEASING.md +21 -0
- {hotdata_framework-0.11.0 → hotdata_framework-0.12.1}/hotdata_framework/__init__.py +7 -0
- {hotdata_framework-0.11.0 → hotdata_framework-0.12.1}/hotdata_framework/client.py +218 -8
- {hotdata_framework-0.11.0 → hotdata_framework-0.12.1}/hotdata_framework/databases.py +48 -0
- {hotdata_framework-0.11.0 → hotdata_framework-0.12.1}/pyproject.toml +2 -2
- {hotdata_framework-0.11.0 → hotdata_framework-0.12.1}/tests/test_client.py +331 -1
- {hotdata_framework-0.11.0 → hotdata_framework-0.12.1}/tests/test_contract.py +3 -0
- {hotdata_framework-0.11.0 → hotdata_framework-0.12.1}/tests/test_databases.py +173 -0
- {hotdata_framework-0.11.0 → hotdata_framework-0.12.1}/uv.lock +5 -5
- hotdata_framework-0.11.0/.github/dependabot.yml +0 -8
- hotdata_framework-0.11.0/.github/workflows/publish.yml +0 -69
- hotdata_framework-0.11.0/.github/workflows/release.yml +0 -54
- {hotdata_framework-0.11.0 → hotdata_framework-0.12.1}/.github/CODEOWNERS +0 -0
- {hotdata_framework-0.11.0 → hotdata_framework-0.12.1}/.github/workflows/check-release.yml +0 -0
- {hotdata_framework-0.11.0 → hotdata_framework-0.12.1}/.github/workflows/ci.yml +0 -0
- {hotdata_framework-0.11.0 → hotdata_framework-0.12.1}/.github/workflows/dependabot-automerge.yml +0 -0
- {hotdata_framework-0.11.0 → hotdata_framework-0.12.1}/.gitignore +0 -0
- {hotdata_framework-0.11.0 → hotdata_framework-0.12.1}/README.md +0 -0
- {hotdata_framework-0.11.0 → hotdata_framework-0.12.1}/examples/basic_usage.py +0 -0
- {hotdata_framework-0.11.0 → hotdata_framework-0.12.1}/hotdata_framework/env.py +0 -0
- {hotdata_framework-0.11.0 → hotdata_framework-0.12.1}/hotdata_framework/errors.py +0 -0
- {hotdata_framework-0.11.0 → hotdata_framework-0.12.1}/hotdata_framework/health.py +0 -0
- {hotdata_framework-0.11.0 → hotdata_framework-0.12.1}/hotdata_framework/managed_client.py +0 -0
- {hotdata_framework-0.11.0 → hotdata_framework-0.12.1}/hotdata_framework/py.typed +0 -0
- {hotdata_framework-0.11.0 → hotdata_framework-0.12.1}/hotdata_framework/result.py +0 -0
- {hotdata_framework-0.11.0 → hotdata_framework-0.12.1}/scripts/check-release.py +0 -0
- {hotdata_framework-0.11.0 → hotdata_framework-0.12.1}/scripts/extract-changelog.py +0 -0
- {hotdata_framework-0.11.0 → hotdata_framework-0.12.1}/scripts/publish-workflow.sh +0 -0
- {hotdata_framework-0.11.0 → hotdata_framework-0.12.1}/scripts/release.sh +0 -0
- {hotdata_framework-0.11.0 → hotdata_framework-0.12.1}/scripts/update_changelog.py +0 -0
- {hotdata_framework-0.11.0 → hotdata_framework-0.12.1}/tests/test_errors.py +0 -0
- {hotdata_framework-0.11.0 → hotdata_framework-0.12.1}/tests/test_health.py +0 -0
- {hotdata_framework-0.11.0 → hotdata_framework-0.12.1}/tests/test_indexes.py +0 -0
- {hotdata_framework-0.11.0 → hotdata_framework-0.12.1}/tests/test_managed_client.py +0 -0
- {hotdata_framework-0.11.0 → hotdata_framework-0.12.1}/tests/test_request_timeout.py +0 -0
- {hotdata_framework-0.11.0 → hotdata_framework-0.12.1}/tests/test_result.py +0 -0
- {hotdata_framework-0.11.0 → hotdata_framework-0.12.1}/tests/test_retry_policy.py +0 -0
- {hotdata_framework-0.11.0 → hotdata_framework-0.12.1}/tests/test_update_changelog.py +0 -0
- {hotdata_framework-0.11.0 → hotdata_framework-0.12.1}/tests/test_version.py +0 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
updates:
|
|
3
|
+
- package-ecosystem: uv
|
|
4
|
+
directory: "/"
|
|
5
|
+
schedule:
|
|
6
|
+
interval: daily
|
|
7
|
+
allow:
|
|
8
|
+
- dependency-name: hotdata
|
|
9
|
+
|
|
10
|
+
# Action pins had no watcher, which is how gh-action-pypi-publish sat at
|
|
11
|
+
# v1.13.0 until its bundled twine broke a release at upload time. Unlike the
|
|
12
|
+
# uv entry there is no `allow` filter: the point is to see every stale pin,
|
|
13
|
+
# not a chosen one.
|
|
14
|
+
- package-ecosystem: github-actions
|
|
15
|
+
directory: "/"
|
|
16
|
+
schedule:
|
|
17
|
+
interval: weekly
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- 'v[0-9]*'
|
|
7
|
+
# Retry an existing tag without moving it. A publish can fail for reasons that
|
|
8
|
+
# have nothing to do with the code — a stale action pin, a PyPI outage — and
|
|
9
|
+
# with only the tag trigger the choices were to delete and re-push the tag or
|
|
10
|
+
# to burn a version number on a CI fix. Neither is a good answer to
|
|
11
|
+
# "the upload failed, run it again".
|
|
12
|
+
workflow_dispatch:
|
|
13
|
+
inputs:
|
|
14
|
+
tag:
|
|
15
|
+
description: Existing tag to build and publish (e.g. v1.2.3)
|
|
16
|
+
required: true
|
|
17
|
+
type: string
|
|
18
|
+
|
|
19
|
+
concurrency:
|
|
20
|
+
group: pypi-publish-${{ inputs.tag || github.ref_name }}
|
|
21
|
+
cancel-in-progress: false
|
|
22
|
+
|
|
23
|
+
permissions:
|
|
24
|
+
contents: read
|
|
25
|
+
|
|
26
|
+
jobs:
|
|
27
|
+
build:
|
|
28
|
+
name: Build distribution
|
|
29
|
+
runs-on: ubuntu-latest
|
|
30
|
+
env:
|
|
31
|
+
# The tag being released, whether it arrived by push or by dispatch.
|
|
32
|
+
TAG: ${{ inputs.tag || github.ref_name }}
|
|
33
|
+
steps:
|
|
34
|
+
# Before checkout, because checkout resolves the input as an arbitrary ref:
|
|
35
|
+
# a branch or SHA is fetched first and only rejected later by the version
|
|
36
|
+
# match below, which is also looser (`^v[0-9]`). Strict here so the dispatch
|
|
37
|
+
# contract matches release.yml — release.sh only ever produces X.Y.Z.
|
|
38
|
+
- name: Validate release tag format
|
|
39
|
+
if: github.event_name == 'workflow_dispatch'
|
|
40
|
+
env:
|
|
41
|
+
INPUT_TAG: ${{ inputs.tag }}
|
|
42
|
+
run: |
|
|
43
|
+
set -euo pipefail
|
|
44
|
+
if [[ ! "$INPUT_TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
|
45
|
+
echo "tag must look like vX.Y.Z, got: $INPUT_TAG" >&2
|
|
46
|
+
exit 1
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
|
50
|
+
with:
|
|
51
|
+
ref: ${{ inputs.tag || github.ref_name }}
|
|
52
|
+
|
|
53
|
+
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
|
|
54
|
+
with:
|
|
55
|
+
python-version: '3.12'
|
|
56
|
+
|
|
57
|
+
- name: Install build tooling
|
|
58
|
+
run: python -m pip install --upgrade build twine
|
|
59
|
+
|
|
60
|
+
- name: Verify tag matches pyproject version
|
|
61
|
+
run: |
|
|
62
|
+
if [[ ! "$TAG" =~ ^v[0-9] ]]; then
|
|
63
|
+
echo "Release tag '$TAG' must start with 'v' followed by a digit (e.g. v1.0.0)" >&2
|
|
64
|
+
exit 1
|
|
65
|
+
fi
|
|
66
|
+
tag="${TAG#v}"
|
|
67
|
+
pkg_version=$(python -c "import tomllib,pathlib; print(tomllib.loads(pathlib.Path('pyproject.toml').read_text())['project']['version'])")
|
|
68
|
+
if [ "$tag" != "$pkg_version" ]; then
|
|
69
|
+
echo "Release tag ($tag) does not match pyproject.toml version ($pkg_version)" >&2
|
|
70
|
+
exit 1
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
- name: Build sdist and wheel
|
|
74
|
+
run: python -m build
|
|
75
|
+
|
|
76
|
+
- name: Check distribution metadata
|
|
77
|
+
run: python -m twine check --strict dist/*
|
|
78
|
+
|
|
79
|
+
- uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
|
|
80
|
+
with:
|
|
81
|
+
name: dist
|
|
82
|
+
path: dist/
|
|
83
|
+
|
|
84
|
+
publish:
|
|
85
|
+
name: Publish to PyPI
|
|
86
|
+
needs: build
|
|
87
|
+
runs-on: ubuntu-latest
|
|
88
|
+
environment:
|
|
89
|
+
name: pypi
|
|
90
|
+
url: https://pypi.org/p/hotdata-framework
|
|
91
|
+
permissions:
|
|
92
|
+
id-token: write
|
|
93
|
+
steps:
|
|
94
|
+
- uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5
|
|
95
|
+
with:
|
|
96
|
+
name: dist
|
|
97
|
+
path: dist/
|
|
98
|
+
|
|
99
|
+
# v1.13.0's bundled twine rejects `Metadata-Version: 2.5`, which current
|
|
100
|
+
# hatchling emits: `InvalidDistribution: '2.5' is not a valid metadata
|
|
101
|
+
# version`. The build job's own `twine check --strict` passes, because it
|
|
102
|
+
# pip-installs a current twine — so the failure appears only at upload,
|
|
103
|
+
# after the tag is already public.
|
|
104
|
+
- name: Publish via Trusted Publishing
|
|
105
|
+
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
name: GitHub Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- 'v[0-9]*'
|
|
7
|
+
# Repair the Release for a tag that already exists, without moving it. See
|
|
8
|
+
# RELEASING.md, "If a release workflow fails" — added alongside this trigger,
|
|
9
|
+
# since a capability nobody can find is not much better than not having it.
|
|
10
|
+
workflow_dispatch:
|
|
11
|
+
inputs:
|
|
12
|
+
tag:
|
|
13
|
+
description: Existing tag to create or update a Release for (e.g. v1.2.3)
|
|
14
|
+
required: true
|
|
15
|
+
type: string
|
|
16
|
+
|
|
17
|
+
permissions:
|
|
18
|
+
contents: write
|
|
19
|
+
|
|
20
|
+
jobs:
|
|
21
|
+
release:
|
|
22
|
+
name: Create GitHub Release
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
env:
|
|
25
|
+
# The tag being released, whether it arrived by push or by dispatch.
|
|
26
|
+
TAG: ${{ inputs.tag || github.ref_name }}
|
|
27
|
+
steps:
|
|
28
|
+
# Before checkout, because checkout resolves the input as an arbitrary ref
|
|
29
|
+
# — and this workflow needs the guard more than publish.yml does. It holds
|
|
30
|
+
# `contents: write`, and action-gh-release CREATES a tag when tag_name does
|
|
31
|
+
# not resolve to one, so an unvalidated `tag: main` would check out cleanly
|
|
32
|
+
# and leave refs/tags/main plus a release named for it. The push path is
|
|
33
|
+
# constrained by the v[0-9]* filter; the dispatch path was not constrained
|
|
34
|
+
# at all.
|
|
35
|
+
- name: Validate release tag format
|
|
36
|
+
if: github.event_name == 'workflow_dispatch'
|
|
37
|
+
env:
|
|
38
|
+
INPUT_TAG: ${{ inputs.tag }}
|
|
39
|
+
run: |
|
|
40
|
+
set -euo pipefail
|
|
41
|
+
if [[ ! "$INPUT_TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
|
42
|
+
echo "tag must look like vX.Y.Z, got: $INPUT_TAG" >&2
|
|
43
|
+
exit 1
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
|
47
|
+
with:
|
|
48
|
+
ref: ${{ inputs.tag || github.ref_name }}
|
|
49
|
+
|
|
50
|
+
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
|
|
51
|
+
with:
|
|
52
|
+
python-version: '3.12'
|
|
53
|
+
|
|
54
|
+
- name: Read package metadata
|
|
55
|
+
id: meta
|
|
56
|
+
run: |
|
|
57
|
+
pkg_name=$(python -c "import tomllib,pathlib; print(tomllib.loads(pathlib.Path('pyproject.toml').read_text())['project']['name'])")
|
|
58
|
+
pkg_version="${TAG#v}"
|
|
59
|
+
echo "name=${pkg_name}" >> "$GITHUB_OUTPUT"
|
|
60
|
+
echo "version=${pkg_version}" >> "$GITHUB_OUTPUT"
|
|
61
|
+
|
|
62
|
+
- name: Extract changelog notes
|
|
63
|
+
id: notes
|
|
64
|
+
run: |
|
|
65
|
+
set -euo pipefail
|
|
66
|
+
version="${TAG#v}"
|
|
67
|
+
if [[ -f CHANGELOG.md ]]; then
|
|
68
|
+
body="$(python scripts/extract-changelog.py "$version")"
|
|
69
|
+
else
|
|
70
|
+
body="Release ${version}."
|
|
71
|
+
fi
|
|
72
|
+
delimiter="EOF_${RANDOM}_${RANDOM}"
|
|
73
|
+
{
|
|
74
|
+
echo "body<<${delimiter}"
|
|
75
|
+
echo "$body"
|
|
76
|
+
echo "${delimiter}"
|
|
77
|
+
} >> "$GITHUB_OUTPUT"
|
|
78
|
+
|
|
79
|
+
- name: Create GitHub Release
|
|
80
|
+
uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 # v2.2.2
|
|
81
|
+
with:
|
|
82
|
+
tag_name: ${{ inputs.tag || github.ref_name }}
|
|
83
|
+
name: ${{ steps.meta.outputs.name }} ${{ steps.meta.outputs.version }}
|
|
84
|
+
body: ${{ steps.notes.outputs.body }}
|
|
85
|
+
generate_release_notes: false
|
|
86
|
+
# Let GitHub decide by tag date/semver rather than by run order. On a
|
|
87
|
+
# push the tag is the newest version and becomes latest; on a dispatch
|
|
88
|
+
# repairing an older tag, a newer release keeps the badge. Note `false`
|
|
89
|
+
# is not "leave alone" — it explicitly marks a release NOT latest, so
|
|
90
|
+
# gating on the event would demote the newest tag in the very case this
|
|
91
|
+
# trigger exists for: repairing its Release after a failed push run.
|
|
92
|
+
make_latest: legacy
|
|
@@ -7,6 +7,45 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
|
|
11
|
+
## [0.12.1] - 2026-08-18
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- fix(load): submit managed loads as a job and poll, instead of holding one request open
|
|
16
|
+
|
|
17
|
+
## [0.12.0] - 2026-08-11
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- Table storage layout, both directions. `add_managed_table()` and
|
|
22
|
+
`create_managed_database()` take `partition_by` / `sorted_by`, and
|
|
23
|
+
`managed_table_layout()` reads back what was actually declared as a
|
|
24
|
+
`TableLayout`. `TablePartitionKey` and `TableSortKey` are re-exported so
|
|
25
|
+
callers need one import.
|
|
26
|
+
|
|
27
|
+
Both halves matter because a layout is fixed when the table is created and
|
|
28
|
+
there is no alter path: a table declared without one keeps that shape until it
|
|
29
|
+
is recreated and its data rewritten. So declaring is not enough — a caller has
|
|
30
|
+
to be able to confirm it took, and to refuse to load when it cannot.
|
|
31
|
+
|
|
32
|
+
`managed_table_layout()` raises `KeyError` for a table that is not declared,
|
|
33
|
+
rather than returning an empty layout. "Not there" and "declared without a
|
|
34
|
+
layout" lead to opposite decisions for a caller.
|
|
35
|
+
|
|
36
|
+
Until now this package could not express a layout at all, which is why at least
|
|
37
|
+
one consumer hand-built the HTTP request instead. The generated key models are
|
|
38
|
+
passed through rather than wrapped, so the transform vocabulary stays exactly
|
|
39
|
+
the API's.
|
|
40
|
+
|
|
41
|
+
### Changed
|
|
42
|
+
|
|
43
|
+
- Require `hotdata>=0.9.0,<0.10`. 0.9.0 is the first release whose models carry
|
|
44
|
+
`partition_by` / `sorted_by` on the add-table request, the create-database
|
|
45
|
+
table declarations, and the table-info response. On an older `hotdata` the
|
|
46
|
+
fields would be silently dropped by the model and the table declared without a
|
|
47
|
+
layout, returning success — which is the failure this feature exists to end.
|
|
48
|
+
|
|
10
49
|
## [0.11.0] - 2026-08-11
|
|
11
50
|
|
|
12
51
|
### Changed
|
|
@@ -31,6 +31,9 @@ The supported import surface is:
|
|
|
31
31
|
- `WorkspaceSelection`
|
|
32
32
|
- `ManagedDatabase`
|
|
33
33
|
- `ManagedTable`
|
|
34
|
+
- `TableLayout`
|
|
35
|
+
- `TablePartitionKey`
|
|
36
|
+
- `TableSortKey`
|
|
34
37
|
- `LoadManagedTableResult`
|
|
35
38
|
- `CreateIndexResult`
|
|
36
39
|
- `DEFAULT_SCHEMA`
|
|
@@ -56,6 +59,8 @@ Adapters should import from `hotdata_framework` and treat this surface as the st
|
|
|
56
59
|
adapters should pass `connection_id` when known.
|
|
57
60
|
- `uploads()` returns the uploads API wrapper for parquet staging.
|
|
58
61
|
- `list_managed_databases()` returns all databases via the `/databases` API.
|
|
62
|
+
- `add_managed_table(...)` and `create_managed_database(...)` accept `partition_by` / `sorted_by` to declare a table's storage layout. The layout is fixed when the table is created and cannot be altered afterwards, so omitting it is permanent for that table.
|
|
63
|
+
- `managed_table_layout(database, table, schema=...)` returns the declared layout as `TableLayout`. Empty lists mean no layout was declared — sound only because the table is resolved through a managed database. Raises `KeyError` when the table is not declared, keeping "absent" distinct from "declared without a layout".
|
|
59
64
|
- `resolve_managed_database(name_or_id)` resolves a database by id (direct lookup) or description (list scan). A `403` from `/databases` surfaces as `RuntimeError` (forbidden, not absent), preserving the underlying `ApiException` as `__cause__`.
|
|
60
65
|
- `create_managed_database(description=..., schema=..., tables=..., expires_at=...)` creates a database via the `/databases` API and optionally declares tables up front. Returns a `ManagedDatabase` (id + `default_connection_id`) sufficient to load without a further read.
|
|
61
66
|
- `delete_managed_database(name_or_id)` deletes a database via the `/databases` API.
|
|
@@ -64,7 +69,7 @@ Adapters should import from `hotdata_framework` and treat this surface as the st
|
|
|
64
69
|
- `load_managed_table(database, table, schema=..., upload_id=..., file=...)` publishes parquet data into a declared managed table.
|
|
65
70
|
- `delete_managed_table(database, table, schema=...)` deletes a managed table.
|
|
66
71
|
- `create_index(database, table, schema=..., columns=..., index_type=..., index_name=...)` builds a `"sorted"`, `"bm25"`, or `"vector"` index on a managed table and returns a `CreateIndexResult`. It is the framework-side equivalent of the CLI's `hotdata indexes create`; indexing a table on a plain (non-managed) connection is out of scope. `index_name` defaults to `{table}_{columns}_{index_type}`, matching the CLI's derivation when `--name` is omitted. `index_type` is required rather than defaulting to the API's `"sorted"`. The build runs as a background job; the call polls it to a terminal state and raises `RuntimeError` with the job's `error_message` when it fails, because the submit call reports success regardless. `wait=False` returns as soon as the job is accepted, with `status="pending"` and a `job_id` for the caller to poll. For `index_type="vector"`, omitting `embedding_provider_id` indexes an existing vector column and `metric` (`"l2"`, `"cosine"`, `"dot"`) selects the distance function the index accelerates — a query using a different function silently falls back to a full scan; setting `embedding_provider_id` indexes a source *text* column instead, and the returned `source_column` names the column to pass to `vector_distance`. Argument combinations the server would silently ignore raise `ValueError` before any request is sent.
|
|
67
|
-
- The `database` argument of `list_managed_tables`, `load_managed_table`, `add_managed_table`, `delete_managed_table`, `delete_managed_database`, `create_index`, and `execute_sql` accepts a name/id **or** an already-resolved `ManagedDatabase`. Passing a `ManagedDatabase` skips the name/id read probe, so a create-scoped key that cannot read `/databases` can load into a database it just created.
|
|
72
|
+
- The `database` argument of `list_managed_tables`, `load_managed_table`, `add_managed_table`, `delete_managed_table`, `delete_managed_database`, `create_index`, `managed_table_layout`, and `execute_sql` accepts a name/id **or** an already-resolved `ManagedDatabase`. Passing a `ManagedDatabase` skips the name/id read probe, so a create-scoped key that cannot read `/databases` can load into a database it just created.
|
|
68
73
|
|
|
69
74
|
### `QueryResult`
|
|
70
75
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: hotdata-framework
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.12.1
|
|
4
4
|
Summary: Python framework for building Hotdata integrations: workspace 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
|
|
@@ -21,7 +21,7 @@ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
|
21
21
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
22
|
Classifier: Typing :: Typed
|
|
23
23
|
Requires-Python: >=3.10
|
|
24
|
-
Requires-Dist: hotdata<0.
|
|
24
|
+
Requires-Dist: hotdata<0.10,>=0.9.0
|
|
25
25
|
Requires-Dist: pandas>=2.0
|
|
26
26
|
Requires-Dist: pyarrow>=14.0
|
|
27
27
|
Description-Content-Type: text/markdown
|
|
@@ -34,6 +34,27 @@ Pushing a `vX.Y.Z` tag triggers two workflows:
|
|
|
34
34
|
| `publish.yml` | Build wheel/sdist and publish to PyPI |
|
|
35
35
|
| `release.yml` | Create the GitHub Release with notes from `CHANGELOG.md` |
|
|
36
36
|
|
|
37
|
+
## If a release workflow fails
|
|
38
|
+
|
|
39
|
+
Both workflows also accept a manual re-run against an existing tag, so a failure
|
|
40
|
+
unrelated to the code — a stale action pin, a PyPI outage — does not require
|
|
41
|
+
deleting the tag or burning a version number:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
gh workflow run "Publish to PyPI" --ref main -f tag=vX.Y.Z
|
|
45
|
+
gh workflow run "GitHub Release" --ref main -f tag=vX.Y.Z
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
`--ref main` selects the workflow *definition*, so a fix to the workflow file
|
|
49
|
+
itself is picked up; everything it runs — including `scripts/extract-changelog.py`
|
|
50
|
+
— still comes from the tag, since that is what is being built and released. The
|
|
51
|
+
two refs serve different purposes, which is why they can differ.
|
|
52
|
+
|
|
53
|
+
A version is only spent once PyPI has accepted an upload. If the publish failed
|
|
54
|
+
before that, the same version can still be published — check with
|
|
55
|
+
`curl -s -o /dev/null -w '%{http_code}' https://pypi.org/pypi/<pkg>/<version>/json`
|
|
56
|
+
returning 404.
|
|
57
|
+
|
|
37
58
|
## Enforcement
|
|
38
59
|
|
|
39
60
|
- **PR check** (`check-release.yml`): if `pyproject.toml` version changes, `CHANGELOG.md` must contain a matching `## [X.Y.Z]` section.
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
from importlib.metadata import PackageNotFoundError, version
|
|
4
4
|
|
|
5
|
+
from hotdata.models.table_partition_key import TablePartitionKey
|
|
6
|
+
from hotdata.models.table_sort_key import TableSortKey
|
|
7
|
+
|
|
5
8
|
from hotdata_framework.client import (
|
|
6
9
|
HotdataClient,
|
|
7
10
|
ResultSummary,
|
|
@@ -14,6 +17,7 @@ from hotdata_framework.databases import (
|
|
|
14
17
|
LoadManagedTableResult,
|
|
15
18
|
ManagedDatabase,
|
|
16
19
|
ManagedTable,
|
|
20
|
+
TableLayout,
|
|
17
21
|
is_parquet_path,
|
|
18
22
|
)
|
|
19
23
|
from hotdata_framework.env import (
|
|
@@ -55,6 +59,9 @@ __all__ = [
|
|
|
55
59
|
"QueryResult",
|
|
56
60
|
"ResultSummary",
|
|
57
61
|
"RunHistoryItem",
|
|
62
|
+
"TableLayout",
|
|
63
|
+
"TablePartitionKey",
|
|
64
|
+
"TableSortKey",
|
|
58
65
|
"WorkspaceSelection",
|
|
59
66
|
"__version__",
|
|
60
67
|
"classify_sdk_error",
|