durable-workflow 2.9.0__tar.gz → 2.9.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.
- {durable_workflow-2.9.0/src/durable_workflow.egg-info → durable_workflow-2.9.1}/PKG-INFO +2 -1
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/pyproject.toml +4 -3
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/src/durable_workflow/workflow.py +7 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1/src/durable_workflow.egg-info}/PKG-INFO +2 -1
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/src/durable_workflow.egg-info/SOURCES.txt +1 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/src/durable_workflow.egg-info/requires.txt +1 -0
- durable_workflow-2.9.1/tests/test_threaded_heartbeat_example.py +125 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/LICENSE +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/README.md +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/setup.cfg +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/src/durable_workflow/__init__.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/src/durable_workflow/_activity_lease.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/src/durable_workflow/_activity_process.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/src/durable_workflow/_avro.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/src/durable_workflow/_cancellation_scope.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/src/durable_workflow/_cancellation_scope_history.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/src/durable_workflow/_cooperative_cancellation.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/src/durable_workflow/_prepared_local_activity.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/src/durable_workflow/_query_task_authority.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/src/durable_workflow/_sticky_workflow_cache.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/src/durable_workflow/activity.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/src/durable_workflow/auth_composition.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/src/durable_workflow/cancellation.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/src/durable_workflow/client.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/src/durable_workflow/errors.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/src/durable_workflow/external_storage.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/src/durable_workflow/external_task_input.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/src/durable_workflow/external_task_result.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/src/durable_workflow/history_bundle_verify.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/src/durable_workflow/interceptors.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/src/durable_workflow/invocable.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/src/durable_workflow/metrics.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/src/durable_workflow/nexus.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/src/durable_workflow/py.typed +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/src/durable_workflow/python_conformance.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/src/durable_workflow/replay_conformance.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/src/durable_workflow/replay_verify.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/src/durable_workflow/retry_policy.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/src/durable_workflow/serializer.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/src/durable_workflow/sync.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/src/durable_workflow/telemetry.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/src/durable_workflow/telemetry_carrier.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/src/durable_workflow/testing.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/src/durable_workflow/worker.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/src/durable_workflow/worker_session.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/src/durable_workflow/workflow_updates_conformance.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/src/durable_workflow.egg-info/dependency_links.txt +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/src/durable_workflow.egg-info/entry_points.txt +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/src/durable_workflow.egg-info/top_level.txt +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_activity_cancellation_policies.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_activity_context.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_activity_heartbeat_authority.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_activity_lease_authority.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_activity_lease_renewal.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_activity_lease_worker.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_activity_process.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_auth_composition.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_avro_value_benchmark.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_avro_value_protocol.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_cancellation_context.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_cancellation_remaining_time.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_cancellation_scope_admission.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_cancellation_scope_authoring.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_cancellation_scope_boundary.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_cancellation_scope_opening.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_child_workflow_policies.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_ci_checkout.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_ci_integration_endpoint.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_client.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_client_telemetry.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_client_timeouts.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_codec_regression_corpus.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_committed_cancellation_scope_descendants.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_committed_cancellation_scope_groups.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_committed_cancellation_scope_history.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_committed_cancellation_scope_replay.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_condition_replay_drift.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_control_plane_parity_fixtures.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_cooperative_cancellation.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_cooperative_cancellation_client.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_cooperative_cancellation_worker.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_cooperative_remote_worker.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_docstring_cross_references.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_durable_selection.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_errors.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_external_storage.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_external_task_input.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_external_task_result.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_golden_history_replay.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_history_bundle_verify.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_history_event_contract.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_invocable.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_message_streams.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_metrics.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_nexus_durable_wait.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_nexus_namespace.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_nexus_wire.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_order_processing_example.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_parallel_completion_order_replay.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_parallel_saga_authoring.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_patch_history.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_pending_operation_replay.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_prepared_local_activity_client.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_prepared_local_activity_groups.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_prepared_local_activity_policies.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_prepared_local_activity_worker.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_public_boundary_scanner.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_pypi_project_surface.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_python_conformance.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_queries.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_query_task_authority.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_query_timeout_retry.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_readme_quickstart.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_redrive_replay.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_registration_ambiguity.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_release_metadata.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_replay.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_replay_clock.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_replay_conformance.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_replay_regression_corpus.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_replay_verify.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_retry_policy.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_run_cancellation_scope_replay.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_runtime_external_payload_transport.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_schedules.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_scoped_run_cancellation_context.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_serializer.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_signals.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_sleep.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_standalone_activity_client.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_sticky_execution.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_storage_admission.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_sync.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_sync_activities.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_telemetry.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_telemetry_carrier.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_testing_harness.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_update_signal_condition_replay.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_updates.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_wait_condition.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_worker.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_worker_observation.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_worker_session.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_worker_telemetry_wire.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_workflow_entry_arguments.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_workflow_entrypoint_validation.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_workflow_handler_validation.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_workflow_identity.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_workflow_result_timeout.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_workflow_streams.py +0 -0
- {durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_workflow_updates_conformance.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: durable-workflow
|
|
3
|
-
Version: 2.9.
|
|
3
|
+
Version: 2.9.1
|
|
4
4
|
Summary: Python client and worker SDK for Durable Workflow Cloud and self-hosted Server
|
|
5
5
|
Author: Durable Workflow Contributors
|
|
6
6
|
License-Expression: MIT
|
|
@@ -20,6 +20,7 @@ Requires-Python: >=3.10
|
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
21
|
License-File: LICENSE
|
|
22
22
|
Requires-Dist: httpx>=0.27
|
|
23
|
+
Requires-Dist: certifi>=2026.10.19
|
|
23
24
|
Requires-Dist: fastavro<2,>=1.12.2
|
|
24
25
|
Provides-Extra: telemetry
|
|
25
26
|
Requires-Dist: opentelemetry-api<2,>=1.45.1; extra == "telemetry"
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "durable-workflow"
|
|
7
|
-
version = "2.9.
|
|
7
|
+
version = "2.9.1"
|
|
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"
|
|
@@ -34,6 +34,7 @@ classifiers = [
|
|
|
34
34
|
]
|
|
35
35
|
dependencies = [
|
|
36
36
|
"httpx>=0.27",
|
|
37
|
+
"certifi>=2026.10.19",
|
|
37
38
|
"fastavro>=1.12.2,<2",
|
|
38
39
|
]
|
|
39
40
|
|
|
@@ -75,8 +76,8 @@ durable-workflow-replay-conformance = "durable_workflow.replay_conformance:main"
|
|
|
75
76
|
durable-workflow-workflow-updates-conformance = "durable_workflow.workflow_updates_conformance:main"
|
|
76
77
|
|
|
77
78
|
[tool.durable-workflow]
|
|
78
|
-
product-train = "2.9.
|
|
79
|
-
registry-version = "2.9.
|
|
79
|
+
product-train = "2.9.1"
|
|
80
|
+
registry-version = "2.9.1"
|
|
80
81
|
supported-server-versions = "2.8.0"
|
|
81
82
|
worker-protocol-version = "1.20"
|
|
82
83
|
control-plane-version = "2"
|
|
@@ -316,6 +316,13 @@ class ActivityRetryPolicy:
|
|
|
316
316
|
budget for an already-scheduled activity. It is a server-side durable
|
|
317
317
|
retry policy, not the SDK HTTP transport retry policy.
|
|
318
318
|
|
|
319
|
+
``initial_interval_seconds`` and ``maximum_interval_seconds`` accept
|
|
320
|
+
fractional seconds. Each generated interval is capped first, then rounded
|
|
321
|
+
up to a whole second for the durable command. For example, an initial
|
|
322
|
+
interval of ``0.1`` becomes one second. The explicit ``backoff_seconds``
|
|
323
|
+
list instead takes integer seconds. This resolution does not guarantee
|
|
324
|
+
when a worker will start the next attempt.
|
|
325
|
+
|
|
319
326
|
``non_retryable_error_types`` names failure types that should bypass this
|
|
320
327
|
retry budget. An activity worker can also report ``non_retryable=True`` on
|
|
321
328
|
a failure to stop retrying that activity execution.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: durable-workflow
|
|
3
|
-
Version: 2.9.
|
|
3
|
+
Version: 2.9.1
|
|
4
4
|
Summary: Python client and worker SDK for Durable Workflow Cloud and self-hosted Server
|
|
5
5
|
Author: Durable Workflow Contributors
|
|
6
6
|
License-Expression: MIT
|
|
@@ -20,6 +20,7 @@ Requires-Python: >=3.10
|
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
21
|
License-File: LICENSE
|
|
22
22
|
Requires-Dist: httpx>=0.27
|
|
23
|
+
Requires-Dist: certifi>=2026.10.19
|
|
23
24
|
Requires-Dist: fastavro<2,>=1.12.2
|
|
24
25
|
Provides-Extra: telemetry
|
|
25
26
|
Requires-Dist: opentelemetry-api<2,>=1.45.1; extra == "telemetry"
|
|
@@ -132,6 +132,7 @@ tests/test_sync_activities.py
|
|
|
132
132
|
tests/test_telemetry.py
|
|
133
133
|
tests/test_telemetry_carrier.py
|
|
134
134
|
tests/test_testing_harness.py
|
|
135
|
+
tests/test_threaded_heartbeat_example.py
|
|
135
136
|
tests/test_update_signal_condition_replay.py
|
|
136
137
|
tests/test_updates.py
|
|
137
138
|
tests/test_wait_condition.py
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"""Exercise the public example with real ActivityContext thread/loop behavior."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import asyncio
|
|
5
|
+
import importlib.util
|
|
6
|
+
import unittest
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from threading import Event, Thread
|
|
9
|
+
from threading import enumerate as threads
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from durable_workflow import activity
|
|
13
|
+
from durable_workflow.activity import ActivityContext, ActivityInfo, _set_context
|
|
14
|
+
from durable_workflow.errors import ActivityCancelled, ActivityLeaseAuthorityLost
|
|
15
|
+
|
|
16
|
+
SPEC = importlib.util.spec_from_file_location(
|
|
17
|
+
"threaded_heartbeats_example",
|
|
18
|
+
Path(__file__).resolve().parents[1] / "examples" / "threaded_heartbeats.py",
|
|
19
|
+
)
|
|
20
|
+
assert SPEC is not None
|
|
21
|
+
assert SPEC.loader is not None
|
|
22
|
+
EXAMPLE = importlib.util.module_from_spec(SPEC)
|
|
23
|
+
SPEC.loader.exec_module(EXAMPLE)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class HeartbeatClient:
|
|
27
|
+
def __init__(self, response: Any = None) -> None:
|
|
28
|
+
self.response = response if response is not None else {"can_continue": True}
|
|
29
|
+
self.calls: list[dict[str, Any]] = []
|
|
30
|
+
self.observed = Event()
|
|
31
|
+
|
|
32
|
+
async def heartbeat_activity_task(self, **request: Any) -> Any:
|
|
33
|
+
self.calls.append(request)
|
|
34
|
+
self.observed.set()
|
|
35
|
+
if isinstance(self.response, Exception):
|
|
36
|
+
raise self.response
|
|
37
|
+
return self.response
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class ThreadedHeartbeatExampleTest(unittest.IsolatedAsyncioTestCase):
|
|
41
|
+
async def asyncSetUp(self) -> None:
|
|
42
|
+
self.client = HeartbeatClient()
|
|
43
|
+
self.ctx = ActivityContext(
|
|
44
|
+
info=ActivityInfo(
|
|
45
|
+
"task-original", "export", "attempt-original", 2, "reports", "worker-original",
|
|
46
|
+
),
|
|
47
|
+
client=self.client, # type: ignore[arg-type]
|
|
48
|
+
)
|
|
49
|
+
_set_context(self.ctx)
|
|
50
|
+
|
|
51
|
+
async def asyncTearDown(self) -> None:
|
|
52
|
+
_set_context(None)
|
|
53
|
+
self.assertFalse([thread for thread in threads() if thread.name == "activity-heartbeat"])
|
|
54
|
+
|
|
55
|
+
async def run_work(self, work: Any) -> Any:
|
|
56
|
+
return await asyncio.wait_for(
|
|
57
|
+
asyncio.to_thread(EXAMPLE.call_with_heartbeats, work, interval=0.01),
|
|
58
|
+
timeout=3,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
async def test_new_thread_needs_the_captured_context(self) -> None:
|
|
62
|
+
errors: list[BaseException] = []
|
|
63
|
+
|
|
64
|
+
def look_up_context() -> None:
|
|
65
|
+
try:
|
|
66
|
+
activity.context()
|
|
67
|
+
except RuntimeError as error:
|
|
68
|
+
errors.append(error)
|
|
69
|
+
|
|
70
|
+
helper = Thread(target=look_up_context)
|
|
71
|
+
helper.start()
|
|
72
|
+
helper.join()
|
|
73
|
+
self.assertEqual(len(errors), 1)
|
|
74
|
+
self.assertIn("outside of an activity", str(errors[0]))
|
|
75
|
+
|
|
76
|
+
async def test_return_keeps_original_attempt_and_joins(self) -> None:
|
|
77
|
+
def work(cancelled: Event) -> dict[str, int]:
|
|
78
|
+
self.assertTrue(self.client.observed.wait(2))
|
|
79
|
+
self.assertFalse(cancelled.is_set())
|
|
80
|
+
return {"count": 7}
|
|
81
|
+
|
|
82
|
+
self.assertEqual(await self.run_work(work), {"count": 7})
|
|
83
|
+
self.assertTrue(self.client.calls)
|
|
84
|
+
for request in self.client.calls:
|
|
85
|
+
self.assertEqual(request["task_id"], "task-original")
|
|
86
|
+
self.assertEqual(request["activity_attempt_id"], "attempt-original")
|
|
87
|
+
self.assertEqual(request["lease_owner"], "worker-original")
|
|
88
|
+
|
|
89
|
+
async def test_work_error_also_joins(self) -> None:
|
|
90
|
+
original = ValueError("business call failed")
|
|
91
|
+
|
|
92
|
+
def work(cancelled: Event) -> None:
|
|
93
|
+
self.assertTrue(self.client.observed.wait(2))
|
|
94
|
+
raise original
|
|
95
|
+
|
|
96
|
+
with self.assertRaises(ValueError) as caught:
|
|
97
|
+
await self.run_work(work)
|
|
98
|
+
self.assertIs(caught.exception, original)
|
|
99
|
+
|
|
100
|
+
def cancelled_work(self, cancelled: Event) -> str:
|
|
101
|
+
self.assertTrue(cancelled.wait(2))
|
|
102
|
+
return "must not be returned"
|
|
103
|
+
|
|
104
|
+
async def test_cancellation_stops_work_and_preserves_type(self) -> None:
|
|
105
|
+
self.client.response = {"cancel_requested": True}
|
|
106
|
+
with self.assertRaises(ActivityCancelled):
|
|
107
|
+
await self.run_work(self.cancelled_work)
|
|
108
|
+
|
|
109
|
+
async def test_authority_loss_keeps_original_identity_and_reason(self) -> None:
|
|
110
|
+
self.client.response = {"can_continue": False, "reason": "activity_attempt_replaced"}
|
|
111
|
+
with self.assertRaises(ActivityLeaseAuthorityLost) as caught:
|
|
112
|
+
await self.run_work(self.cancelled_work)
|
|
113
|
+
self.assertEqual(caught.exception.task_id, "task-original")
|
|
114
|
+
self.assertEqual(caught.exception.reason, "activity_attempt_replaced")
|
|
115
|
+
|
|
116
|
+
async def test_transport_error_is_not_silently_lost(self) -> None:
|
|
117
|
+
original = OSError("transport unavailable")
|
|
118
|
+
self.client.response = original
|
|
119
|
+
with self.assertRaises(OSError) as caught:
|
|
120
|
+
await self.run_work(self.cancelled_work)
|
|
121
|
+
self.assertIs(caught.exception, original)
|
|
122
|
+
|
|
123
|
+
async def test_event_loop_call_refuses_before_work(self) -> None:
|
|
124
|
+
with self.assertRaisesRegex(RuntimeError, "synchronous activity thread"):
|
|
125
|
+
EXAMPLE.call_with_heartbeats(lambda cancelled: self.fail("must not run"))
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{durable_workflow-2.9.0 → durable_workflow-2.9.1}/src/durable_workflow/_cancellation_scope.py
RENAMED
|
File without changes
|
|
File without changes
|
{durable_workflow-2.9.0 → durable_workflow-2.9.1}/src/durable_workflow/_cooperative_cancellation.py
RENAMED
|
File without changes
|
{durable_workflow-2.9.0 → durable_workflow-2.9.1}/src/durable_workflow/_prepared_local_activity.py
RENAMED
|
File without changes
|
{durable_workflow-2.9.0 → durable_workflow-2.9.1}/src/durable_workflow/_query_task_authority.py
RENAMED
|
File without changes
|
{durable_workflow-2.9.0 → durable_workflow-2.9.1}/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.9.0 → durable_workflow-2.9.1}/src/durable_workflow/external_task_input.py
RENAMED
|
File without changes
|
{durable_workflow-2.9.0 → durable_workflow-2.9.1}/src/durable_workflow/external_task_result.py
RENAMED
|
File without changes
|
{durable_workflow-2.9.0 → durable_workflow-2.9.1}/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.9.0 → durable_workflow-2.9.1}/src/durable_workflow/python_conformance.py
RENAMED
|
File without changes
|
{durable_workflow-2.9.0 → durable_workflow-2.9.1}/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
|
|
File without changes
|
{durable_workflow-2.9.0 → durable_workflow-2.9.1}/src/durable_workflow.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{durable_workflow-2.9.0 → durable_workflow-2.9.1}/src/durable_workflow.egg-info/entry_points.txt
RENAMED
|
File without changes
|
{durable_workflow-2.9.0 → durable_workflow-2.9.1}/src/durable_workflow.egg-info/top_level.txt
RENAMED
|
File without changes
|
{durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_activity_cancellation_policies.py
RENAMED
|
File without changes
|
|
File without changes
|
{durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_activity_heartbeat_authority.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.9.0 → durable_workflow-2.9.1}/tests/test_cancellation_scope_admission.py
RENAMED
|
File without changes
|
{durable_workflow-2.9.0 → durable_workflow-2.9.1}/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
|
|
File without changes
|
{durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_committed_cancellation_scope_groups.py
RENAMED
|
File without changes
|
{durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_committed_cancellation_scope_history.py
RENAMED
|
File without changes
|
{durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_committed_cancellation_scope_replay.py
RENAMED
|
File without changes
|
|
File without changes
|
{durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_control_plane_parity_fixtures.py
RENAMED
|
File without changes
|
|
File without changes
|
{durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_cooperative_cancellation_client.py
RENAMED
|
File without changes
|
{durable_workflow-2.9.0 → durable_workflow-2.9.1}/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
|
|
File without changes
|
{durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_parallel_completion_order_replay.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_prepared_local_activity_client.py
RENAMED
|
File without changes
|
{durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_prepared_local_activity_groups.py
RENAMED
|
File without changes
|
{durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_prepared_local_activity_policies.py
RENAMED
|
File without changes
|
{durable_workflow-2.9.0 → durable_workflow-2.9.1}/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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_run_cancellation_scope_replay.py
RENAMED
|
File without changes
|
{durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_runtime_external_payload_transport.py
RENAMED
|
File without changes
|
|
File without changes
|
{durable_workflow-2.9.0 → durable_workflow-2.9.1}/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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{durable_workflow-2.9.0 → durable_workflow-2.9.1}/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
|
|
File without changes
|
{durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_workflow_entrypoint_validation.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{durable_workflow-2.9.0 → durable_workflow-2.9.1}/tests/test_workflow_updates_conformance.py
RENAMED
|
File without changes
|