durable-workflow 0.4.96__tar.gz → 0.4.97__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.
Files changed (70) hide show
  1. {durable_workflow-0.4.96/src/durable_workflow.egg-info → durable_workflow-0.4.97}/PKG-INFO +1 -1
  2. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/pyproject.toml +1 -1
  3. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/src/durable_workflow/workflow.py +9 -2
  4. {durable_workflow-0.4.96 → durable_workflow-0.4.97/src/durable_workflow.egg-info}/PKG-INFO +1 -1
  5. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/tests/test_replay.py +53 -0
  6. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/LICENSE +0 -0
  7. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/README.md +0 -0
  8. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/setup.cfg +0 -0
  9. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/src/durable_workflow/__init__.py +0 -0
  10. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/src/durable_workflow/_avro.py +0 -0
  11. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/src/durable_workflow/activity.py +0 -0
  12. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/src/durable_workflow/auth_composition.py +0 -0
  13. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/src/durable_workflow/client.py +0 -0
  14. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/src/durable_workflow/errors.py +0 -0
  15. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/src/durable_workflow/external_storage.py +0 -0
  16. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/src/durable_workflow/external_task_input.py +0 -0
  17. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/src/durable_workflow/external_task_result.py +0 -0
  18. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/src/durable_workflow/history_bundle_verify.py +0 -0
  19. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/src/durable_workflow/interceptors.py +0 -0
  20. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/src/durable_workflow/invocable.py +0 -0
  21. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/src/durable_workflow/metrics.py +0 -0
  22. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/src/durable_workflow/nexus.py +0 -0
  23. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/src/durable_workflow/py.typed +0 -0
  24. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/src/durable_workflow/python_conformance.py +0 -0
  25. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/src/durable_workflow/replay_conformance.py +0 -0
  26. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/src/durable_workflow/replay_verify.py +0 -0
  27. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/src/durable_workflow/retry_policy.py +0 -0
  28. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/src/durable_workflow/serializer.py +0 -0
  29. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/src/durable_workflow/sync.py +0 -0
  30. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/src/durable_workflow/testing.py +0 -0
  31. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/src/durable_workflow/worker.py +0 -0
  32. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/src/durable_workflow/workflow_updates_conformance.py +0 -0
  33. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/src/durable_workflow.egg-info/SOURCES.txt +0 -0
  34. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/src/durable_workflow.egg-info/dependency_links.txt +0 -0
  35. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/src/durable_workflow.egg-info/entry_points.txt +0 -0
  36. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/src/durable_workflow.egg-info/requires.txt +0 -0
  37. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/src/durable_workflow.egg-info/top_level.txt +0 -0
  38. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/tests/test_activity_context.py +0 -0
  39. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/tests/test_auth_composition.py +0 -0
  40. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/tests/test_client.py +0 -0
  41. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/tests/test_control_plane_parity_fixtures.py +0 -0
  42. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/tests/test_errors.py +0 -0
  43. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/tests/test_external_storage.py +0 -0
  44. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/tests/test_external_task_input.py +0 -0
  45. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/tests/test_external_task_result.py +0 -0
  46. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/tests/test_golden_history_replay.py +0 -0
  47. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/tests/test_history_bundle_verify.py +0 -0
  48. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/tests/test_history_event_contract.py +0 -0
  49. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/tests/test_invocable.py +0 -0
  50. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/tests/test_metrics.py +0 -0
  51. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/tests/test_order_processing_example.py +0 -0
  52. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/tests/test_public_boundary_scanner.py +0 -0
  53. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/tests/test_python_conformance.py +0 -0
  54. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/tests/test_queries.py +0 -0
  55. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/tests/test_readme_quickstart.py +0 -0
  56. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/tests/test_release_docs_audit_workflow.py +0 -0
  57. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/tests/test_replay_conformance.py +0 -0
  58. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/tests/test_replay_verify.py +0 -0
  59. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/tests/test_retry_policy.py +0 -0
  60. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/tests/test_schedules.py +0 -0
  61. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/tests/test_serializer.py +0 -0
  62. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/tests/test_signals.py +0 -0
  63. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/tests/test_sleep.py +0 -0
  64. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/tests/test_standalone_activity_client.py +0 -0
  65. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/tests/test_sync.py +0 -0
  66. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/tests/test_testing_harness.py +0 -0
  67. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/tests/test_updates.py +0 -0
  68. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/tests/test_wait_condition.py +0 -0
  69. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/tests/test_worker.py +0 -0
  70. {durable_workflow-0.4.96 → durable_workflow-0.4.97}/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: 0.4.96
3
+ Version: 0.4.97
4
4
  Summary: Python SDK for the Durable Workflow server (language-neutral HTTP protocol)
