dex-python-sdk 0.0.1__py3-none-any.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 (93) hide show
  1. dex/__init__.py +14 -0
  2. dex/client.py +568 -0
  3. dex/client_options.py +34 -0
  4. dex/command_request.py +130 -0
  5. dex/command_results.py +116 -0
  6. dex/communication.py +141 -0
  7. dex/communication_schema.py +68 -0
  8. dex/data_attributes.py +75 -0
  9. dex/dexpb/__init__.py +1 -0
  10. dex/dexpb/dex_pb2.py +305 -0
  11. dex/dexpb/dex_pb2.pyi +1273 -0
  12. dex/dexpb/dex_pb2_grpc.py +1126 -0
  13. dex/errors.py +132 -0
  14. dex/object_encoder.py +818 -0
  15. dex/persistence.py +94 -0
  16. dex/persistence_options.py +24 -0
  17. dex/persistence_schema.py +58 -0
  18. dex/py.typed +1 -0
  19. dex/registry.py +209 -0
  20. dex/reset_workflow_type_and_options.py +77 -0
  21. dex/rpc.py +100 -0
  22. dex/search_attributes.py +189 -0
  23. dex/state_decision.py +161 -0
  24. dex/state_execution_locals.py +71 -0
  25. dex/state_movement.py +123 -0
  26. dex/state_schema.py +54 -0
  27. dex/stop_workflow_options.py +23 -0
  28. dex/tests/__init__.py +84 -0
  29. dex/tests/dex-service-env/.env +7 -0
  30. dex/tests/dex-service-env/docker-compose-init.sh +44 -0
  31. dex/tests/dex-service-env/docker-compose.yml +87 -0
  32. dex/tests/dex-service-env/dynamicconfig/README.md +39 -0
  33. dex/tests/dex-service-env/dynamicconfig/development-sql.yaml +6 -0
  34. dex/tests/dex-service-env/dynamicconfig/docker.yaml +0 -0
  35. dex/tests/test_abnormal_exit_workflow.py +48 -0
  36. dex/tests/test_basic_workflow.py +75 -0
  37. dex/tests/test_conditional_complete.py +56 -0
  38. dex/tests/test_describe_workflow.py +45 -0
  39. dex/tests/test_empty_data_decodes_properly.py +79 -0
  40. dex/tests/test_internal_channel.py +33 -0
  41. dex/tests/test_internal_channel_with_no_prefix_channel.py +46 -0
  42. dex/tests/test_persistence_data_attributes.py +67 -0
  43. dex/tests/test_persistence_search_attributes.py +132 -0
  44. dex/tests/test_persistence_state_execution_locals.py +43 -0
  45. dex/tests/test_rpc.py +69 -0
  46. dex/tests/test_rpc_with_memo.py +200 -0
  47. dex/tests/test_rpc_with_memo_duplicate_java_tests.py +122 -0
  48. dex/tests/test_signal.py +56 -0
  49. dex/tests/test_skip_wait_until.py +85 -0
  50. dex/tests/test_state_failure_recovery.py +33 -0
  51. dex/tests/test_timer.py +40 -0
  52. dex/tests/test_wait_for_state_execution_completion.py +58 -0
  53. dex/tests/test_workflow_errors.py +92 -0
  54. dex/tests/test_workflow_state_options.py +123 -0
  55. dex/tests/test_workflow_state_options_override.py +49 -0
  56. dex/tests/worker_server.py +74 -0
  57. dex/tests/workflows/abnormal_exit_workflow.py +48 -0
  58. dex/tests/workflows/basic_workflow.py +69 -0
  59. dex/tests/workflows/conditional_complete_workflow.py +101 -0
  60. dex/tests/workflows/describe_workflow.py +52 -0
  61. dex/tests/workflows/empty_data_workflow.py +51 -0
  62. dex/tests/workflows/internal_channel_workflow.py +137 -0
  63. dex/tests/workflows/internal_channel_workflow_with_no_prefix_channel.py +108 -0
  64. dex/tests/workflows/java_duplicate_rpc_memo_workflow.py +285 -0
  65. dex/tests/workflows/persistence_data_attributes_workflow.py +104 -0
  66. dex/tests/workflows/persistence_search_attributes_workflow.py +167 -0
  67. dex/tests/workflows/persistence_state_execution_local_workflow.py +69 -0
  68. dex/tests/workflows/recovery_workflow.py +90 -0
  69. dex/tests/workflows/rpc_memo_workflow.py +238 -0
  70. dex/tests/workflows/rpc_workflow.py +125 -0
  71. dex/tests/workflows/state_options_override_workflow.py +100 -0
  72. dex/tests/workflows/state_options_workflow.py +92 -0
  73. dex/tests/workflows/timer_workflow.py +52 -0
  74. dex/tests/workflows/wait_for_state_with_state_execution_id_workflow.py +77 -0
  75. dex/tests/workflows/wait_for_state_with_wait_for_key_workflow.py +78 -0
  76. dex/tests/workflows/wait_internal_channel_workflow.py +53 -0
  77. dex/tests/workflows/wait_signal_workflow.py +154 -0
  78. dex/type_store.py +105 -0
  79. dex/unregistered_client.py +595 -0
  80. dex/utils/__init__.py +14 -0
  81. dex/utils/dex_typing.py +31 -0
  82. dex/utils/persistence_utils.py +37 -0
  83. dex/worker_service.py +441 -0
  84. dex/workflow.py +86 -0
  85. dex/workflow_context.py +50 -0
  86. dex/workflow_info.py +21 -0
  87. dex/workflow_options.py +79 -0
  88. dex/workflow_state.py +134 -0
  89. dex/workflow_state_options.py +160 -0
  90. dex_python_sdk-0.0.1.dist-info/METADATA +140 -0
  91. dex_python_sdk-0.0.1.dist-info/RECORD +93 -0
  92. dex_python_sdk-0.0.1.dist-info/WHEEL +4 -0
  93. dex_python_sdk-0.0.1.dist-info/licenses/LICENSE +201 -0
