durable-workflow 0.4.96__tar.gz → 0.4.98__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-0.4.96/src/durable_workflow.egg-info → durable_workflow-0.4.98}/PKG-INFO +1 -1
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/pyproject.toml +1 -1
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/src/durable_workflow/workflow.py +10 -3
- {durable_workflow-0.4.96 → durable_workflow-0.4.98/src/durable_workflow.egg-info}/PKG-INFO +1 -1
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/tests/test_replay.py +81 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/LICENSE +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/README.md +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/setup.cfg +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/src/durable_workflow/__init__.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/src/durable_workflow/_avro.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/src/durable_workflow/activity.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/src/durable_workflow/auth_composition.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/src/durable_workflow/client.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/src/durable_workflow/errors.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/src/durable_workflow/external_storage.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/src/durable_workflow/external_task_input.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/src/durable_workflow/external_task_result.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/src/durable_workflow/history_bundle_verify.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/src/durable_workflow/interceptors.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/src/durable_workflow/invocable.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/src/durable_workflow/metrics.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/src/durable_workflow/nexus.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/src/durable_workflow/py.typed +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/src/durable_workflow/python_conformance.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/src/durable_workflow/replay_conformance.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/src/durable_workflow/replay_verify.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/src/durable_workflow/retry_policy.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/src/durable_workflow/serializer.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/src/durable_workflow/sync.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/src/durable_workflow/testing.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/src/durable_workflow/worker.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/src/durable_workflow/workflow_updates_conformance.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/src/durable_workflow.egg-info/SOURCES.txt +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/src/durable_workflow.egg-info/dependency_links.txt +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/src/durable_workflow.egg-info/entry_points.txt +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/src/durable_workflow.egg-info/requires.txt +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/src/durable_workflow.egg-info/top_level.txt +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/tests/test_activity_context.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/tests/test_auth_composition.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/tests/test_client.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/tests/test_control_plane_parity_fixtures.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/tests/test_errors.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/tests/test_external_storage.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/tests/test_external_task_input.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/tests/test_external_task_result.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/tests/test_golden_history_replay.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/tests/test_history_bundle_verify.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/tests/test_history_event_contract.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/tests/test_invocable.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/tests/test_metrics.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/tests/test_order_processing_example.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/tests/test_public_boundary_scanner.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/tests/test_python_conformance.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/tests/test_queries.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/tests/test_readme_quickstart.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/tests/test_release_docs_audit_workflow.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/tests/test_replay_conformance.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/tests/test_replay_verify.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/tests/test_retry_policy.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/tests/test_schedules.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/tests/test_serializer.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/tests/test_signals.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/tests/test_sleep.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/tests/test_standalone_activity_client.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/tests/test_sync.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/tests/test_testing_harness.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/tests/test_updates.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/tests/test_wait_condition.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/tests/test_worker.py +0 -0
- {durable_workflow-0.4.96 → durable_workflow-0.4.98}/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 = "0.4.
|
|
7
|
+
version = "0.4.98"
|
|
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("
|
|
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"))
|
|
@@ -2297,7 +2299,7 @@ def _child_workflow_failed_from_payload(
|
|
|
2297
2299
|
return ChildWorkflowFailed(
|
|
2298
2300
|
message or "child workflow failed",
|
|
2299
2301
|
exception_class,
|
|
2300
|
-
failure_kind=
|
|
2302
|
+
failure_kind="child_workflow",
|
|
2301
2303
|
child_workflow_run_id=child_workflow_run_id,
|
|
2302
2304
|
child_workflow_type=child_workflow_type,
|
|
2303
2305
|
)
|
|
@@ -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
|
-
|
|
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")
|
|
@@ -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,57 @@ class TestChildWorkflow:
|
|
|
1363
1393
|
"child_workflow_type": "sub-workflow",
|
|
1364
1394
|
}
|
|
1365
1395
|
|
|
1396
|
+
def test_child_failed_reports_parent_failure_kind_for_application_child(self) -> None:
|
|
1397
|
+
history = [
|
|
1398
|
+
{
|
|
1399
|
+
"event_type": "ChildRunFailed",
|
|
1400
|
+
"payload": {
|
|
1401
|
+
"message": "planned typed child failure: typed-marker",
|
|
1402
|
+
"exception_type": "PlannedChildFailure",
|
|
1403
|
+
"exception_class": "smoke.PlannedChildFailure",
|
|
1404
|
+
"failure_category": "application",
|
|
1405
|
+
"child_workflow_run_id": "child-run-1",
|
|
1406
|
+
"child_workflow_type": "sub-workflow",
|
|
1407
|
+
},
|
|
1408
|
+
},
|
|
1409
|
+
]
|
|
1410
|
+
|
|
1411
|
+
outcome = replay(ChildWorkflowFailureDetailsWf, history, [])
|
|
1412
|
+
|
|
1413
|
+
assert len(outcome.commands) == 1
|
|
1414
|
+
cmd = outcome.commands[0]
|
|
1415
|
+
assert isinstance(cmd, CompleteWorkflow)
|
|
1416
|
+
assert cmd.result == {
|
|
1417
|
+
"message": "planned typed child failure: typed-marker",
|
|
1418
|
+
"exception_class": "smoke.PlannedChildFailure",
|
|
1419
|
+
"failure_kind": "child_workflow",
|
|
1420
|
+
"child_workflow_run_id": "child-run-1",
|
|
1421
|
+
"child_workflow_type": "sub-workflow",
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
def test_child_failed_prefers_nested_exception_class_over_type(self) -> None:
|
|
1425
|
+
history = [
|
|
1426
|
+
{
|
|
1427
|
+
"event_type": "ChildRunFailed",
|
|
1428
|
+
"payload": {
|
|
1429
|
+
"message": "child failed",
|
|
1430
|
+
"exception_type": "NestedType",
|
|
1431
|
+
"exception": {
|
|
1432
|
+
"type": "NestedType",
|
|
1433
|
+
"class": "pkg.NestedClass",
|
|
1434
|
+
"message": "child failed",
|
|
1435
|
+
},
|
|
1436
|
+
},
|
|
1437
|
+
},
|
|
1438
|
+
]
|
|
1439
|
+
|
|
1440
|
+
outcome = replay(ChildWorkflowFailureDetailsWf, history, [])
|
|
1441
|
+
|
|
1442
|
+
assert len(outcome.commands) == 1
|
|
1443
|
+
cmd = outcome.commands[0]
|
|
1444
|
+
assert isinstance(cmd, CompleteWorkflow)
|
|
1445
|
+
assert cmd.result["exception_class"] == "pkg.NestedClass"
|
|
1446
|
+
|
|
1366
1447
|
def test_child_cancelled_is_observed_by_parent_as_typed_cancellation(self) -> None:
|
|
1367
1448
|
history = [
|
|
1368
1449
|
{
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{durable_workflow-0.4.96 → durable_workflow-0.4.98}/src/durable_workflow/auth_composition.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{durable_workflow-0.4.96 → durable_workflow-0.4.98}/src/durable_workflow/external_storage.py
RENAMED
|
File without changes
|
{durable_workflow-0.4.96 → durable_workflow-0.4.98}/src/durable_workflow/external_task_input.py
RENAMED
|
File without changes
|
{durable_workflow-0.4.96 → durable_workflow-0.4.98}/src/durable_workflow/external_task_result.py
RENAMED
|
File without changes
|
{durable_workflow-0.4.96 → durable_workflow-0.4.98}/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-0.4.96 → durable_workflow-0.4.98}/src/durable_workflow/python_conformance.py
RENAMED
|
File without changes
|
{durable_workflow-0.4.96 → durable_workflow-0.4.98}/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
|
{durable_workflow-0.4.96 → durable_workflow-0.4.98}/src/durable_workflow.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{durable_workflow-0.4.96 → durable_workflow-0.4.98}/src/durable_workflow.egg-info/entry_points.txt
RENAMED
|
File without changes
|
{durable_workflow-0.4.96 → durable_workflow-0.4.98}/src/durable_workflow.egg-info/requires.txt
RENAMED
|
File without changes
|
{durable_workflow-0.4.96 → durable_workflow-0.4.98}/src/durable_workflow.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{durable_workflow-0.4.96 → durable_workflow-0.4.98}/tests/test_control_plane_parity_fixtures.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{durable_workflow-0.4.96 → durable_workflow-0.4.98}/tests/test_release_docs_audit_workflow.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-0.4.96 → durable_workflow-0.4.98}/tests/test_standalone_activity_client.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{durable_workflow-0.4.96 → durable_workflow-0.4.98}/tests/test_workflow_updates_conformance.py
RENAMED
|
File without changes
|