durable-workflow 2.12.1__tar.gz → 2.12.3__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.12.1/src/durable_workflow.egg-info → durable_workflow-2.12.3}/PKG-INFO +13 -1
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/README.md +9 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/pyproject.toml +6 -3
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/src/durable_workflow/_activity_process.py +2 -1
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/src/durable_workflow/_failure_reporting.py +6 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/src/durable_workflow/invocable.py +4 -3
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/src/durable_workflow/worker.py +11 -10
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/src/durable_workflow/workflow.py +214 -43
- {durable_workflow-2.12.1 → durable_workflow-2.12.3/src/durable_workflow.egg-info}/PKG-INFO +13 -1
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/src/durable_workflow.egg-info/SOURCES.txt +2 -0
- durable_workflow-2.12.3/tests/test_empty_callback_failure.py +156 -0
- durable_workflow-2.12.3/tests/test_generator_lifecycle.py +401 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_replay_regression_corpus.py +31 -7
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/LICENSE +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/setup.cfg +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/src/durable_workflow/__init__.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/src/durable_workflow/_activity_lease.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/src/durable_workflow/_avro.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/src/durable_workflow/_cancellation_scope.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/src/durable_workflow/_cancellation_scope_history.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/src/durable_workflow/_cooperative_cancellation.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/src/durable_workflow/_metric_routes.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/src/durable_workflow/_poll_transport.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/src/durable_workflow/_prepared_local_activity.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/src/durable_workflow/_query_task_authority.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/src/durable_workflow/_sticky_workflow_cache.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/src/durable_workflow/activity.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/src/durable_workflow/auth_composition.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/src/durable_workflow/cancellation.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/src/durable_workflow/client.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/src/durable_workflow/errors.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/src/durable_workflow/external_storage.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/src/durable_workflow/external_task_input.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/src/durable_workflow/external_task_result.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/src/durable_workflow/history_bundle_verify.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/src/durable_workflow/interceptors.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/src/durable_workflow/metrics.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/src/durable_workflow/nexus.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/src/durable_workflow/py.typed +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/src/durable_workflow/python_conformance.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/src/durable_workflow/replay_conformance.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/src/durable_workflow/replay_verify.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/src/durable_workflow/retry_policy.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/src/durable_workflow/serializer.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/src/durable_workflow/sync.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/src/durable_workflow/telemetry.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/src/durable_workflow/telemetry_carrier.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/src/durable_workflow/testing.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/src/durable_workflow/worker_session.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/src/durable_workflow/workflow_updates_conformance.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/src/durable_workflow.egg-info/dependency_links.txt +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/src/durable_workflow.egg-info/entry_points.txt +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/src/durable_workflow.egg-info/requires.txt +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/src/durable_workflow.egg-info/top_level.txt +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_activity_cancellation_policies.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_activity_context.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_activity_heartbeat_authority.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_activity_lease_authority.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_activity_lease_renewal.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_activity_lease_worker.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_activity_process.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_activity_retry_info.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_auth_composition.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_avro_value_benchmark.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_avro_value_protocol.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_cancellation_context.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_cancellation_remaining_time.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_cancellation_scope_admission.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_cancellation_scope_authoring.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_cancellation_scope_boundary.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_cancellation_scope_opening.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_child_workflow_policies.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_ci_checkout.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_ci_integration_endpoint.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_client.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_client_telemetry.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_client_timeouts.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_codec_regression_corpus.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_committed_cancellation_scope_descendants.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_committed_cancellation_scope_groups.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_committed_cancellation_scope_history.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_committed_cancellation_scope_replay.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_condition_replay_drift.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_control_plane_parity_fixtures.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_cooperative_cancellation.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_cooperative_cancellation_client.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_cooperative_cancellation_worker.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_cooperative_remote_worker.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_docstring_cross_references.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_durable_selection.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_errors.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_external_storage.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_external_task_input.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_external_task_result.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_failure_reporting.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_golden_history_replay.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_history_bundle_schema2.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_history_bundle_verify.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_history_event_contract.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_invocable.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_local_failure_reporting.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_message_streams.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_metric_routes.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_metrics.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_nexus_durable_wait.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_nexus_namespace.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_nexus_wire.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_order_processing_example.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_parallel_completion_order_replay.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_parallel_saga_authoring.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_patch_history.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_pending_operation_replay.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_poll_failure_backoff.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_poll_transport.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_prepared_local_activity_client.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_prepared_local_activity_groups.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_prepared_local_activity_policies.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_prepared_local_activity_worker.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_public_boundary_scanner.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_pypi_project_surface.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_python_conformance.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_queries.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_query_task_authority.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_query_timeout_retry.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_readme_quickstart.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_redrive_replay.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_registration_ambiguity.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_release_metadata.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_replay.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_replay_clock.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_replay_conformance.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_replay_verify.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_retry_policy.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_run_cancellation_scope_replay.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_runtime_external_payload_transport.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_schedules.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_scoped_run_cancellation_context.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_sdk_metrics_contract.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_serializer.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_signals.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_sleep.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_standalone_activity_client.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_sticky_execution.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_storage_admission.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_sync.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_sync_activities.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_telemetry.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_telemetry_carrier.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_testing_failures.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_testing_harness.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_testing_nexus.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_threaded_heartbeat_example.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_tls_trust.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_transport_diagnostics.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_update_signal_condition_replay.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_updates.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_wait_condition.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_worker.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_worker_deregistration.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_worker_observation.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_worker_session.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_worker_telemetry_wire.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_workflow_entry_arguments.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_workflow_entrypoint_validation.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_workflow_handler_validation.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_workflow_identity.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_workflow_result_timeout.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/tests/test_workflow_streams.py +0 -0
- {durable_workflow-2.12.1 → durable_workflow-2.12.3}/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.12.
|
|
3
|
+
Version: 2.12.3
|
|
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
|
|
@@ -14,6 +14,9 @@ Classifier: Programming Language :: Python :: 3
|
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.10
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.11
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
17
20
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
18
21
|
Classifier: Typing :: Typed
|
|
19
22
|
Requires-Python: >=3.10
|
|
@@ -186,6 +189,15 @@ independently of application heartbeats. Existing terminal cancellation remains
|
|
|
186
189
|
available. See the [cancellation guide](docs/cooperative-cancellation-design.md)
|
|
187
190
|
for immutable context, operation policies, shielded cleanup and recovery.
|
|
188
191
|
|
|
192
|
+
A suspended replay is not a workflow exit. Python may run `finally` blocks and
|
|
193
|
+
context-manager exits when disposing of that replay. The SDK discards commands
|
|
194
|
+
and `ctx.logger` messages produced only by disposal; queries observe the state
|
|
195
|
+
at the pending command. Actual completion, failure and cooperative cancellation
|
|
196
|
+
run cleanup through the normal history, and cold replay uses its recorded
|
|
197
|
+
results. Put external cleanup in an Activity rather than direct I/O in a
|
|
198
|
+
finalizer, and use `ctx.cancellation_shield()` when that Activity must finish
|
|
199
|
+
after cancellation. The disposal guarantees require SDK 2.12.2 or later.
|
|
200
|
+
|
|
189
201
|
Both `await activity.context().heartbeat()` and synchronous
|
|
190
202
|
`activity.context().heartbeat_sync()` stop at an explicit Server refusal.
|
|
191
203
|
They raise `ActivityLeaseAuthorityLost` with its original `.reason` for an
|
|
@@ -142,6 +142,15 @@ independently of application heartbeats. Existing terminal cancellation remains
|
|
|
142
142
|
available. See the [cancellation guide](docs/cooperative-cancellation-design.md)
|
|
143
143
|
for immutable context, operation policies, shielded cleanup and recovery.
|
|
144
144
|
|
|
145
|
+
A suspended replay is not a workflow exit. Python may run `finally` blocks and
|
|
146
|
+
context-manager exits when disposing of that replay. The SDK discards commands
|
|
147
|
+
and `ctx.logger` messages produced only by disposal; queries observe the state
|
|
148
|
+
at the pending command. Actual completion, failure and cooperative cancellation
|
|
149
|
+
run cleanup through the normal history, and cold replay uses its recorded
|
|
150
|
+
results. Put external cleanup in an Activity rather than direct I/O in a
|
|
151
|
+
finalizer, and use `ctx.cancellation_shield()` when that Activity must finish
|
|
152
|
+
after cancellation. The disposal guarantees require SDK 2.12.2 or later.
|
|
153
|
+
|
|
145
154
|
Both `await activity.context().heartbeat()` and synchronous
|
|
146
155
|
`activity.context().heartbeat_sync()` stop at an explicit Server refusal.
|
|
147
156
|
They raise `ActivityLeaseAuthorityLost` with its original `.reason` for an
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "durable-workflow"
|
|
7
|
-
version = "2.12.
|
|
7
|
+
version = "2.12.3"
|
|
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"
|
|
@@ -29,6 +29,9 @@ classifiers = [
|
|
|
29
29
|
"Programming Language :: Python :: 3.10",
|
|
30
30
|
"Programming Language :: Python :: 3.11",
|
|
31
31
|
"Programming Language :: Python :: 3.12",
|
|
32
|
+
"Programming Language :: Python :: 3.13",
|
|
33
|
+
"Programming Language :: Python :: 3.14",
|
|
34
|
+
"Programming Language :: Python :: 3.15",
|
|
32
35
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
33
36
|
"Typing :: Typed",
|
|
34
37
|
]
|
|
@@ -76,8 +79,8 @@ durable-workflow-replay-conformance = "durable_workflow.replay_conformance:main"
|
|
|
76
79
|
durable-workflow-workflow-updates-conformance = "durable_workflow.workflow_updates_conformance:main"
|
|
77
80
|
|
|
78
81
|
[tool.durable-workflow]
|
|
79
|
-
product-train = "2.12.
|
|
80
|
-
registry-version = "2.12.
|
|
82
|
+
product-train = "2.12.3"
|
|
83
|
+
registry-version = "2.12.3"
|
|
81
84
|
supported-server-versions = "2.8.0"
|
|
82
85
|
worker-protocol-version = "1.20"
|
|
83
86
|
control-plane-version = "2"
|
{durable_workflow-2.12.1 → durable_workflow-2.12.3}/src/durable_workflow/_activity_process.py
RENAMED
|
@@ -24,6 +24,7 @@ from multiprocessing.process import BaseProcess
|
|
|
24
24
|
from pathlib import Path
|
|
25
25
|
from typing import Any, cast
|
|
26
26
|
|
|
27
|
+
from ._failure_reporting import exception_message
|
|
27
28
|
from .activity import ActivityContext, ActivityInfo, _set_context
|
|
28
29
|
from .errors import ActivityCancelled, NonRetryableError
|
|
29
30
|
from .interceptors import ActivityInterceptorContext, WorkerInterceptor
|
|
@@ -139,7 +140,7 @@ def _callback(directory: str, channel: socket.socket) -> None:
|
|
|
139
140
|
except BaseException as error:
|
|
140
141
|
code = getattr(error, "code", None)
|
|
141
142
|
_write_payload(directory, "outcome", CallbackOutcome(failure=CallbackFailure(
|
|
142
|
-
message=
|
|
143
|
+
message=exception_message(error), failure_type=type(error).__name__,
|
|
143
144
|
failure_class=f"{type(error).__module__}.{type(error).__qualname__}",
|
|
144
145
|
failure_code=code if isinstance(code, int) and not isinstance(code, bool) else None,
|
|
145
146
|
stack_trace=traceback.format_exc(), non_retryable=isinstance(error, NonRetryableError),
|
{durable_workflow-2.12.1 → durable_workflow-2.12.3}/src/durable_workflow/_failure_reporting.py
RENAMED
|
@@ -26,6 +26,12 @@ _EXCEPTION_IDENTITY = frozenset({
|
|
|
26
26
|
})
|
|
27
27
|
|
|
28
28
|
|
|
29
|
+
def exception_message(error: BaseException, *, fallback: str | None = None) -> str:
|
|
30
|
+
"""Keep useful diagnostic text; blank exceptions still need a valid report."""
|
|
31
|
+
message = str(error)
|
|
32
|
+
return message if message.strip() else fallback or type(error).__name__
|
|
33
|
+
|
|
34
|
+
|
|
29
35
|
def encoded_json(value: Any) -> bytes:
|
|
30
36
|
"""Use HTTPX's actual JSON settings, including escaping and NaN refusal."""
|
|
31
37
|
return httpx.Request("POST", "https://failure.invalid", json=value).content
|
|
@@ -12,6 +12,7 @@ from datetime import datetime, timezone
|
|
|
12
12
|
from typing import Any, cast
|
|
13
13
|
|
|
14
14
|
from . import serializer
|
|
15
|
+
from ._failure_reporting import exception_message
|
|
15
16
|
from .errors import ActivityCancelled, NonRetryableError
|
|
16
17
|
from .external_storage import ExternalPayloadCache, ExternalStorageDriver
|
|
17
18
|
from .external_task_input import ExternalTaskInput, parse_external_task_input
|
|
@@ -105,7 +106,7 @@ class InvocableActivityHandler:
|
|
|
105
106
|
started,
|
|
106
107
|
kind="application",
|
|
107
108
|
classification="application_error",
|
|
108
|
-
message=
|
|
109
|
+
message=exception_message(exc),
|
|
109
110
|
failure_type=type(exc).__name__,
|
|
110
111
|
stack_trace=traceback.format_exc(),
|
|
111
112
|
retryable=False,
|
|
@@ -116,7 +117,7 @@ class InvocableActivityHandler:
|
|
|
116
117
|
started,
|
|
117
118
|
kind="timeout",
|
|
118
119
|
classification="deadline_exceeded",
|
|
119
|
-
message=
|
|
120
|
+
message=exception_message(exc, fallback="Invocable activity handler exceeded its deadline."),
|
|
120
121
|
failure_type=type(exc).__name__,
|
|
121
122
|
stack_trace=traceback.format_exc(),
|
|
122
123
|
timeout_type="deadline_exceeded",
|
|
@@ -140,7 +141,7 @@ class InvocableActivityHandler:
|
|
|
140
141
|
started,
|
|
141
142
|
kind="application",
|
|
142
143
|
classification="application_error",
|
|
143
|
-
message=
|
|
144
|
+
message=exception_message(exc),
|
|
144
145
|
failure_type=type(exc).__name__,
|
|
145
146
|
stack_trace=traceback.format_exc(),
|
|
146
147
|
retryable=True,
|
|
@@ -44,7 +44,7 @@ from ._activity_lease import activity_lease_renewal_supported, current_activity_
|
|
|
44
44
|
from ._activity_process import CallbackFailure, CallbackInvocation, CallbackProcessLost, SupervisedCallback
|
|
45
45
|
from ._cancellation_scope_history import CancellationScopeBudget, CancellationScopeDeliveryReceipt
|
|
46
46
|
from ._cooperative_cancellation import CancellationRequest, read_cancellation_history
|
|
47
|
-
from ._failure_reporting import REQUEST_BYTES, normalize_local_failure
|
|
47
|
+
from ._failure_reporting import REQUEST_BYTES, exception_message, normalize_local_failure
|
|
48
48
|
from ._prepared_local_activity import PreparedAttempt, PreparedCancellationObserved, PreparedLocalRunner
|
|
49
49
|
from ._query_task_authority import QueryTaskAuthority, current_query_authority
|
|
50
50
|
from ._sticky_workflow_cache import CacheKey, StickyWorkflowCache, complete_history
|
|
@@ -3240,7 +3240,7 @@ class Worker:
|
|
|
3240
3240
|
except NonRetryableError as e:
|
|
3241
3241
|
log.exception("activity failed (non-retryable)")
|
|
3242
3242
|
await self._report_activity_task_failure(
|
|
3243
|
-
task_id, attempt_id, "activity failure", message=
|
|
3243
|
+
task_id, attempt_id, "activity failure", message=exception_message(e), failure_type=type(e).__name__,
|
|
3244
3244
|
failure_class=_exception_class_name(e), failure_code=_exception_code(e),
|
|
3245
3245
|
stack_trace=traceback.format_exc(), non_retryable=True,
|
|
3246
3246
|
)
|
|
@@ -3271,7 +3271,8 @@ class Worker:
|
|
|
3271
3271
|
raise error
|
|
3272
3272
|
log.error("activity failed", exc_info=(type(error), error, error.__traceback__))
|
|
3273
3273
|
await self._report_activity_task_failure(
|
|
3274
|
-
task_id, attempt_id, "activity failure", message=
|
|
3274
|
+
task_id, attempt_id, "activity failure", message=exception_message(error),
|
|
3275
|
+
failure_type=type(error).__name__,
|
|
3275
3276
|
failure_class=_exception_class_name(error), failure_code=_exception_code(error),
|
|
3276
3277
|
stack_trace="".join(traceback.format_exception(type(error), error, error.__traceback__)),
|
|
3277
3278
|
)
|
|
@@ -3573,7 +3574,7 @@ class Worker:
|
|
|
3573
3574
|
)
|
|
3574
3575
|
return "failed"
|
|
3575
3576
|
except QueryFailed as e:
|
|
3576
|
-
message =
|
|
3577
|
+
message = exception_message(e)
|
|
3577
3578
|
if "unknown query" in message:
|
|
3578
3579
|
reason = "rejected_unknown_query"
|
|
3579
3580
|
elif message.startswith("workflow replay failed before query:"):
|
|
@@ -3594,7 +3595,7 @@ class Worker:
|
|
|
3594
3595
|
await self._fail_query_task(
|
|
3595
3596
|
query_task_id,
|
|
3596
3597
|
attempt,
|
|
3597
|
-
|
|
3598
|
+
exception_message(e, fallback="query execution failed"),
|
|
3598
3599
|
reason="query_rejected",
|
|
3599
3600
|
failure_type=type(e).__name__,
|
|
3600
3601
|
stack_trace=traceback.format_exc(),
|
|
@@ -3631,7 +3632,7 @@ class Worker:
|
|
|
3631
3632
|
await self._fail_query_task(
|
|
3632
3633
|
query_task_id,
|
|
3633
3634
|
attempt,
|
|
3634
|
-
|
|
3635
|
+
exception_message(e, fallback="query result completion was rejected by the server"),
|
|
3635
3636
|
reason=server_reason if server_reason else "query_result_completion_failed",
|
|
3636
3637
|
failure_type=type(e).__name__,
|
|
3637
3638
|
stack_trace=traceback.format_exc(),
|
|
@@ -3667,7 +3668,7 @@ class Worker:
|
|
|
3667
3668
|
await self._fail_query_task(
|
|
3668
3669
|
query_task_id,
|
|
3669
3670
|
attempt,
|
|
3670
|
-
|
|
3671
|
+
exception_message(e, fallback="query result completion failed"),
|
|
3671
3672
|
reason="query_result_completion_failed",
|
|
3672
3673
|
failure_type=type(e).__name__,
|
|
3673
3674
|
stack_trace=traceback.format_exc(),
|
|
@@ -4075,7 +4076,7 @@ class Worker:
|
|
|
4075
4076
|
return await self._reject_update_validation_task(
|
|
4076
4077
|
task_id,
|
|
4077
4078
|
attempt,
|
|
4078
|
-
|
|
4079
|
+
exception_message(exc, fallback="update validator rejected the request"),
|
|
4079
4080
|
reason="update_validator_rejected",
|
|
4080
4081
|
failure_type=exc.exception_type or type(exc).__name__,
|
|
4081
4082
|
validation_errors=exc.validation_errors,
|
|
@@ -4085,7 +4086,7 @@ class Worker:
|
|
|
4085
4086
|
return await self._reject_update_validation_task(
|
|
4086
4087
|
task_id,
|
|
4087
4088
|
attempt,
|
|
4088
|
-
|
|
4089
|
+
exception_message(exc),
|
|
4089
4090
|
reason="update_validation_worker_failed",
|
|
4090
4091
|
failure_type=type(exc).__name__,
|
|
4091
4092
|
stack_trace=traceback.format_exc(),
|
|
@@ -4094,7 +4095,7 @@ class Worker:
|
|
|
4094
4095
|
return await self._reject_update_validation_task(
|
|
4095
4096
|
task_id,
|
|
4096
4097
|
attempt,
|
|
4097
|
-
|
|
4098
|
+
exception_message(exc, fallback="update validator rejected the request"),
|
|
4098
4099
|
reason="update_validator_rejected",
|
|
4099
4100
|
failure_type=type(exc).__name__,
|
|
4100
4101
|
stack_trace=traceback.format_exc(),
|