dex/command_request.py ADDED
@@ -0,0 +1,130 @@
1
+ # Copyright (c) 2022-2026 Super Durable, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from dataclasses import dataclass
16
+ from typing import Optional, Union
17
+
18
+ from dex.errors import WorkflowDefinitionError
19
+ from dex.dex_api.models import CommandWaitingType
20
+ from dex.dex_api.models.command_combination import CommandCombination
21
+ from dex.dex_api.models.command_request import (
22
+ CommandRequest as IdlCommandRequest,
23
+ )
24
+ from dex.dex_api.models.inter_state_channel_command import (
25
+ InterStateChannelCommand as IdlInternalChannelCommand,
26
+ )
27
+ from dex.dex_api.models.signal_command import SignalCommand as IdlSignalCommand
28
+ from dex.dex_api.models.timer_command import TimerCommand as IdlTimerCommand
29
+
30
+
31
+ @dataclass
32
+ class TimerCommand:
33
+ command_id: str
34
+ duration_seconds: int
35
+
36
+ @classmethod
37
+ def by_seconds(cls, duration_seconds: int, command_id: Optional[str] = None):
38
+ return TimerCommand(
39
+ command_id if command_id is not None else "", duration_seconds
40
+ )
41
+
42
+
43
+ @dataclass
44
+ class InternalChannelCommand:
45
+ command_id: str
46
+ channel_name: str
47
+
48
+ @classmethod
49
+ def by_name(cls, channel_name: str, command_id: Optional[str] = None):
50
+ return InternalChannelCommand(
51
+ command_id if command_id is not None else "", channel_name
52
+ )
53
+
54
+
55
+ @dataclass
56
+ class SignalChannelCommand:
57
+ command_id: str
58
+ channel_name: str
59
+
60
+ @classmethod
61
+ def by_name(cls, channel_name: str, command_id: Optional[str] = None):
62
+ return SignalChannelCommand(
63
+ command_id if command_id is not None else "",
64
+ channel_name,
65
+ )
66
+
67
+
68
+ BaseCommand = Union[TimerCommand, InternalChannelCommand, SignalChannelCommand]
69
+
70
+
71
+ @dataclass
72
+ class CommandRequest:
73
+ commands: list[BaseCommand]
74
+ command_waiting_type: CommandWaitingType
75
+ command_combinations: list[CommandCombination]
76
+
77
+ @classmethod
78
+ def for_any_command_completed(cls, *commands: BaseCommand):
79
+ bc = [c for c in commands]
80
+ return CommandRequest(bc, CommandWaitingType.ANY_COMPLETED, [])
81
+
82
+ @classmethod
83
+ def for_all_command_completed(cls, *commands: BaseCommand):
84
+ bc = [c for c in commands]
85
+ return CommandRequest(bc, CommandWaitingType.ALL_COMPLETED, [])
86
+
87
+ @classmethod
88
+ def for_any_command_combination_completed(
89
+ cls, command_combinations_list: list[list[str]], *commands: BaseCommand
90
+ ):
91
+ return CommandRequest(
92
+ list(commands),
93
+ CommandWaitingType.ANY_COMBINATION_COMPLETED,
94
+ [CommandCombination(c) for c in command_combinations_list],
95
+ )
96
+
97
+ @classmethod
98
+ def empty(cls):
99
+ return CommandRequest(list(), CommandWaitingType.ALL_COMPLETED, [])
100
+
101
+
102
+ def _to_idl_command_request(request: CommandRequest) -> IdlCommandRequest:
103
+ req = IdlCommandRequest(
104
+ command_waiting_type=request.command_waiting_type,
105
+ )
106
+
107
+ timer_commands = []
108
+ internal_channel_commands = []
109
+ signal_commands = []
110
+ for t in request.commands:
111
+ if isinstance(t, TimerCommand):
112
+ timer_commands.append(IdlTimerCommand(t.duration_seconds, t.command_id))
113
+ elif isinstance(t, InternalChannelCommand):
114
+ internal_channel_commands.append(
115
+ IdlInternalChannelCommand(t.channel_name, t.command_id)
116
+ )
117
+ elif isinstance(t, SignalChannelCommand):
118
+ signal_commands.append(IdlSignalCommand(t.channel_name, t.command_id))
119
+ else:
120
+ raise WorkflowDefinitionError(f"unknown command {t.__class__.__qualname__}")
121
+
122
+ if len(timer_commands) > 0:
123
+ req.timer_commands = timer_commands
124
+ if len(internal_channel_commands) > 0:
125
+ req.inter_state_channel_commands = internal_channel_commands
126
+ if len(signal_commands) > 0:
127
+ req.signal_commands = signal_commands
128
+ if len(request.command_combinations) > 0:
129
+ req.command_combinations = request.command_combinations
130
+ return req
dex/command_results.py ADDED
@@ -0,0 +1,116 @@
1
+ # Copyright (c) 2022-2026 Super Durable, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import typing
16
+ from dataclasses import dataclass
17
+ from typing import Any, Union, Optional
18
+
19
+ from dex.errors import WorkflowDefinitionError, NotRegisteredError
20
+ from dex.dex_api.models import (
21
+ ChannelRequestStatus,
22
+ CommandResults as IdlCommandResults,
23
+ TimerStatus,
24
+ )
25
+ from dex.dex_api.types import Unset
26
+ from dex.object_encoder import ObjectEncoder
27
+ from dex.type_store import TypeStore
28
+
29
+
30
+ @dataclass
31
+ class TimerCommandResult:
32
+ status: TimerStatus
33
+ command_id: str
34
+
35
+
36
+ @dataclass
37
+ class InternalChannelCommandResult:
38
+ channel_name: str
39
+ value: Any
40
+ status: ChannelRequestStatus
41
+ command_id: str
42
+
43
+
44
+ @dataclass
45
+ class SignalChannelCommandResult:
46
+ channel_name: str
47
+ value: Any
48
+ status: ChannelRequestStatus
49
+ command_id: str
50
+
51
+
52
+ @dataclass
53
+ class CommandResults:
54
+ timer_commands: list[TimerCommandResult]
55
+ internal_channel_commands: list[InternalChannelCommandResult]
56
+ signal_channel_commands: list[SignalChannelCommandResult]
57
+ wait_until_api_succeeded: Optional[bool] = None
58
+
59
+
60
+ def from_idl_command_results(
61
+ idl_results: Union[Unset, IdlCommandResults],
62
+ internal_channel_types: TypeStore,
63
+ signal_channel_types: dict[str, typing.Optional[type]],
64
+ object_encoder: ObjectEncoder,
65
+ ) -> CommandResults:
66
+ results = CommandResults(list(), list(), list(), None)
67
+ if isinstance(idl_results, Unset):
68
+ return results
69
+
70
+ if not isinstance(idl_results.timer_results, Unset):
71
+ for timer in idl_results.timer_results:
72
+ results.timer_commands.append(
73
+ TimerCommandResult(timer.timer_status, timer.command_id)
74
+ )
75
+
76
+ if not isinstance(idl_results.inter_state_channel_results, Unset):
77
+ for inter in idl_results.inter_state_channel_results:
78
+
79
+ try:
80
+ val_type = internal_channel_types.get_type(inter.channel_name)
81
+ except NotRegisteredError as exception:
82
+ raise WorkflowDefinitionError(
83
+ "internal channel is not registered: " + inter.channel_name
84
+ ) from exception
85
+
86
+ encoded = object_encoder.decode(inter.value, val_type)
87
+
88
+ results.internal_channel_commands.append(
89
+ InternalChannelCommandResult(
90
+ inter.channel_name,
91
+ encoded,
92
+ inter.request_status,
93
+ inter.command_id,
94
+ )
95
+ )
96
+
97
+ if not isinstance(idl_results.signal_results, Unset):
98
+ for sig in idl_results.signal_results:
99
+ results.signal_channel_commands.append(
100
+ SignalChannelCommandResult(
101
+ sig.signal_channel_name,
102
+ object_encoder.decode(
103
+ sig.signal_value,
104
+ signal_channel_types.get(sig.signal_channel_name),
105
+ ),
106
+ sig.signal_request_status,
107
+ sig.command_id,
108
+ )
109
+ )
110
+
111
+ if not isinstance(idl_results.state_wait_until_failed, Unset):
112
+ # The server will set state_wait_until_failed to true if the waitUntil API failed.
113
+ # Hence, flag inversion is needed here to indicate that the waitUntil API succeeded.
114
+ results.wait_until_api_succeeded = not idl_results.state_wait_until_failed
115
+
116
+ return results
dex/communication.py ADDED
@@ -0,0 +1,141 @@
1
+ # Copyright (c) 2022-2026 Super Durable, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from typing import Any, Optional, Union
16
+
17
+ from dex.errors import WorkflowDefinitionError, NotRegisteredError
18
+ from dex.dex_api.models import (
19
+ EncodedObject,
20
+ InterStateChannelPublishing,
21
+ WorkflowWorkerRpcRequestInternalChannelInfos,
22
+ WorkflowWorkerRpcRequestSignalChannelInfos,
23
+ )
24
+ from dex.dex_api.types import Unset
25
+ from dex.object_encoder import ObjectEncoder
26
+ from dex.state_movement import StateMovement
27
+ from dex.type_store import TypeStore
28
+
29
+
30
+ class Communication:
31
+ _internal_channel_type_store: TypeStore
32
+ _signal_channel_type_store: dict[str, Optional[type]]
33
+ _object_encoder: ObjectEncoder
34
+ _to_publish_internal_channel: dict[str, list[Union[EncodedObject, Unset]]]
35
+ _state_movements: list[StateMovement]
36
+ _internal_channel_infos: Optional[WorkflowWorkerRpcRequestInternalChannelInfos]
37
+ _signal_channel_infos: Optional[WorkflowWorkerRpcRequestSignalChannelInfos]
38
+
39
+ def __init__(
40
+ self,
41
+ internal_channel_type_store: TypeStore,
42
+ signal_channel_type_store: dict[str, Optional[type]],
43
+ object_encoder: ObjectEncoder,
44
+ internal_channel_infos: Optional[WorkflowWorkerRpcRequestInternalChannelInfos],
45
+ signal_channel_infos: Optional[WorkflowWorkerRpcRequestSignalChannelInfos],
46
+ ):
47
+ self._object_encoder = object_encoder
48
+ self._internal_channel_type_store = internal_channel_type_store
49
+ self._signal_channel_type_store = signal_channel_type_store
50
+ self._to_publish_internal_channel = {}
51
+ self._state_movements = []
52
+ self._internal_channel_infos = internal_channel_infos
53
+ self._signal_channel_infos = signal_channel_infos
54
+
55
+ def trigger_state_execution(self, state: Union[str, type], state_input: Any = None):
56
+ """
57
+
58
+ Args:
59
+ state: the workflowState TODO the type hint should be type[WorkflowState]
60
+ state_input: the input of the state
61
+ """
62
+ movement = StateMovement.create(state, state_input)
63
+ self._state_movements.append(movement)
64
+
65
+ def publish_to_internal_channel(self, channel_name: str, value: Any = None):
66
+ try:
67
+ registered_type = self._internal_channel_type_store.get_type(channel_name)
68
+ except NotRegisteredError as exception:
69
+ raise WorkflowDefinitionError(
70
+ f"InternalChannel channel_name is not defined {channel_name}"
71
+ ) from exception
72
+
73
+ if (
74
+ value is not None
75
+ and registered_type is not None
76
+ and not isinstance(value, registered_type)
77
+ ):
78
+ raise WorkflowDefinitionError(
79
+ f"InternalChannel value is not of type {registered_type}"
80
+ )
81
+ vals = self._to_publish_internal_channel.get(channel_name)
82
+ if vals is None:
83
+ vals = []
84
+ vals.append(self._object_encoder.encode(value))
85
+ self._to_publish_internal_channel[channel_name] = vals
86
+
87
+ def get_to_publishing_internal_channel(self) -> list[InterStateChannelPublishing]:
88
+ pubs = []
89
+ for name, vals in self._to_publish_internal_channel.items():
90
+ for val in vals:
91
+ pubs.append(InterStateChannelPublishing(name, val))
92
+ return pubs
93
+
94
+ def get_to_trigger_state_movements(self) -> list[StateMovement]:
95
+ return self._state_movements
96
+
97
+ def get_internal_channel_size(self, channel_name):
98
+ is_type_registered = self._internal_channel_type_store.is_valid_name_or_prefix(
99
+ channel_name
100
+ )
101
+
102
+ if is_type_registered is False:
103
+ raise WorkflowDefinitionError(
104
+ f"InternalChannel channel_name is not defined {channel_name}"
105
+ )
106
+
107
+ if (
108
+ self._internal_channel_infos is not None
109
+ and channel_name in self._internal_channel_infos
110
+ ):
111
+ server_channel_size = self._internal_channel_infos[channel_name].size
112
+ else:
113
+ server_channel_size = 0
114
+
115
+ if channel_name in self._to_publish_internal_channel:
116
+ buffer_channel_size = len(self._to_publish_internal_channel[channel_name])
117
+ else:
118
+ buffer_channel_size = 0
119
+
120
+ return server_channel_size + buffer_channel_size
121
+
122
+ def get_signal_channel_size(self, channel_name):
123
+ registered_type = self._signal_channel_type_store.get(channel_name)
124
+
125
+ if registered_type is None:
126
+ for name, t in self._signal_channel_type_store.items():
127
+ if channel_name.startswith(name):
128
+ registered_type = t
129
+
130
+ if registered_type is None:
131
+ raise WorkflowDefinitionError(
132
+ f"SignalChannel channel_name is not defined {channel_name}"
133
+ )
134
+
135
+ if (
136
+ self._signal_channel_infos is not None
137
+ and channel_name in self._signal_channel_infos
138
+ ):
139
+ return self._signal_channel_infos[channel_name].size
140
+ else:
141
+ return 0
@@ -0,0 +1,68 @@
1
+ # Copyright (c) 2022-2026 Super Durable, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from dataclasses import dataclass, field
16
+ from enum import Enum
17
+ from typing import List, Optional, Union
18
+
19
+
20
+ class CommunicationMethodType(Enum):
21
+ SignalChannel = 1
22
+ InternalChannel = 2
23
+
24
+
25
+ @dataclass
26
+ class CommunicationMethod:
27
+ name: str
28
+ method_type: CommunicationMethodType
29
+ value_type: Optional[type]
30
+ is_prefix: bool
31
+
32
+ @classmethod
33
+ def signal_channel_def(cls, name: str, value_type: Union[type, None]):
34
+ return CommunicationMethod(
35
+ name,
36
+ CommunicationMethodType.SignalChannel,
37
+ value_type if value_type is not None else type(None),
38
+ False,
39
+ )
40
+
41
+ @classmethod
42
+ def internal_channel_def(cls, name: str, value_type: Union[type, None]):
43
+ return CommunicationMethod(
44
+ name,
45
+ CommunicationMethodType.InternalChannel,
46
+ value_type if value_type is not None else type(None),
47
+ False,
48
+ )
49
+
50
+ @classmethod
51
+ def internal_channel_def_by_prefix(
52
+ cls, name_prefix: str, value_type: Union[type, None]
53
+ ):
54
+ return CommunicationMethod(
55
+ name_prefix,
56
+ CommunicationMethodType.InternalChannel,
57
+ value_type if value_type is not None else type(None),
58
+ True,
59
+ )
60
+
61
+
62
+ @dataclass
63
+ class CommunicationSchema:
64
+ communication_methods: List[CommunicationMethod] = field(default_factory=list)
65
+
66
+ @classmethod
67
+ def create(cls, *methods: CommunicationMethod):
68
+ return CommunicationSchema(list(methods))
dex/data_attributes.py ADDED
@@ -0,0 +1,75 @@
1
+ # Copyright (c) 2022-2026 Super Durable, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from typing import Any, Union
16
+
17
+ from dex.errors import WorkflowDefinitionError
18
+ from dex.dex_api.models import EncodedObject
19
+ from dex.dex_api.types import Unset
20
+ from dex.object_encoder import ObjectEncoder
21
+ from dex.type_store import TypeStore
22
+
23
+
24
+ class DataAttributes:
25
+ _type_store: TypeStore
26
+ _object_encoder: ObjectEncoder
27
+ _current_values: dict[str, Union[EncodedObject, None, Unset]]
28
+ _updated_values_to_return: dict[str, Union[EncodedObject, Unset]]
29
+
30
+ def __init__(
31
+ self,
32
+ type_store: TypeStore,
33
+ object_encoder: ObjectEncoder,
34
+ current_values: dict[str, Union[EncodedObject, None, Unset]],
35
+ ):
36
+ self._object_encoder = object_encoder
37
+ self._type_store = type_store
38
+ self._current_values = current_values
39
+ self._updated_values_to_return = {}
40
+
41
+ def get_data_attribute(self, key: str) -> Any:
42
+ is_registered = self._type_store.is_valid_name_or_prefix(key)
43
+ if not is_registered:
44
+ raise WorkflowDefinitionError(f"data attribute %s is not registered {key}")
45
+
46
+ encoded_object = self._current_values.get(key)
47
+ if encoded_object is None:
48
+ return None
49
+
50
+ registered_type = self._type_store.get_type(key)
51
+ return self._object_encoder.decode(encoded_object, registered_type)
52
+
53
+ def set_data_attribute(self, key: str, value: Any):
54
+ is_registered = self._type_store.is_valid_name_or_prefix(key)
55
+ if not is_registered:
56
+ raise WorkflowDefinitionError(
57
+ f"data attribute {key} is not registered {key}"
58
+ )
59
+
60
+ registered_type = self._type_store.get_type(key)
61
+ if (
62
+ value is not None
63
+ and registered_type is not None
64
+ and not isinstance(value, registered_type)
65
+ ):
66
+ raise WorkflowDefinitionError(
67
+ f"data attribute {key} is of the right type {registered_type}"
68
+ )
69
+
70
+ encoded_value = self._object_encoder.encode(value)
71
+ self._current_values[key] = encoded_value
72
+ self._updated_values_to_return[key] = encoded_value
73
+
74
+ def get_updated_values_to_return(self) -> dict[str, Union[EncodedObject, Unset]]:
75
+ return self._updated_values_to_return
dex/dexpb/__init__.py ADDED
@@ -0,0 +1 @@
1
+ # Generated package for Dex protobuf stubs.