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.
- dex/__init__.py +14 -0
- dex/client.py +568 -0
- dex/client_options.py +34 -0
- dex/command_request.py +130 -0
- dex/command_results.py +116 -0
- dex/communication.py +141 -0
- dex/communication_schema.py +68 -0
- dex/data_attributes.py +75 -0
- dex/dexpb/__init__.py +1 -0
- dex/dexpb/dex_pb2.py +305 -0
- dex/dexpb/dex_pb2.pyi +1273 -0
- dex/dexpb/dex_pb2_grpc.py +1126 -0
- dex/errors.py +132 -0
- dex/object_encoder.py +818 -0
- dex/persistence.py +94 -0
- dex/persistence_options.py +24 -0
- dex/persistence_schema.py +58 -0
- dex/py.typed +1 -0
- dex/registry.py +209 -0
- dex/reset_workflow_type_and_options.py +77 -0
- dex/rpc.py +100 -0
- dex/search_attributes.py +189 -0
- dex/state_decision.py +161 -0
- dex/state_execution_locals.py +71 -0
- dex/state_movement.py +123 -0
- dex/state_schema.py +54 -0
- dex/stop_workflow_options.py +23 -0
- dex/tests/__init__.py +84 -0
- dex/tests/dex-service-env/.env +7 -0
- dex/tests/dex-service-env/docker-compose-init.sh +44 -0
- dex/tests/dex-service-env/docker-compose.yml +87 -0
- dex/tests/dex-service-env/dynamicconfig/README.md +39 -0
- dex/tests/dex-service-env/dynamicconfig/development-sql.yaml +6 -0
- dex/tests/dex-service-env/dynamicconfig/docker.yaml +0 -0
- dex/tests/test_abnormal_exit_workflow.py +48 -0
- dex/tests/test_basic_workflow.py +75 -0
- dex/tests/test_conditional_complete.py +56 -0
- dex/tests/test_describe_workflow.py +45 -0
- dex/tests/test_empty_data_decodes_properly.py +79 -0
- dex/tests/test_internal_channel.py +33 -0
- dex/tests/test_internal_channel_with_no_prefix_channel.py +46 -0
- dex/tests/test_persistence_data_attributes.py +67 -0
- dex/tests/test_persistence_search_attributes.py +132 -0
- dex/tests/test_persistence_state_execution_locals.py +43 -0
- dex/tests/test_rpc.py +69 -0
- dex/tests/test_rpc_with_memo.py +200 -0
- dex/tests/test_rpc_with_memo_duplicate_java_tests.py +122 -0
- dex/tests/test_signal.py +56 -0
- dex/tests/test_skip_wait_until.py +85 -0
- dex/tests/test_state_failure_recovery.py +33 -0
- dex/tests/test_timer.py +40 -0
- dex/tests/test_wait_for_state_execution_completion.py +58 -0
- dex/tests/test_workflow_errors.py +92 -0
- dex/tests/test_workflow_state_options.py +123 -0
- dex/tests/test_workflow_state_options_override.py +49 -0
- dex/tests/worker_server.py +74 -0
- dex/tests/workflows/abnormal_exit_workflow.py +48 -0
- dex/tests/workflows/basic_workflow.py +69 -0
- dex/tests/workflows/conditional_complete_workflow.py +101 -0
- dex/tests/workflows/describe_workflow.py +52 -0
- dex/tests/workflows/empty_data_workflow.py +51 -0
- dex/tests/workflows/internal_channel_workflow.py +137 -0
- dex/tests/workflows/internal_channel_workflow_with_no_prefix_channel.py +108 -0
- dex/tests/workflows/java_duplicate_rpc_memo_workflow.py +285 -0
- dex/tests/workflows/persistence_data_attributes_workflow.py +104 -0
- dex/tests/workflows/persistence_search_attributes_workflow.py +167 -0
- dex/tests/workflows/persistence_state_execution_local_workflow.py +69 -0
- dex/tests/workflows/recovery_workflow.py +90 -0
- dex/tests/workflows/rpc_memo_workflow.py +238 -0
- dex/tests/workflows/rpc_workflow.py +125 -0
- dex/tests/workflows/state_options_override_workflow.py +100 -0
- dex/tests/workflows/state_options_workflow.py +92 -0
- dex/tests/workflows/timer_workflow.py +52 -0
- dex/tests/workflows/wait_for_state_with_state_execution_id_workflow.py +77 -0
- dex/tests/workflows/wait_for_state_with_wait_for_key_workflow.py +78 -0
- dex/tests/workflows/wait_internal_channel_workflow.py +53 -0
- dex/tests/workflows/wait_signal_workflow.py +154 -0
- dex/type_store.py +105 -0
- dex/unregistered_client.py +595 -0
- dex/utils/__init__.py +14 -0
- dex/utils/dex_typing.py +31 -0
- dex/utils/persistence_utils.py +37 -0
- dex/worker_service.py +441 -0
- dex/workflow.py +86 -0
- dex/workflow_context.py +50 -0
- dex/workflow_info.py +21 -0
- dex/workflow_options.py +79 -0
- dex/workflow_state.py +134 -0
- dex/workflow_state_options.py +160 -0
- dex_python_sdk-0.0.1.dist-info/METADATA +140 -0
- dex_python_sdk-0.0.1.dist-info/RECORD +93 -0
- dex_python_sdk-0.0.1.dist-info/WHEEL +4 -0
- 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.
|