5
5
  Author: Durable Workflow Contributors
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "durable-workflow"
7
- version = "0.4.96"
7
+ version = "0.4.97"
8
8
  description = "Python SDK for the Durable Workflow server (language-neutral HTTP protocol)"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -2270,10 +2270,12 @@ def _child_workflow_failed_from_payload(
2270
2270
  message = _optional_str(exception.get("message"))
2271
2271
 
2272
2272
  exception_class = _optional_str(payload.get("exception_class"))
2273
+ if exception_class is None and exception is not None:
2274
+ exception_class = _optional_str(exception.get("class"))
2273
2275
  if exception_class is None:
2274
2276
  exception_class = _optional_str(payload.get("exception_type"))
2275
2277
  if exception_class is None and exception is not None:
2276
- exception_class = _optional_str(exception.get("class")) or _optional_str(exception.get("type"))
2278
+ exception_class = _optional_str(exception.get("type"))
2277
2279
 
2278
2280
  child_workflow_run_id = _optional_str(payload.get("child_workflow_run_id"))
2279
2281
  child_workflow_type = _optional_str(payload.get("child_workflow_type"))
@@ -2823,7 +2825,12 @@ def _replay_state(
2823
2825
  result_cursor = 0
2824
2826
  pending_step_cursor = 0
2825
2827
  wait_yield_count = 0
2826
- gen = instance.run(ctx, *start_input)
2828
+ try:
2829
+ gen = instance.run(ctx, *start_input)
2830
+ except NonDeterministicReplayError:
2831
+ raise
2832
+ except Exception as exc:
2833
+ return _state([_fail_workflow_from_exception(exc)])
2827
2834
  if not hasattr(gen, "__next__"):
2828
2835
  if isinstance(gen, ContinueAsNew):
2829
2836
  _assert_no_unconsumed_history("continue as new")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: durable-workflow
3
- Version: 0.4.96
3
+ Version: 0.4.97
4
4
  Summary: Python SDK for the Durable Workflow server (language-neutral HTTP protocol)
5
5
  Author: Durable Workflow Contributors
6
6
  License-Expression: MIT
@@ -155,6 +155,18 @@ class FailingWorkflow:
155
155
  raise ValueError("something went wrong")
156
156
 
157
157
 
158
+ @workflow.defn(name="construction-failing-workflow")
159
+ class ConstructionFailingWorkflow:
160
+ def run(self, ctx: WorkflowContext): # type: ignore[no-untyped-def]
161
+ raise RuntimeError("failed before first yield")
162
+
163
+
164
+ @workflow.defn(name="construction-stop-iteration-workflow")
165
+ class ConstructionStopIterationWorkflow:
166
+ def run(self, ctx: WorkflowContext): # type: ignore[no-untyped-def]
167
+ raise StopIteration("stopped before first yield")
168
+
169
+
158
170
  @workflow.defn(name="typed-compensation-failure-workflow")
159
171
  class TypedCompensationFailureWorkflow:
160
172
  def run(self, ctx: WorkflowContext): # type: ignore[no-untyped-def]
@@ -848,6 +860,24 @@ class TestFailingWorkflow:
848
860
  assert cmd.exception_type == "ValueError"
849
861
  assert cmd.exception_class == "builtins.ValueError"
850
862
 
863
+ def test_non_generator_exception_produces_fail_command(self) -> None:
864
+ outcome = replay(ConstructionFailingWorkflow, [], [])
865
+ assert len(outcome.commands) == 1
866
+ cmd = outcome.commands[0]
867
+ assert isinstance(cmd, FailWorkflow)
868
+ assert "failed before first yield" in cmd.message
869
+ assert cmd.exception_type == "RuntimeError"
870
+ assert cmd.exception_class == "builtins.RuntimeError"
871
+
872
+ def test_non_generator_stop_iteration_is_not_completion(self) -> None:
873
+ outcome = replay(ConstructionStopIterationWorkflow, [], [])
874
+ assert len(outcome.commands) == 1
875
+ cmd = outcome.commands[0]
876
+ assert isinstance(cmd, FailWorkflow)
877
+ assert "stopped before first yield" in cmd.message
878
+ assert cmd.exception_type == "StopIteration"
879
+ assert cmd.exception_class == "builtins.StopIteration"
880
+
851
881
  def test_fail_server_command_shape(self) -> None:
852
882
  history = [{"event_type": "ActivityCompleted", "payload": {"result": '"ok"'}}]
853
883
  outcome = replay(FailingWorkflow, history, [])
@@ -1363,6 +1393,29 @@ class TestChildWorkflow:
1363
1393
  "child_workflow_type": "sub-workflow",
1364
1394
  }
1365
1395
 
1396
+ def test_child_failed_prefers_nested_exception_class_over_type(self) -> None:
1397
+ history = [
1398
+ {
1399
+ "event_type": "ChildRunFailed",
1400
+ "payload": {
1401
+ "message": "child failed",
1402
+ "exception_type": "NestedType",
1403
+ "exception": {
1404
+ "type": "NestedType",
1405
+ "class": "pkg.NestedClass",
1406
+ "message": "child failed",
1407
+ },
1408
+ },
1409
+ },
1410
+ ]
1411
+
1412
+ outcome = replay(ChildWorkflowFailureDetailsWf, history, [])
1413
+
1414
+ assert len(outcome.commands) == 1
1415
+ cmd = outcome.commands[0]
1416
+ assert isinstance(cmd, CompleteWorkflow)
1417
+ assert cmd.result["exception_class"] == "pkg.NestedClass"
1418
+
1366
1419
  def test_child_cancelled_is_observed_by_parent_as_typed_cancellation(self) -> None:
1367
1420
  history = [
1368
1421
  {