dex-python-sdk 0.0.2__cp311-abi3-win_amd64.whl

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 (121) hide show
  1. dex/__init__.py +146 -0
  2. dex/_grpc_errors.py +73 -0
  3. dex/_invocation_context.py +269 -0
  4. dex/_native.pyd +0 -0
  5. dex/_native.pyi +20 -0
  6. dex/_utils.py +18 -0
  7. dex/_value_hydrator.py +223 -0
  8. dex/_value_mapper.py +174 -0
  9. dex/_worker_dispatcher.py +451 -0
  10. dex/_worker_service.py +60 -0
  11. dex/attribute.py +87 -0
  12. dex/blob_cache.py +73 -0
  13. dex/channel.py +165 -0
  14. dex/client.py +715 -0
  15. dex/client_options.py +19 -0
  16. dex/codec.py +286 -0
  17. dex/command_request.py +120 -0
  18. dex/command_results.py +107 -0
  19. dex/communication.py +136 -0
  20. dex/communication_schema.py +54 -0
  21. dex/condition.py +74 -0
  22. dex/context.py +86 -0
  23. dex/data_attributes.py +70 -0
  24. dex/dexpb/__init__.py +1 -0
  25. dex/dexpb/dex_pb2.py +381 -0
  26. dex/dexpb/dex_pb2.pyi +1734 -0
  27. dex/dexpb/dex_pb2_grpc.py +1298 -0
  28. dex/errors.py +109 -0
  29. dex/flow.py +456 -0
  30. dex/flow_config.py +29 -0
  31. dex/flow_info.py +51 -0
  32. dex/flow_options.py +122 -0
  33. dex/object_encoder.py +799 -0
  34. dex/persistence.py +89 -0
  35. dex/persistence_options.py +12 -0
  36. dex/persistence_schema.py +51 -0
  37. dex/py.typed +1 -0
  38. dex/registry.py +204 -0
  39. dex/reset_workflow_type_and_options.py +67 -0
  40. dex/rpc.py +93 -0
  41. dex/runtime_errors.py +81 -0
  42. dex/search_attributes.py +184 -0
  43. dex/state_decision.py +153 -0
  44. dex/state_execution_locals.py +66 -0
  45. dex/state_movement.py +115 -0
  46. dex/state_schema.py +48 -0
  47. dex/step.py +194 -0
  48. dex/step_execution.py +42 -0
  49. dex/stop_workflow_options.py +18 -0
  50. dex/tests/__init__.py +80 -0
  51. dex/tests/dex-service-env/.env +7 -0
  52. dex/tests/dex-service-env/docker-compose-init.sh +44 -0
  53. dex/tests/dex-service-env/docker-compose.yml +97 -0
  54. dex/tests/dex-service-env/dynamicconfig/README.md +39 -0
  55. dex/tests/dex-service-env/dynamicconfig/development-sql.yaml +9 -0
  56. dex/tests/dex-service-env/dynamicconfig/docker.yaml +2 -0
  57. dex/tests/test_abnormal_exit_workflow.py +43 -0
  58. dex/tests/test_basic_workflow.py +70 -0
  59. dex/tests/test_conditional_complete.py +50 -0
  60. dex/tests/test_describe_workflow.py +40 -0
  61. dex/tests/test_empty_data_decodes_properly.py +74 -0
  62. dex/tests/test_internal_channel.py +28 -0
  63. dex/tests/test_internal_channel_with_no_prefix_channel.py +41 -0
  64. dex/tests/test_persistence_data_attributes.py +62 -0
  65. dex/tests/test_persistence_search_attributes.py +127 -0
  66. dex/tests/test_persistence_state_execution_locals.py +38 -0
  67. dex/tests/test_rpc.py +64 -0
  68. dex/tests/test_rpc_with_memo.py +195 -0
  69. dex/tests/test_rpc_with_memo_duplicate_java_tests.py +117 -0
  70. dex/tests/test_signal.py +51 -0
  71. dex/tests/test_skip_wait_until.py +76 -0
  72. dex/tests/test_state_failure_recovery.py +28 -0
  73. dex/tests/test_timer.py +35 -0
  74. dex/tests/test_wait_for_state_execution_completion.py +53 -0
  75. dex/tests/test_workflow_errors.py +87 -0
  76. dex/tests/test_workflow_state_options.py +118 -0
  77. dex/tests/test_workflow_state_options_override.py +44 -0
  78. dex/tests/worker_server.py +64 -0
  79. dex/tests/workflows/abnormal_exit_workflow.py +42 -0
  80. dex/tests/workflows/basic_workflow.py +62 -0
  81. dex/tests/workflows/conditional_complete_workflow.py +95 -0
  82. dex/tests/workflows/describe_workflow.py +46 -0
  83. dex/tests/workflows/empty_data_workflow.py +45 -0
  84. dex/tests/workflows/internal_channel_workflow.py +129 -0
  85. dex/tests/workflows/internal_channel_workflow_with_no_prefix_channel.py +100 -0
  86. dex/tests/workflows/java_duplicate_rpc_memo_workflow.py +276 -0
  87. dex/tests/workflows/persistence_data_attributes_workflow.py +98 -0
  88. dex/tests/workflows/persistence_search_attributes_workflow.py +159 -0
  89. dex/tests/workflows/persistence_state_execution_local_workflow.py +63 -0
  90. dex/tests/workflows/recovery_workflow.py +82 -0
  91. dex/tests/workflows/rpc_memo_workflow.py +231 -0
  92. dex/tests/workflows/rpc_workflow.py +117 -0
  93. dex/tests/workflows/state_options_override_workflow.py +93 -0
  94. dex/tests/workflows/state_options_workflow.py +84 -0
  95. dex/tests/workflows/timer_workflow.py +46 -0
  96. dex/tests/workflows/wait_for_state_with_state_execution_id_workflow.py +70 -0
  97. dex/tests/workflows/wait_for_state_with_wait_for_key_workflow.py +71 -0
  98. dex/tests/workflows/wait_internal_channel_workflow.py +47 -0
  99. dex/tests/workflows/wait_signal_workflow.py +147 -0
  100. dex/timer.py +21 -0
  101. dex/type_store.py +99 -0
  102. dex/unregistered_client.py +585 -0
  103. dex/utils/__init__.py +3 -0
  104. dex/utils/dex_typing.py +25 -0
  105. dex/utils/persistence_utils.py +32 -0
  106. dex/wait.py +49 -0
  107. dex/worker.py +121 -0
  108. dex/worker_options.py +22 -0
  109. dex/worker_service.py +432 -0
  110. dex/workflow.py +79 -0
  111. dex/workflow_context.py +44 -0
  112. dex/workflow_info.py +16 -0
  113. dex/workflow_options.py +74 -0
  114. dex/workflow_state.py +123 -0
  115. dex/workflow_state_options.py +154 -0
  116. dex_python_sdk-0.0.2.dist-info/METADATA +202 -0
  117. dex_python_sdk-0.0.2.dist-info/RECORD +121 -0
  118. dex_python_sdk-0.0.2.dist-info/WHEEL +4 -0
  119. dex_python_sdk-0.0.2.dist-info/licenses/LEGACY_NOTICES.md +61 -0
  120. dex_python_sdk-0.0.2.dist-info/licenses/LICENSE +192 -0
  121. dex_python_sdk-0.0.2.dist-info/sboms/dex-blob-cache-python.cyclonedx.json +2406 -0
