durable-workflow 2.5.5__tar.gz → 2.5.6__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.5.5/src/durable_workflow.egg-info → durable_workflow-2.5.6}/PKG-INFO +1 -1
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/pyproject.toml +3 -3
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/src/durable_workflow/worker.py +11 -7
- {durable_workflow-2.5.5 → durable_workflow-2.5.6/src/durable_workflow.egg-info}/PKG-INFO +1 -1
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_replay_regression_corpus.py +42 -1
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_worker.py +55 -1
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/LICENSE +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/README.md +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/setup.cfg +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/src/durable_workflow/__init__.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/src/durable_workflow/_activity_process.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/src/durable_workflow/_avro.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/src/durable_workflow/_cancellation_scope.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/src/durable_workflow/_cancellation_scope_history.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/src/durable_workflow/_cooperative_cancellation.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/src/durable_workflow/_prepared_local_activity.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/src/durable_workflow/_sticky_workflow_cache.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/src/durable_workflow/activity.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/src/durable_workflow/auth_composition.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/src/durable_workflow/cancellation.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/src/durable_workflow/client.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/src/durable_workflow/errors.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/src/durable_workflow/external_storage.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/src/durable_workflow/external_task_input.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/src/durable_workflow/external_task_result.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/src/durable_workflow/history_bundle_verify.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/src/durable_workflow/interceptors.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/src/durable_workflow/invocable.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/src/durable_workflow/metrics.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/src/durable_workflow/nexus.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/src/durable_workflow/py.typed +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/src/durable_workflow/python_conformance.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/src/durable_workflow/replay_conformance.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/src/durable_workflow/replay_verify.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/src/durable_workflow/retry_policy.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/src/durable_workflow/serializer.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/src/durable_workflow/sync.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/src/durable_workflow/testing.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/src/durable_workflow/worker_session.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/src/durable_workflow/workflow.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/src/durable_workflow/workflow_updates_conformance.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/src/durable_workflow.egg-info/SOURCES.txt +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/src/durable_workflow.egg-info/dependency_links.txt +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/src/durable_workflow.egg-info/entry_points.txt +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/src/durable_workflow.egg-info/requires.txt +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/src/durable_workflow.egg-info/top_level.txt +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_activity_cancellation_policies.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_activity_context.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_activity_process.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_auth_composition.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_avro_value_benchmark.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_avro_value_protocol.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_cancellation_context.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_cancellation_remaining_time.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_cancellation_scope_admission.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_cancellation_scope_authoring.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_cancellation_scope_boundary.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_cancellation_scope_opening.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_child_workflow_policies.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_ci_checkout.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_ci_integration_endpoint.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_client.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_client_timeouts.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_codec_regression_corpus.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_committed_cancellation_scope_descendants.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_committed_cancellation_scope_groups.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_committed_cancellation_scope_history.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_committed_cancellation_scope_replay.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_condition_replay_drift.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_control_plane_parity_fixtures.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_cooperative_cancellation.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_cooperative_cancellation_client.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_cooperative_cancellation_worker.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_cooperative_remote_worker.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_docstring_cross_references.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_durable_selection.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_errors.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_external_storage.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_external_task_input.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_external_task_result.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_golden_history_replay.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_history_bundle_verify.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_history_event_contract.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_invocable.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_message_streams.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_metrics.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_nexus_namespace.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_nexus_wire.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_order_processing_example.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_parallel_completion_order_replay.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_parallel_saga_authoring.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_patch_history.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_pending_operation_replay.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_prepared_local_activity_client.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_prepared_local_activity_groups.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_prepared_local_activity_policies.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_prepared_local_activity_worker.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_public_boundary_scanner.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_pypi_project_surface.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_python_conformance.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_queries.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_readme_quickstart.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_redrive_replay.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_registration_ambiguity.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_release_metadata.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_replay.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_replay_conformance.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_replay_verify.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_retry_policy.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_run_cancellation_scope_replay.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_runtime_external_payload_transport.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_schedules.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_scoped_run_cancellation_context.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_serializer.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_signals.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_sleep.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_standalone_activity_client.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_sticky_execution.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_storage_admission.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_sync.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_testing_harness.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_update_signal_condition_replay.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_updates.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_wait_condition.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_worker_session.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_workflow_entrypoint_validation.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_workflow_result_timeout.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_workflow_streams.py +0 -0
- {durable_workflow-2.5.5 → durable_workflow-2.5.6}/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.5.
|
|
7
|
+
version = "2.5.6"
|
|
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.5.
|
|
75
|
-
registry-version = "2.5.
|
|
74
|
+
product-train = "2.5.6"
|
|
75
|
+
registry-version = "2.5.6"
|
|
76
76
|
supported-server-versions = "2.5.16"
|
|
77
77
|
worker-protocol-version = "1.19"
|
|
78
78
|
control-plane-version = "2"
|
|
@@ -4077,11 +4077,12 @@ class Worker:
|
|
|
4077
4077
|
try:
|
|
4078
4078
|
if self._stop.is_set():
|
|
4079
4079
|
return
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
|
|
4080
|
+
loops = [asyncio.create_task(self._heartbeat_loop())]
|
|
4081
|
+
if self.workflows:
|
|
4082
|
+
loops.append(asyncio.create_task(self._poll_workflow_tasks()))
|
|
4083
|
+
if self.activities:
|
|
4084
|
+
loops.append(asyncio.create_task(self._poll_activity_tasks()))
|
|
4085
|
+
if self.workflows and self._query_tasks_supported:
|
|
4085
4086
|
if self._can_clone_client_for_query_tasks():
|
|
4086
4087
|
self._start_query_task_thread()
|
|
4087
4088
|
else:
|
|
@@ -4243,7 +4244,7 @@ class Worker:
|
|
|
4243
4244
|
if self._stop.is_set():
|
|
4244
4245
|
raise asyncio.CancelledError
|
|
4245
4246
|
background_tasks.append(asyncio.create_task(self._heartbeat_loop()))
|
|
4246
|
-
if self._query_tasks_supported:
|
|
4247
|
+
if self.workflows and self._query_tasks_supported:
|
|
4247
4248
|
if self._can_clone_client_for_query_tasks():
|
|
4248
4249
|
self._start_query_task_thread()
|
|
4249
4250
|
else:
|
|
@@ -4295,7 +4296,7 @@ class Worker:
|
|
|
4295
4296
|
f"workflow {workflow_id} not terminal after {timeout}s (status={desc.status})"
|
|
4296
4297
|
)
|
|
4297
4298
|
|
|
4298
|
-
if next_task_kind == "workflow":
|
|
4299
|
+
if self.workflows and (next_task_kind == "workflow" or not self.activities):
|
|
4299
4300
|
await self._reserve_workflow_capacity()
|
|
4300
4301
|
try:
|
|
4301
4302
|
poll_start = time.perf_counter()
|
|
@@ -4347,6 +4348,9 @@ class Worker:
|
|
|
4347
4348
|
next_task_kind = "workflow"
|
|
4348
4349
|
continue
|
|
4349
4350
|
|
|
4351
|
+
if not self.activities:
|
|
4352
|
+
await asyncio.sleep(poll_interval)
|
|
4353
|
+
continue
|
|
4350
4354
|
self._validate_cooperative_activity_handlers()
|
|
4351
4355
|
if len(self._remote_activity_processes) >= self.max_concurrent_activity_tasks:
|
|
4352
4356
|
next_task_kind = "workflow"
|
|
@@ -9,7 +9,7 @@ from unittest.mock import AsyncMock
|
|
|
9
9
|
import pytest
|
|
10
10
|
|
|
11
11
|
from durable_workflow import Replayer, Worker, serializer, workflow
|
|
12
|
-
from durable_workflow.client import Client, WorkflowStreamAppendItem
|
|
12
|
+
from durable_workflow.client import PROTOCOL_VERSION, Client, WorkflowExecution, WorkflowStreamAppendItem
|
|
13
13
|
from durable_workflow.errors import NonDeterministicReplayError, WorkflowCancelled, WorkflowPayloadDecodeError
|
|
14
14
|
from durable_workflow.workflow import (
|
|
15
15
|
LocalActivityExecutionAborted,
|
|
@@ -31,6 +31,7 @@ from tests.test_update_signal_condition_replay import (
|
|
|
31
31
|
PostConditionReceiversWorkflow,
|
|
32
32
|
UpdateSignalConditionTimerWorkflow,
|
|
33
33
|
)
|
|
34
|
+
from tests.test_worker import compatible_cluster_info
|
|
34
35
|
|
|
35
36
|
FIXTURE_SCHEMA = "durable-workflow.replay-regression/v1"
|
|
36
37
|
FIXTURE_DIR = Path(__file__).parent / "fixtures" / "replay_regressions"
|
|
@@ -489,6 +490,46 @@ def test_checked_in_replay_regression_corpus_uses_official_replayer(
|
|
|
489
490
|
_execute_fixture(fixture)
|
|
490
491
|
|
|
491
492
|
|
|
493
|
+
@pytest.mark.asyncio
|
|
494
|
+
async def test_workflow_only_driver_resumes_external_activity_without_activity_polling() -> None:
|
|
495
|
+
fixture = json.loads(
|
|
496
|
+
(FIXTURE_DIR / "workflow-only-worker-resumes-external-activity.json").read_text(encoding="utf-8")
|
|
497
|
+
)
|
|
498
|
+
client = AsyncMock(spec=Client)
|
|
499
|
+
client.get_cluster_info.return_value = compatible_cluster_info(worker_protocol={"version": PROTOCOL_VERSION})
|
|
500
|
+
client.register_worker.return_value = {"registered": True}
|
|
501
|
+
client.deregister_worker_registration.return_value = {"outcome": "deregistered"}
|
|
502
|
+
client.poll_activity_task.side_effect = AssertionError("workflow-only worker must not poll activities")
|
|
503
|
+
client.poll_workflow_task.side_effect = [None, {
|
|
504
|
+
"task_id": "external-activity-resume-task", "workflow_id": "external-activity-caller",
|
|
505
|
+
"run_id": "original-run", "workflow_type": fixture["workflow"]["type"],
|
|
506
|
+
"workflow_task_attempt": 1, "history_events": fixture["history"],
|
|
507
|
+
"arguments": serializer.encode(fixture["workflow"]["input"], codec="avro"),
|
|
508
|
+
"payload_codec": "avro",
|
|
509
|
+
}]
|
|
510
|
+
client.complete_workflow_task.return_value = {"outcome": "completed"}
|
|
511
|
+
|
|
512
|
+
async def describe(workflow_id: str) -> WorkflowExecution:
|
|
513
|
+
return WorkflowExecution(
|
|
514
|
+
workflow_id=workflow_id, run_id="original-run", workflow_type=fixture["workflow"]["type"],
|
|
515
|
+
status="completed" if client.complete_workflow_task.await_count else "running",
|
|
516
|
+
)
|
|
517
|
+
|
|
518
|
+
client.describe_workflow.side_effect = describe
|
|
519
|
+
worker = Worker(client, task_queue="external-activity", workflows=[GoldenSingleActivityWorkflow], activities=[])
|
|
520
|
+
|
|
521
|
+
result = await worker.run_until(workflow_id="external-activity-caller", timeout=1, poll_interval=0.01)
|
|
522
|
+
|
|
523
|
+
assert result.status == "completed"
|
|
524
|
+
assert result.run_id == "original-run"
|
|
525
|
+
assert client.poll_workflow_task.await_count == 2
|
|
526
|
+
client.poll_activity_task.assert_not_awaited()
|
|
527
|
+
client.complete_workflow_task.assert_awaited_once()
|
|
528
|
+
commands = client.complete_workflow_task.call_args.kwargs["commands"]
|
|
529
|
+
assert [command["type"] for command in commands] == [fixture["expected"]["type"]]
|
|
530
|
+
assert serializer.decode_envelope(commands[0]["result"]) == fixture["expected"]["result"]
|
|
531
|
+
|
|
532
|
+
|
|
492
533
|
@pytest.mark.asyncio
|
|
493
534
|
async def test_worker_replays_recorded_side_effects_across_history_pages() -> None:
|
|
494
535
|
fixture = json.loads((FIXTURE_DIR / "paged-recorded-side-effects.json").read_text(encoding="utf-8"))
|
|
@@ -4403,6 +4403,8 @@ class TestWorkerShutdown:
|
|
|
4403
4403
|
client,
|
|
4404
4404
|
task_queue="orders",
|
|
4405
4405
|
worker_id="worker-only",
|
|
4406
|
+
workflows=[TestWorkflow],
|
|
4407
|
+
activities=[echo_activity],
|
|
4406
4408
|
poll_timeout=0.01,
|
|
4407
4409
|
heartbeat_interval=0.01,
|
|
4408
4410
|
)
|
|
@@ -4597,7 +4599,7 @@ class TestWorkerShutdown:
|
|
|
4597
4599
|
},
|
|
4598
4600
|
)
|
|
4599
4601
|
mock_client.deregister_worker_registration.side_effect = cleanup_error
|
|
4600
|
-
worker = Worker(mock_client, task_queue="q1")
|
|
4602
|
+
worker = Worker(mock_client, task_queue="q1", workflows=[TestWorkflow])
|
|
4601
4603
|
worker._poll_workflow_tasks = AsyncMock( # type: ignore[method-assign]
|
|
4602
4604
|
side_effect=RuntimeError("worker loop failed")
|
|
4603
4605
|
)
|
|
@@ -4729,6 +4731,31 @@ class TestWorkerShutdown:
|
|
|
4729
4731
|
|
|
4730
4732
|
|
|
4731
4733
|
class TestPollLoops:
|
|
4734
|
+
@pytest.mark.asyncio
|
|
4735
|
+
@pytest.mark.parametrize("has_workflows,has_activities", [(True, False), (False, True), (False, False)])
|
|
4736
|
+
async def test_run_polls_only_registered_task_families(
|
|
4737
|
+
self, mock_client: AsyncMock, has_workflows: bool, has_activities: bool,
|
|
4738
|
+
) -> None:
|
|
4739
|
+
worker = Worker(
|
|
4740
|
+
mock_client,
|
|
4741
|
+
task_queue="q1",
|
|
4742
|
+
workflows=[TestWorkflow] if has_workflows else [],
|
|
4743
|
+
activities=[echo_activity] if has_activities else [],
|
|
4744
|
+
poll_timeout=0.01,
|
|
4745
|
+
)
|
|
4746
|
+
run_task = asyncio.create_task(worker.run())
|
|
4747
|
+
try:
|
|
4748
|
+
await asyncio.sleep(0.05)
|
|
4749
|
+
finally:
|
|
4750
|
+
await worker.stop()
|
|
4751
|
+
await run_task
|
|
4752
|
+
|
|
4753
|
+
assert bool(mock_client.poll_workflow_task.await_count) is has_workflows
|
|
4754
|
+
assert bool(mock_client.poll_activity_task.await_count) is has_activities
|
|
4755
|
+
assert bool(mock_client.poll_query_task.await_count) is has_workflows
|
|
4756
|
+
mock_client.register_worker.assert_awaited_once()
|
|
4757
|
+
mock_client.deregister_worker_registration.assert_awaited_once_with(worker.worker_id)
|
|
4758
|
+
|
|
4732
4759
|
@pytest.mark.asyncio
|
|
4733
4760
|
async def test_run_starts_both_loops(self, mock_client: AsyncMock) -> None:
|
|
4734
4761
|
worker = Worker(
|
|
@@ -4990,6 +5017,33 @@ class TestWorkerHeartbeats:
|
|
|
4990
5017
|
|
|
4991
5018
|
|
|
4992
5019
|
class TestRunUntil:
|
|
5020
|
+
@pytest.mark.asyncio
|
|
5021
|
+
@pytest.mark.parametrize("has_workflows,has_activities", [(True, False), (False, True), (False, False)])
|
|
5022
|
+
async def test_run_until_polls_only_registered_task_families(
|
|
5023
|
+
self, mock_client: AsyncMock, has_workflows: bool, has_activities: bool,
|
|
5024
|
+
) -> None:
|
|
5025
|
+
mock_client.describe_workflow = AsyncMock(side_effect=[
|
|
5026
|
+
WorkflowExecution(workflow_id="wf-1", run_id="run-1", workflow_type="test-wf", status="running"),
|
|
5027
|
+
WorkflowExecution(workflow_id="wf-1", run_id="run-1", workflow_type="test-wf", status="running"),
|
|
5028
|
+
WorkflowExecution(workflow_id="wf-1", run_id="run-1", workflow_type="test-wf", status="completed"),
|
|
5029
|
+
])
|
|
5030
|
+
worker = Worker(
|
|
5031
|
+
mock_client,
|
|
5032
|
+
task_queue="q1",
|
|
5033
|
+
workflows=[TestWorkflow] if has_workflows else [],
|
|
5034
|
+
activities=[echo_activity] if has_activities else [],
|
|
5035
|
+
poll_timeout=0.01,
|
|
5036
|
+
)
|
|
5037
|
+
|
|
5038
|
+
result = await worker.run_until(workflow_id="wf-1", timeout=1.0, poll_interval=0.01)
|
|
5039
|
+
|
|
5040
|
+
assert result.status == "completed"
|
|
5041
|
+
assert bool(mock_client.poll_workflow_task.await_count) is has_workflows
|
|
5042
|
+
assert bool(mock_client.poll_activity_task.await_count) is has_activities
|
|
5043
|
+
if not has_workflows:
|
|
5044
|
+
mock_client.poll_query_task.assert_not_awaited()
|
|
5045
|
+
mock_client.deregister_worker_registration.assert_awaited_once_with(worker.worker_id)
|
|
5046
|
+
|
|
4993
5047
|
@pytest.mark.asyncio
|
|
4994
5048
|
async def test_run_until_returns_terminal_description(self, mock_client: AsyncMock) -> None:
|
|
4995
5049
|
mock_client.describe_workflow = AsyncMock(
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{durable_workflow-2.5.5 → durable_workflow-2.5.6}/src/durable_workflow/_cancellation_scope.py
RENAMED
|
File without changes
|
|
File without changes
|
{durable_workflow-2.5.5 → durable_workflow-2.5.6}/src/durable_workflow/_cooperative_cancellation.py
RENAMED
|
File without changes
|
{durable_workflow-2.5.5 → durable_workflow-2.5.6}/src/durable_workflow/_prepared_local_activity.py
RENAMED
|
File without changes
|
{durable_workflow-2.5.5 → durable_workflow-2.5.6}/src/durable_workflow/_sticky_workflow_cache.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.5.5 → durable_workflow-2.5.6}/src/durable_workflow/external_task_input.py
RENAMED
|
File without changes
|
{durable_workflow-2.5.5 → durable_workflow-2.5.6}/src/durable_workflow/external_task_result.py
RENAMED
|
File without changes
|
{durable_workflow-2.5.5 → durable_workflow-2.5.6}/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.5.5 → durable_workflow-2.5.6}/src/durable_workflow/python_conformance.py
RENAMED
|
File without changes
|
{durable_workflow-2.5.5 → durable_workflow-2.5.6}/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.5.5 → durable_workflow-2.5.6}/src/durable_workflow.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{durable_workflow-2.5.5 → durable_workflow-2.5.6}/src/durable_workflow.egg-info/entry_points.txt
RENAMED
|
File without changes
|
{durable_workflow-2.5.5 → durable_workflow-2.5.6}/src/durable_workflow.egg-info/requires.txt
RENAMED
|
File without changes
|
{durable_workflow-2.5.5 → durable_workflow-2.5.6}/src/durable_workflow.egg-info/top_level.txt
RENAMED
|
File without changes
|
{durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_activity_cancellation_policies.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
|
{durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_cancellation_scope_admission.py
RENAMED
|
File without changes
|
{durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_cancellation_scope_authoring.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.5.5 → durable_workflow-2.5.6}/tests/test_committed_cancellation_scope_groups.py
RENAMED
|
File without changes
|
{durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_committed_cancellation_scope_history.py
RENAMED
|
File without changes
|
{durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_committed_cancellation_scope_replay.py
RENAMED
|
File without changes
|
|
File without changes
|
{durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_control_plane_parity_fixtures.py
RENAMED
|
File without changes
|
|
File without changes
|
{durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_cooperative_cancellation_client.py
RENAMED
|
File without changes
|
{durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_cooperative_cancellation_worker.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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_parallel_completion_order_replay.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_prepared_local_activity_client.py
RENAMED
|
File without changes
|
{durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_prepared_local_activity_groups.py
RENAMED
|
File without changes
|
{durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_prepared_local_activity_policies.py
RENAMED
|
File without changes
|
{durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_prepared_local_activity_worker.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
|
{durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_run_cancellation_scope_replay.py
RENAMED
|
File without changes
|
{durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_runtime_external_payload_transport.py
RENAMED
|
File without changes
|
|
File without changes
|
{durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_scoped_run_cancellation_context.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.5.5 → durable_workflow-2.5.6}/tests/test_update_signal_condition_replay.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_workflow_entrypoint_validation.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{durable_workflow-2.5.5 → durable_workflow-2.5.6}/tests/test_workflow_updates_conformance.py
RENAMED
|
File without changes
|