diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0744f22..f8195e1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,7 +52,8 @@ jobs: - name: Bootstrap patched pip trust uses: durable-workflow/.github/.github/actions/bootstrap-patched-pip@75587fc33ff40c34f32ef777293dad56bb6f48c7 # clean-trust - name: Install policy dependencies - run: python -m pip install PyYAML==6.0.2 + run: >- + python -m pip install --only-binary :all: PyYAML==6.0.2 - name: Require centrally approved immutable action commits run: >- python .central-action-policy/scripts/qualification_policy.py validate @@ -191,7 +192,8 @@ jobs: uses: durable-workflow/.github/.github/actions/bootstrap-patched-pip@75587fc33ff40c34f32ef777293dad56bb6f48c7 # clean-trust with: clients: pip,python - - run: pip install build twine + - run: >- + pip install --only-binary :all: build==1.6.1 twine==7.0.0 - run: python -m build - name: Compare built release metadata with the exact source commit run: python scripts/check_release_metadata.py --source-ref "$(git rev-parse HEAD)" --dist dist diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 43c9b71..93e6e51 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -113,7 +113,8 @@ jobs: clients: pip,python - name: Install build tools - run: pip install build twine + run: >- + pip install --only-binary :all: build==1.6.1 twine==7.0.0 - name: Build package run: python -m build diff --git a/CHANGELOG.md b/CHANGELOG.md index a84cd8c..051bf08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [2.17.1] - 2026-11-13 + ### Fixed - `Client` refuses supplied `token`, `control_token` and `worker_token` values with edge whitespace or control characters before choosing a role or opening an HTTP client. The error names only the option. Values are never trimmed, and empty values retain their existing fallback behavior. (#304) diff --git a/docs/reference/client.md b/docs/reference/client.md index 42f9dff..7440843 100644 --- a/docs/reference/client.md +++ b/docs/reference/client.md @@ -1,5 +1,50 @@ # Client +## Read an update outcome + +`Client.update_workflow()` returns a Server receipt. Its default wait stage is +`accepted`: the handler may still be running when the call returns. Keep the +receipt's `update_id` to read the result later without submitting another update. + +```python +from durable_workflow import UpdateStillPending + +receipt = await client.update_workflow( + "order-1", "rename", args=["new name"], wait_for="accepted", +) +update_id = receipt["update_id"] + +try: + result = await client.update_outcome("order-1", update_id, wait_timeout_seconds=30) +except UpdateStillPending as pending: + update_id = pending.update_id + print(f"Update {update_id} is still running; read its outcome again later.") +else: + print(result) # A completed handler may return None. +``` + +`update_outcome()` waits for completion by default. A pending update raises +`UpdateStillPending`, carrying the workflow, run, update and request identities. +It never returns pending work as `None`. To inspect the raw receipt without +waiting for completion, call `update_outcome_receipt()`; pass +`wait_for="completed"` to wait there too. Reads follow an update across +continue-as-new and never admit another update. + +| Outcome | Result | +| --- | --- | +| Completed | The decoded handler result, including `None` | +| Still running | `UpdateStillPending`; keep its `update_id` and read again | +| Handler failed or validator rejected | `UpdateFailed` or `UpdateRejected` | +| No retained update with this id | `UpdateNotFound`, including after pruning | +| Response does not identify the requested update or a recognized status | `UnrecognizedUpdateOutcome` | + +Both read methods require Server discovery to advertise +`capabilities.update_outcome_reads`. An unsupported Server raises +`RuntimeCapabilityUnsupported`; it does not fall back to resubmitting the update. +`WorkflowHandle` and `durable_workflow.sync.Client` provide the same methods. +See the [complete update reference](../sdk-reference.md#workflow-signals-queries-and-updates) +for wait stages, validation and response checks. + ## Trust a private certificate authority The `tls_context` option requires Python SDK 2.10.0 or later. diff --git a/pyproject.toml b/pyproject.toml index e20b853..c71e776 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "durable-workflow" -version = "2.17.0" +version = "2.17.1" description = "Python client and worker SDK for Durable Workflow Cloud and self-hosted Server" readme = "README.md" requires-python = ">=3.10" @@ -79,8 +79,8 @@ durable-workflow-replay-conformance = "durable_workflow.replay_conformance:main" durable-workflow-workflow-updates-conformance = "durable_workflow.workflow_updates_conformance:main" [tool.durable-workflow] -product-train = "2.17.0" -registry-version = "2.17.0" +product-train = "2.17.1" +registry-version = "2.17.1" supported-server-versions = "2.8.0" supported-server-release-baselines = { "2" = "2.8.0", "3" = "3.0.0" } worker-protocol-version = "1.20"