@@ -0,0 +1,82 @@
1
+ # Legacy Materials in this file remain under their original licenses.
2
+ # See LEGACY_NOTICES.md.
3
+
4
+ # Modifications Copyright (c) 2026 Super Durable, Inc.
5
+ #
6
+ # Modifications after the Legacy Cutoff are licensed under the
7
+ # Super Durable Source License 1.0.
8
+ # Legacy Materials remain under their original licenses.
9
+ # See LICENSE and LEGACY_NOTICES.md.
10
+
11
+ from dex.command_results import CommandResults
12
+ from dex.communication import Communication
13
+ from dex.dex_api.models import RetryPolicy
14
+ from dex.dex_api.models.wait_until_api_failure_policy import WaitUntilApiFailurePolicy
15
+ from dex.persistence import Persistence
16
+ from dex.state_decision import StateDecision
17
+ from dex.state_schema import StateSchema
18
+ from dex.workflow import ObjectWorkflow
19
+ from dex.workflow_context import WorkflowContext
20
+ from dex.workflow_state import T, WorkflowState
21
+ from dex.workflow_state_options import WorkflowStateOptions
22
+
23
+ class FailWaitUntilState(WorkflowState[None]):
24
+ def wait_until(
25
+ self,
26
+ ctx: WorkflowContext,
27
+ input: T,
28
+ persistence: Persistence,
29
+ communication: Communication,
30
+ ):
31
+ raise RuntimeError("failed wait_until")
32
+
33
+ def execute(
34
+ self,
35
+ ctx: WorkflowContext,
36
+ input: T,
37
+ command_results: CommandResults,
38
+ persistence: Persistence,
39
+ communication: Communication,
40
+ ):
41
+ return StateDecision.single_next_state(FailExecuteState)
42
+
43
+ def get_state_options(self) -> WorkflowStateOptions:
44
+ return WorkflowStateOptions(
45
+ execute_api_retry_policy=RetryPolicy(maximum_attempts=1),
46
+ wait_until_api_retry_policy=RetryPolicy(maximum_attempts=1),
47
+ proceed_to_execute_when_wait_until_retry_exhausted=WaitUntilApiFailurePolicy.PROCEED_ON_FAILURE,
48
+ )
49
+
50
+ class FailExecuteState(WorkflowState[None]):
51
+ def execute(
52
+ self,
53
+ ctx: WorkflowContext,
54
+ input: T,
55
+ command_results: CommandResults,
56
+ persistence: Persistence,
57
+ communication: Communication,
58
+ ) -> StateDecision:
59
+ raise RuntimeError("a random error to fail the state")
60
+
61
+ def get_state_options(self) -> WorkflowStateOptions:
62
+ return WorkflowStateOptions(
63
+ execute_api_retry_policy=RetryPolicy(maximum_attempts=1),
64
+ proceed_to_state_when_execute_retry_exhausted=RecoveryState,
65
+ )
66
+
67
+ class RecoveryState(WorkflowState[None]):
68
+ def execute(
69
+ self,
70
+ ctx: WorkflowContext,
71
+ input: T,
72
+ command_results: CommandResults,
73
+ persistence: Persistence,
74
+ communication: Communication,
75
+ ) -> StateDecision:
76
+ return StateDecision.graceful_complete_workflow("done")
77
+
78
+ class RecoveryWorkflow(ObjectWorkflow):
79
+ def get_workflow_states(self) -> StateSchema:
80
+ return StateSchema.with_starting_state(
81
+ FailWaitUntilState(), FailExecuteState(), RecoveryState()
82
+ )
@@ -0,0 +1,231 @@
1
+ # Legacy Materials in this file remain under their original licenses.
2
+ # See LEGACY_NOTICES.md.
3
+
4
+ # Modifications Copyright (c) 2026 Super Durable, Inc.
5
+ #
6
+ # Modifications after the Legacy Cutoff are licensed under the
7
+ # Super Durable Source License 1.0.
8
+ # Legacy Materials remain under their original licenses.
9
+ # See LICENSE and LEGACY_NOTICES.md.
10
+
11
+ from typing import Optional
12
+
13
+ from dex.command_request import CommandRequest, InternalChannelCommand
14
+ from dex.command_results import CommandResults
15
+ from dex.communication import Communication
16
+ from dex.communication_schema import CommunicationMethod, CommunicationSchema
17
+ from dex.dex_api.models.search_attribute_value_type import SearchAttributeValueType
18
+ from dex.persistence import Persistence
19
+ from dex.persistence_options import PersistenceOptions
20
+ from dex.persistence_schema import PersistenceField, PersistenceSchema
21
+ from dex.rpc import rpc
22
+ from dex.state_decision import StateDecision
23
+ from dex.state_schema import StateSchema
24
+ from dex.workflow import ObjectWorkflow
25
+ from dex.workflow_context import WorkflowContext
26
+ from dex.workflow_state import WorkflowState
27
+
28
+ INTERNAL_CHANNEL_NAME = "test-channel-1"
29
+ TEST_DATA_OBJECT_KEY = "data-obj-1"
30
+ TEST_SEARCH_ATTRIBUTE_KEY = "CustomKeywordField"
31
+ TEST_SEARCH_ATTRIBUTE_INT = "CustomIntField"
32
+ RPC_INPUT = "rpc-input"
33
+ RPC_OUTPUT = 100
34
+ TEST_STR = "test-str"
35
+ TEST_DELAY = 0.1
36
+
37
+ class RpcMemoWorkflow(ObjectWorkflow):
38
+ def get_workflow_states(self) -> StateSchema:
39
+ return StateSchema.with_starting_state(RpcMemoWorkflowState1())
40
+
41
+ def get_persistence_schema(self) -> PersistenceSchema:
42
+ return PersistenceSchema.create(
43
+ PersistenceField.data_attribute_def(TEST_DATA_OBJECT_KEY, str),
44
+ PersistenceField.search_attribute_def(
45
+ TEST_SEARCH_ATTRIBUTE_INT, SearchAttributeValueType.INT
46
+ ),
47
+ PersistenceField.search_attribute_def(
48
+ TEST_SEARCH_ATTRIBUTE_KEY, SearchAttributeValueType.KEYWORD
49
+ ),
50
+ )
51
+
52
+ def get_communication_schema(self) -> CommunicationSchema:
53
+ return CommunicationSchema.create(
54
+ CommunicationMethod.internal_channel_def(INTERNAL_CHANNEL_NAME, None)
55
+ )
56
+
57
+ def get_persistence_options(self) -> PersistenceOptions:
58
+ return PersistenceOptions(enable_caching=True)
59
+
60
+ @rpc()
61
+ def test_rpc_func1(
62
+ self,
63
+ ctx: WorkflowContext,
64
+ input: str,
65
+ persistence: Persistence,
66
+ communication: Communication,
67
+ ) -> int:
68
+ if not ctx.workflow_id or not ctx.workflow_run_id:
69
+ raise RuntimeError("invalid context")
70
+
71
+ persistence.set_data_attribute(TEST_DATA_OBJECT_KEY, None)
72
+ persistence.set_data_attribute(TEST_DATA_OBJECT_KEY, input)
73
+ persistence.set_search_attribute_keyword(TEST_SEARCH_ATTRIBUTE_KEY, input)
74
+ persistence.set_search_attribute_int64(TEST_SEARCH_ATTRIBUTE_INT, RPC_OUTPUT)
75
+ communication.publish_to_internal_channel(INTERNAL_CHANNEL_NAME, None)
76
+ return RPC_OUTPUT
77
+
78
+ @rpc()
79
+ def test_rpc_func0(
80
+ self,
81
+ ctx: WorkflowContext,
82
+ input: str,
83
+ persistence: Persistence,
84
+ communication: Communication,
85
+ ) -> int:
86
+ if not ctx.workflow_id or not ctx.workflow_run_id:
87
+ raise RuntimeError("invalid context")
88
+
89
+ persistence.set_data_attribute(TEST_DATA_OBJECT_KEY, TEST_STR)
90
+ persistence.set_search_attribute_keyword(TEST_SEARCH_ATTRIBUTE_KEY, TEST_STR)
91
+ persistence.set_search_attribute_int64(TEST_SEARCH_ATTRIBUTE_INT, RPC_OUTPUT)
92
+ communication.publish_to_internal_channel(INTERNAL_CHANNEL_NAME, None)
93
+ return RPC_OUTPUT
94
+
95
+ @rpc()
96
+ def test_rpc_proc1(
97
+ self,
98
+ ctx: WorkflowContext,
99
+ input: str,
100
+ persistence: Persistence,
101
+ communication: Communication,
102
+ ):
103
+ if not ctx.workflow_id or not ctx.workflow_run_id:
104
+ raise RuntimeError("invalid context")
105
+
106
+ persistence.set_data_attribute(TEST_DATA_OBJECT_KEY, input)
107
+ persistence.set_search_attribute_keyword(TEST_SEARCH_ATTRIBUTE_KEY, input)
108
+ persistence.set_search_attribute_int64(TEST_SEARCH_ATTRIBUTE_INT, RPC_OUTPUT)
109
+ communication.publish_to_internal_channel(INTERNAL_CHANNEL_NAME, None)
110
+
111
+ @rpc()
112
+ def test_rpc_proc0(
113
+ self,
114
+ ctx: WorkflowContext,
115
+ input: str,
116
+ persistence: Persistence,
117
+ communication: Communication,
118
+ ):
119
+ if not ctx.workflow_id or not ctx.workflow_run_id:
120
+ raise RuntimeError("invalid context")
121
+
122
+ persistence.set_data_attribute(TEST_DATA_OBJECT_KEY, TEST_STR)
123
+ persistence.set_search_attribute_keyword(TEST_SEARCH_ATTRIBUTE_KEY, TEST_STR)
124
+ persistence.set_search_attribute_int64(TEST_SEARCH_ATTRIBUTE_INT, RPC_OUTPUT)
125
+ communication.publish_to_internal_channel(INTERNAL_CHANNEL_NAME, None)
126
+
127
+ @rpc()
128
+ def test_rpc_func1_readonly(
129
+ self,
130
+ ctx: WorkflowContext,
131
+ input: str,
132
+ persistence: Persistence,
133
+ communication: Communication,
134
+ ) -> int:
135
+ if not ctx.workflow_id or not ctx.workflow_run_id:
136
+ raise RuntimeError("invalid context")
137
+ return RPC_OUTPUT
138
+
139
+ @rpc()
140
+ def test_rpc_set_data_attribute(
141
+ self,
142
+ ctx: WorkflowContext,
143
+ input: str,
144
+ persistence: Persistence,
145
+ communication: Communication,
146
+ ):
147
+ if not ctx.workflow_id or not ctx.workflow_run_id:
148
+ raise RuntimeError("invalid context")
149
+ persistence.set_data_attribute(TEST_DATA_OBJECT_KEY, input)
150
+
151
+ @rpc()
152
+ def test_rpc_get_data_attribute(
153
+ self,
154
+ ctx: WorkflowContext,
155
+ input: str,
156
+ persistence: Persistence,
157
+ communication: Communication,
158
+ ) -> str:
159
+ if not ctx.workflow_id or not ctx.workflow_run_id:
160
+ raise RuntimeError("invalid context")
161
+ return persistence.get_data_attribute(TEST_DATA_OBJECT_KEY)
162
+
163
+ @rpc(bypass_caching_for_strong_consistency=True)
164
+ def test_rpc_get_data_attribute_strong_consistency(
165
+ self,
166
+ ctx: WorkflowContext,
167
+ input: str,
168
+ persistence: Persistence,
169
+ communication: Communication,
170
+ ) -> str:
171
+ if not ctx.workflow_id or not ctx.workflow_run_id:
172
+ raise RuntimeError("invalid context")
173
+ return persistence.get_data_attribute(TEST_DATA_OBJECT_KEY)
174
+
175
+ @rpc()
176
+ def test_rpc_set_keyword(
177
+ self,
178
+ ctx: WorkflowContext,
179
+ input: str,
180
+ persistence: Persistence,
181
+ communication: Communication,
182
+ ):
183
+ if not ctx.workflow_id or not ctx.workflow_run_id:
184
+ raise RuntimeError("invalid context")
185
+ persistence.set_search_attribute_keyword(TEST_SEARCH_ATTRIBUTE_KEY, input)
186
+
187
+ @rpc(bypass_caching_for_strong_consistency=True)
188
+ def test_rpc_get_keyword_strong_consistency(
189
+ self,
190
+ ctx: WorkflowContext,
191
+ input: str,
192
+ persistence: Persistence,
193
+ communication: Communication,
194
+ ) -> Optional[str]:
195
+ if not ctx.workflow_id or not ctx.workflow_run_id:
196
+ raise RuntimeError("invalid context")
197
+ return persistence.get_search_attribute_keyword(TEST_SEARCH_ATTRIBUTE_KEY)
198
+
199
+ @rpc()
200
+ def test_rpc_get_keyword(
201
+ self,
202
+ ctx: WorkflowContext,
203
+ input: str,
204
+ persistence: Persistence,
205
+ communication: Communication,
206
+ ) -> Optional[str]:
207
+ if not ctx.workflow_id or not ctx.workflow_run_id:
208
+ raise RuntimeError("invalid context")
209
+ return persistence.get_search_attribute_keyword(TEST_SEARCH_ATTRIBUTE_KEY)
210
+
211
+ class RpcMemoWorkflowState1(WorkflowState[int]):
212
+ def wait_until(
213
+ self,
214
+ ctx: WorkflowContext,
215
+ input: int,
216
+ persistence: Persistence,
217
+ communication: Communication,
218
+ ) -> CommandRequest:
219
+ return CommandRequest.for_any_command_completed(
220
+ InternalChannelCommand.by_name(INTERNAL_CHANNEL_NAME)
221
+ )
222
+
223
+ def execute(
224
+ self,
225
+ ctx: WorkflowContext,
226
+ input: int,
227
+ command_results: CommandResults,
228
+ persistence: Persistence,
229
+ communication: Communication,
230
+ ) -> StateDecision:
231
+ return StateDecision.graceful_complete_workflow()
@@ -0,0 +1,117 @@
1
+ # Legacy Materials in this file remain under their original licenses.
2
+ # See LEGACY_NOTICES.md.
3
+
4
+ # Modifications Copyright (c) 2026 Super Durable, Inc.
5
+ #
6
+ # Modifications after the Legacy Cutoff are licensed under the
7
+ # Super Durable Source License 1.0.
8
+ # Legacy Materials remain under their original licenses.
9
+ # See LICENSE and LEGACY_NOTICES.md.
10
+
11
+ from dataclasses import dataclass
12
+
13
+ from dex.command_request import CommandRequest, InternalChannelCommand
14
+ from dex.command_results import CommandResults
15
+ from dex.communication import Communication
16
+ from dex.communication_schema import CommunicationMethod, CommunicationSchema
17
+ from dex.persistence import Persistence
18
+ from dex.persistence_schema import PersistenceField, PersistenceSchema
19
+ from dex.rpc import rpc
20
+ from dex.state_decision import StateDecision
21
+ from dex.state_schema import StateSchema
22
+ from dex.workflow import ObjectWorkflow
23
+ from dex.workflow_context import WorkflowContext
24
+ from dex.workflow_state import T, WorkflowState
25
+
26
+ test_data_attribute = "test-1"
27
+ channel_name = "test-2"
28
+ idle_channel_name = "test-3"
29
+
30
+ @dataclass
31
+ class Mydata:
32
+ strdata: str
33
+ intdata: int
34
+
35
+ class WaitState(WorkflowState[None]):
36
+ def wait_until(
37
+ self,
38
+ ctx: WorkflowContext,
39
+ input: T,
40
+ persistence: Persistence,
41
+ communication: Communication,
42
+ ) -> CommandRequest:
43
+ return CommandRequest.for_all_command_completed(
44
+ InternalChannelCommand.by_name(channel_name)
45
+ )
46
+
47
+ def execute(
48
+ self,
49
+ ctx: WorkflowContext,
50
+ input: T,
51
+ command_results: CommandResults,
52
+ persistence: Persistence,
53
+ communication: Communication,
54
+ ) -> StateDecision:
55
+ return StateDecision.single_next_state(EndState)
56
+
57
+ class EndState(WorkflowState[None]):
58
+ def execute(
59
+ self,
60
+ ctx: WorkflowContext,
61
+ input: T,
62
+ command_results: CommandResults,
63
+ persistence: Persistence,
64
+ communication: Communication,
65
+ ) -> StateDecision:
66
+ return StateDecision.graceful_complete_workflow("done")
67
+
68
+ class RPCWorkflow(ObjectWorkflow):
69
+ def get_persistence_schema(self) -> PersistenceSchema:
70
+ return PersistenceSchema.create(
71
+ PersistenceField.data_attribute_def(test_data_attribute, int)
72
+ )
73
+
74
+ def get_communication_schema(self) -> CommunicationSchema:
75
+ return CommunicationSchema.create(
76
+ CommunicationMethod.internal_channel_def(channel_name, type(None)),
77
+ CommunicationMethod.internal_channel_def(idle_channel_name, str),
78
+ )
79
+
80
+ def get_workflow_states(self) -> StateSchema:
81
+ return StateSchema.no_starting_state(WaitState(), EndState())
82
+
83
+ @rpc(timeout_seconds=100)
84
+ def test_simple_rpc(self):
85
+ return 123
86
+
87
+ @rpc()
88
+ def test_rpc_persistence_write(
89
+ self,
90
+ input: int,
91
+ persistence: Persistence,
92
+ ):
93
+ persistence.set_data_attribute(test_data_attribute, input)
94
+
95
+ @rpc()
96
+ def test_rpc_persistence_read(self, pers: Persistence):
97
+ return pers.get_data_attribute(test_data_attribute)
98
+
99
+ @rpc()
100
+ def test_rpc_trigger_state(self, pers: Persistence, com: Communication, i: int):
101
+ pers.set_data_attribute(test_data_attribute, i)
102
+ com.trigger_state_execution(WaitState)
103
+
104
+ @rpc()
105
+ def test_rpc_publish_channel(self, com: Communication):
106
+ com.publish_to_internal_channel(channel_name)
107
+
108
+ @rpc()
109
+ def test_rpc_publish_to_idle_channel(self, com: Communication, data: str):
110
+ com.publish_to_internal_channel(idle_channel_name, data)
111
+ return com.get_internal_channel_size(idle_channel_name)
112
+
113
+ @rpc()
114
+ def test_rpc_input_type(self, input: Mydata) -> Mydata:
115
+ if input.intdata != 100 or input.strdata != "test":
116
+ raise Exception("input type test failed")
117
+ return input
@@ -0,0 +1,93 @@
1
+ # Legacy Materials in this file remain under their original licenses.
2
+ # See LEGACY_NOTICES.md.
3
+
4
+ # Modifications Copyright (c) 2026 Super Durable, Inc.
5
+ #
6
+ # Modifications after the Legacy Cutoff are licensed under the
7
+ # Super Durable Source License 1.0.
8
+ # Legacy Materials remain under their original licenses.
9
+ # See LICENSE and LEGACY_NOTICES.md.
10
+
11
+ from dex.command_request import CommandRequest
12
+ from dex.command_results import CommandResults
13
+ from dex.communication import Communication
14
+ from dex.persistence_schema import PersistenceSchema, PersistenceField
15
+
16
+ from dex.dex_api.models import RetryPolicy, WaitUntilApiFailurePolicy
17
+ from dex.persistence import Persistence
18
+ from dex.state_decision import StateDecision
19
+ from dex.state_schema import StateSchema
20
+ from dex.workflow import ObjectWorkflow
21
+ from dex.workflow_context import WorkflowContext
22
+ from dex.workflow_state import T, WorkflowState
23
+ from dex.workflow_state_options import WorkflowStateOptions
24
+
25
+ output_da = "output_da"
26
+
27
+ class InitState(WorkflowState[str]):
28
+ def wait_until(
29
+ self,
30
+ ctx: WorkflowContext,
31
+ input: T,
32
+ persistence: Persistence,
33
+ communication: Communication,
34
+ ) -> CommandRequest:
35
+ persistence.set_data_attribute(
36
+ output_da, str(input) + "_InitState_waitUntil_completed"
37
+ )
38
+ return CommandRequest.empty()
39
+
40
+ def execute(
41
+ self,
42
+ ctx: WorkflowContext,
43
+ input: T,
44
+ command_results: CommandResults,
45
+ persistence: Persistence,
46
+ communication: Communication,
47
+ ) -> StateDecision:
48
+ data = persistence.get_data_attribute(output_da)
49
+ data += "_InitState_execute_completed"
50
+ return StateDecision.single_next_state(
51
+ NonInitState,
52
+ data,
53
+ WorkflowStateOptions(
54
+ wait_until_api_retry_policy=RetryPolicy(maximum_attempts=2),
55
+ proceed_to_execute_when_wait_until_retry_exhausted=WaitUntilApiFailurePolicy.PROCEED_ON_FAILURE,
56
+ ),
57
+ )
58
+
59
+ class NonInitState(WorkflowState[str]):
60
+ def wait_until(
61
+ self,
62
+ ctx: WorkflowContext,
63
+ input: T,
64
+ persistence: Persistence,
65
+ communication: Communication,
66
+ ) -> CommandRequest:
67
+ raise RuntimeError("test failure")
68
+
69
+ def execute(
70
+ self,
71
+ ctx: WorkflowContext,
72
+ input: T,
73
+ command_results: CommandResults,
74
+ persistence: Persistence,
75
+ communication: Communication,
76
+ ) -> StateDecision:
77
+ data = str(input) + "_NonInitState_execute_completed"
78
+ return StateDecision.graceful_complete_workflow(data)
79
+
80
+ def get_state_options(self) -> WorkflowStateOptions:
81
+ return WorkflowStateOptions(
82
+ wait_until_api_retry_policy=RetryPolicy(maximum_attempts=1),
83
+ proceed_to_execute_when_wait_until_retry_exhausted=WaitUntilApiFailurePolicy.FAIL_WORKFLOW_ON_FAILURE,
84
+ )
85
+
86
+ class StateOptionsOverrideWorkflow(ObjectWorkflow):
87
+ def get_persistence_schema(self) -> PersistenceSchema:
88
+ return PersistenceSchema.create(
89
+ PersistenceField.data_attribute_def(output_da, str),
90
+ )
91
+
92
+ def get_workflow_states(self) -> StateSchema:
93
+ return StateSchema.with_starting_state(InitState(), NonInitState())
@@ -0,0 +1,84 @@
1
+ # Legacy Materials in this file remain under their original licenses.
2
+ # See LEGACY_NOTICES.md.
3
+
4
+ # Modifications Copyright (c) 2026 Super Durable, Inc.
5
+ #
6
+ # Modifications after the Legacy Cutoff are licensed under the
7
+ # Super Durable Source License 1.0.
8
+ # Legacy Materials remain under their original licenses.
9
+ # See LICENSE and LEGACY_NOTICES.md.
10
+
11
+ from dex.command_request import CommandRequest
12
+ from dex.command_results import CommandResults
13
+ from dex.communication import Communication
14
+
15
+ from dex.dex_api.models import RetryPolicy, WaitUntilApiFailurePolicy
16
+ from dex.persistence import Persistence
17
+ from dex.state_decision import StateDecision
18
+ from dex.state_schema import StateSchema
19
+ from dex.workflow import ObjectWorkflow
20
+ from dex.workflow_context import WorkflowContext
21
+ from dex.workflow_state import T, WorkflowState
22
+ from dex.workflow_state_options import WorkflowStateOptions
23
+
24
+ class InitState1(WorkflowState[str]):
25
+ def get_state_options(self) -> WorkflowStateOptions:
26
+ return WorkflowStateOptions(
27
+ wait_until_api_retry_policy=RetryPolicy(maximum_attempts=1),
28
+ proceed_to_execute_when_wait_until_retry_exhausted=WaitUntilApiFailurePolicy.PROCEED_ON_FAILURE,
29
+ )
30
+
31
+ def wait_until(
32
+ self,
33
+ ctx: WorkflowContext,
34
+ input: T,
35
+ persistence: Persistence,
36
+ communication: Communication,
37
+ ) -> CommandRequest:
38
+ raise RuntimeError("test failure")
39
+
40
+ def execute(
41
+ self,
42
+ ctx: WorkflowContext,
43
+ input: T,
44
+ command_results: CommandResults,
45
+ persistence: Persistence,
46
+ communication: Communication,
47
+ ) -> StateDecision:
48
+ data = "InitState1_execute_completed"
49
+ return StateDecision.graceful_complete_workflow(data)
50
+
51
+ class InitState2(WorkflowState[str]):
52
+ def get_state_options(self) -> WorkflowStateOptions:
53
+ return WorkflowStateOptions(
54
+ wait_until_api_retry_policy=RetryPolicy(maximum_attempts=1),
55
+ proceed_to_execute_when_wait_until_retry_exhausted=WaitUntilApiFailurePolicy.FAIL_WORKFLOW_ON_FAILURE,
56
+ )
57
+
58
+ def wait_until(
59
+ self,
60
+ ctx: WorkflowContext,
61
+ input: T,
62
+ persistence: Persistence,
63
+ communication: Communication,
64
+ ) -> CommandRequest:
65
+ raise RuntimeError("test failure")
66
+
67
+ def execute(
68
+ self,
69
+ ctx: WorkflowContext,
70
+ input: T,
71
+ command_results: CommandResults,
72
+ persistence: Persistence,
73
+ communication: Communication,
74
+ ) -> StateDecision:
75
+ data = "InitState2_execute_completed"
76
+ return StateDecision.graceful_complete_workflow(data)
77
+
78
+ class StateOptionsWorkflow1(ObjectWorkflow):
79
+ def get_workflow_states(self) -> StateSchema:
80
+ return StateSchema.with_starting_state(InitState1())
81
+
82
+ class StateOptionsWorkflow2(ObjectWorkflow):
83
+ def get_workflow_states(self) -> StateSchema:
84
+ return StateSchema.with_starting_state(InitState2())
@@ -0,0 +1,46 @@
1
+ # Legacy Materials in this file remain under their original licenses.
2
+ # See LEGACY_NOTICES.md.
3
+
4
+ # Modifications Copyright (c) 2026 Super Durable, Inc.
5
+ #
6
+ # Modifications after the Legacy Cutoff are licensed under the
7
+ # Super Durable Source License 1.0.
8
+ # Legacy Materials remain under their original licenses.
9
+ # See LICENSE and LEGACY_NOTICES.md.
10
+
11
+ from dex.command_request import CommandRequest, TimerCommand
12
+ from dex.command_results import CommandResults
13
+ from dex.communication import Communication
14
+ from dex.persistence import Persistence
15
+ from dex.state_decision import StateDecision
16
+ from dex.state_schema import StateSchema
17
+ from dex.workflow import ObjectWorkflow
18
+ from dex.workflow_context import WorkflowContext
19
+ from dex.workflow_state import T, WorkflowState
20
+
21
+ class WaitState(WorkflowState[int]):
22
+ def wait_until(
23
+ self,
24
+ ctx: WorkflowContext,
25
+ input: int,
26
+ persistence: Persistence,
27
+ communication: Communication,
28
+ ) -> CommandRequest:
29
+ return CommandRequest.for_all_command_completed(
30
+ TimerCommand.by_seconds(input * 3600),
31
+ TimerCommand.by_seconds(input),
32
+ )
33
+
34
+ def execute(
35
+ self,
36
+ ctx: WorkflowContext,
37
+ input: T,
38
+ command_results: CommandResults,
39
+ persistence: Persistence,
40
+ communication: Communication,
41
+ ) -> StateDecision:
42
+ return StateDecision.graceful_complete_workflow()
43
+
44
+ class TimerWorkflow(ObjectWorkflow):
45
+ def get_workflow_states(self) -> StateSchema:
46
+ return StateSchema.with_starting_state(WaitState())