durable-workflow 2.3.8__tar.gz → 2.3.9__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.
- {durable_workflow-2.3.8/src/durable_workflow.egg-info → durable_workflow-2.3.9}/PKG-INFO +1 -1
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/pyproject.toml +3 -3
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/src/durable_workflow/client.py +1 -1
- {durable_workflow-2.3.8 → durable_workflow-2.3.9/src/durable_workflow.egg-info}/PKG-INFO +1 -1
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/src/durable_workflow.egg-info/SOURCES.txt +1 -0
- durable_workflow-2.3.9/tests/test_client_timeouts.py +93 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/LICENSE +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/README.md +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/setup.cfg +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/src/durable_workflow/__init__.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/src/durable_workflow/_avro.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/src/durable_workflow/activity.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/src/durable_workflow/auth_composition.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/src/durable_workflow/errors.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/src/durable_workflow/external_storage.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/src/durable_workflow/external_task_input.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/src/durable_workflow/external_task_result.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/src/durable_workflow/history_bundle_verify.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/src/durable_workflow/interceptors.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/src/durable_workflow/invocable.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/src/durable_workflow/metrics.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/src/durable_workflow/nexus.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/src/durable_workflow/py.typed +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/src/durable_workflow/python_conformance.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/src/durable_workflow/replay_conformance.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/src/durable_workflow/replay_verify.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/src/durable_workflow/retry_policy.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/src/durable_workflow/serializer.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/src/durable_workflow/sync.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/src/durable_workflow/testing.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/src/durable_workflow/worker.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/src/durable_workflow/worker_session.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/src/durable_workflow/workflow.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/src/durable_workflow/workflow_updates_conformance.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/src/durable_workflow.egg-info/dependency_links.txt +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/src/durable_workflow.egg-info/entry_points.txt +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/src/durable_workflow.egg-info/requires.txt +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/src/durable_workflow.egg-info/top_level.txt +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_activity_context.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_auth_composition.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_avro_value_benchmark.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_avro_value_protocol.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_ci_checkout.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_ci_integration_endpoint.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_client.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_codec_regression_corpus.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_control_plane_parity_fixtures.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_docstring_cross_references.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_durable_selection.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_errors.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_external_storage.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_external_task_input.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_external_task_result.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_golden_history_replay.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_history_bundle_verify.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_history_event_contract.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_invocable.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_message_streams.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_metrics.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_order_processing_example.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_parallel_completion_order_replay.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_parallel_saga_authoring.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_public_boundary_scanner.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_pypi_project_surface.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_python_conformance.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_queries.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_readme_quickstart.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_redrive_replay.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_release_metadata.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_replay.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_replay_conformance.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_replay_regression_corpus.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_replay_verify.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_retry_policy.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_runtime_external_payload_transport.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_schedules.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_serializer.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_signals.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_sleep.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_standalone_activity_client.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_storage_admission.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_sync.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_testing_harness.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_update_signal_condition_replay.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_updates.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_wait_condition.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_worker.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_worker_session.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_workflow_result_timeout.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_workflow_streams.py +0 -0
- {durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_workflow_updates_conformance.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "durable-workflow"
|
|
7
|
-
version = "2.3.
|
|
7
|
+
version = "2.3.9"
|
|
8
8
|
description = "Python client and worker SDK for Durable Workflow Cloud and self-hosted Server"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -71,8 +71,8 @@ durable-workflow-replay-conformance = "durable_workflow.replay_conformance:main"
|
|
|
71
71
|
durable-workflow-workflow-updates-conformance = "durable_workflow.workflow_updates_conformance:main"
|
|
72
72
|
|
|
73
73
|
[tool.durable-workflow]
|
|
74
|
-
product-train = "2.3.
|
|
75
|
-
registry-version = "2.3.
|
|
74
|
+
product-train = "2.3.9"
|
|
75
|
+
registry-version = "2.3.9"
|
|
76
76
|
supported-server-versions = "2.4.8"
|
|
77
77
|
worker-protocol-version = "1.19"
|
|
78
78
|
control-plane-version = "2"
|
|
@@ -1794,7 +1794,7 @@ class Client:
|
|
|
1794
1794
|
f"/api{path}",
|
|
1795
1795
|
headers=self._discovery_headers() if discovery else self._headers(worker=worker),
|
|
1796
1796
|
json=request_json,
|
|
1797
|
-
timeout=timeout,
|
|
1797
|
+
timeout=self.timeout if timeout is None else timeout,
|
|
1798
1798
|
)
|
|
1799
1799
|
# Raise HTTPStatusError for 4xx/5xx so retry policy can catch it
|
|
1800
1800
|
resp.raise_for_status()
|
|
@@ -40,6 +40,7 @@ tests/test_avro_value_protocol.py
|
|
|
40
40
|
tests/test_ci_checkout.py
|
|
41
41
|
tests/test_ci_integration_endpoint.py
|
|
42
42
|
tests/test_client.py
|
|
43
|
+
tests/test_client_timeouts.py
|
|
43
44
|
tests/test_codec_regression_corpus.py
|
|
44
45
|
tests/test_control_plane_parity_fixtures.py
|
|
45
46
|
tests/test_docstring_cross_references.py
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import json
|
|
5
|
+
from collections.abc import AsyncIterator
|
|
6
|
+
from contextlib import asynccontextmanager
|
|
7
|
+
|
|
8
|
+
import httpx
|
|
9
|
+
import pytest
|
|
10
|
+
|
|
11
|
+
from durable_workflow.client import Client
|
|
12
|
+
from durable_workflow.retry_policy import TransportRetryPolicy
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@asynccontextmanager
|
|
16
|
+
async def response_server(*, delay: float | None) -> AsyncIterator[str]:
|
|
17
|
+
"""A real HTTP peer, with response progress controlled by the test."""
|
|
18
|
+
writers: list[asyncio.StreamWriter] = []
|
|
19
|
+
handlers: set[asyncio.Task[None]] = set()
|
|
20
|
+
|
|
21
|
+
async def respond(reader: asyncio.StreamReader, writer: asyncio.StreamWriter) -> None:
|
|
22
|
+
task = asyncio.current_task()
|
|
23
|
+
assert task is not None
|
|
24
|
+
handlers.add(task)
|
|
25
|
+
writers.append(writer)
|
|
26
|
+
try:
|
|
27
|
+
headers = await reader.readuntil(b"\r\n\r\n")
|
|
28
|
+
for header in headers.split(b"\r\n"):
|
|
29
|
+
if header.lower().startswith(b"content-length:"):
|
|
30
|
+
await reader.readexactly(int(header.split(b":", 1)[1]))
|
|
31
|
+
if delay is None:
|
|
32
|
+
await reader.read()
|
|
33
|
+
return
|
|
34
|
+
await asyncio.sleep(delay)
|
|
35
|
+
body = json.dumps({"task": None, "poll_status": "empty"}).encode()
|
|
36
|
+
writer.write(
|
|
37
|
+
b"HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n"
|
|
38
|
+
+ f"Content-Length: {len(body)}\r\nConnection: close\r\n\r\n".encode()
|
|
39
|
+
+ body
|
|
40
|
+
)
|
|
41
|
+
await writer.drain()
|
|
42
|
+
finally:
|
|
43
|
+
writer.close()
|
|
44
|
+
await writer.wait_closed()
|
|
45
|
+
handlers.discard(task)
|
|
46
|
+
|
|
47
|
+
server = await asyncio.start_server(respond, "127.0.0.1", 0)
|
|
48
|
+
try:
|
|
49
|
+
assert server.sockets
|
|
50
|
+
yield f"http://127.0.0.1:{server.sockets[0].getsockname()[1]}"
|
|
51
|
+
finally:
|
|
52
|
+
server.close()
|
|
53
|
+
await server.wait_closed()
|
|
54
|
+
for writer in writers:
|
|
55
|
+
writer.close()
|
|
56
|
+
if handlers:
|
|
57
|
+
await asyncio.gather(*handlers)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@pytest.mark.asyncio
|
|
61
|
+
@pytest.mark.parametrize("operation", ["control", "discovery", "worker"])
|
|
62
|
+
async def test_configured_timeout_bounds_a_stalled_response(operation: str) -> None:
|
|
63
|
+
async with (
|
|
64
|
+
response_server(delay=None) as endpoint,
|
|
65
|
+
Client(endpoint, timeout=0.05, retry_policy=TransportRetryPolicy(max_attempts=1)) as client,
|
|
66
|
+
):
|
|
67
|
+
if operation == "control":
|
|
68
|
+
request = client.health()
|
|
69
|
+
elif operation == "discovery":
|
|
70
|
+
request = client.get_cluster_info()
|
|
71
|
+
else:
|
|
72
|
+
request = client.complete_workflow_task(
|
|
73
|
+
task_id="synthetic-timeout-task",
|
|
74
|
+
lease_owner="synthetic-timeout-worker",
|
|
75
|
+
workflow_task_attempt=1,
|
|
76
|
+
commands=[],
|
|
77
|
+
)
|
|
78
|
+
with pytest.raises(httpx.ReadTimeout):
|
|
79
|
+
await asyncio.wait_for(request, timeout=2)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@pytest.mark.asyncio
|
|
83
|
+
async def test_explicit_poll_timeout_overrides_a_shorter_client_default() -> None:
|
|
84
|
+
async with (
|
|
85
|
+
response_server(delay=0.1) as endpoint,
|
|
86
|
+
Client(endpoint, timeout=0.01, retry_policy=TransportRetryPolicy(max_attempts=1)) as client,
|
|
87
|
+
):
|
|
88
|
+
task = await client.poll_workflow_task(
|
|
89
|
+
worker_id="synthetic-timeout-worker",
|
|
90
|
+
task_queue="synthetic-timeout-queue",
|
|
91
|
+
timeout=0,
|
|
92
|
+
)
|
|
93
|
+
assert task is None
|
|
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
|
{durable_workflow-2.3.8 → durable_workflow-2.3.9}/src/durable_workflow/external_task_input.py
RENAMED
|
File without changes
|
{durable_workflow-2.3.8 → durable_workflow-2.3.9}/src/durable_workflow/external_task_result.py
RENAMED
|
File without changes
|
{durable_workflow-2.3.8 → durable_workflow-2.3.9}/src/durable_workflow/history_bundle_verify.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{durable_workflow-2.3.8 → durable_workflow-2.3.9}/src/durable_workflow/python_conformance.py
RENAMED
|
File without changes
|
{durable_workflow-2.3.8 → durable_workflow-2.3.9}/src/durable_workflow/replay_conformance.py
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
|
{durable_workflow-2.3.8 → durable_workflow-2.3.9}/src/durable_workflow.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{durable_workflow-2.3.8 → durable_workflow-2.3.9}/src/durable_workflow.egg-info/entry_points.txt
RENAMED
|
File without changes
|
{durable_workflow-2.3.8 → durable_workflow-2.3.9}/src/durable_workflow.egg-info/requires.txt
RENAMED
|
File without changes
|
{durable_workflow-2.3.8 → durable_workflow-2.3.9}/src/durable_workflow.egg-info/top_level.txt
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
|
{durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_control_plane_parity_fixtures.py
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
|
{durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_parallel_completion_order_replay.py
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
|
{durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_runtime_external_payload_transport.py
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
|
{durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_update_signal_condition_replay.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{durable_workflow-2.3.8 → durable_workflow-2.3.9}/tests/test_workflow_updates_conformance.py
RENAMED
|
File without changes
|