cadence-python-client 0.1.0__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 (95) hide show
  1. cadence/__init__.py +18 -0
  2. cadence/_internal/__init__.py +8 -0
  3. cadence/_internal/activity/__init__.py +5 -0
  4. cadence/_internal/activity/_activity_executor.py +113 -0
  5. cadence/_internal/activity/_context.py +58 -0
  6. cadence/_internal/rpc/__init__.py +0 -0
  7. cadence/_internal/rpc/error.py +148 -0
  8. cadence/_internal/rpc/retry.py +104 -0
  9. cadence/_internal/rpc/yarpc.py +42 -0
  10. cadence/_internal/workflow/__init__.py +0 -0
  11. cadence/_internal/workflow/context.py +121 -0
  12. cadence/_internal/workflow/decision_events_iterator.py +161 -0
  13. cadence/_internal/workflow/decisions_helper.py +312 -0
  14. cadence/_internal/workflow/deterministic_event_loop.py +498 -0
  15. cadence/_internal/workflow/history_event_iterator.py +58 -0
  16. cadence/_internal/workflow/statemachine/__init__.py +0 -0
  17. cadence/_internal/workflow/statemachine/activity_state_machine.py +106 -0
  18. cadence/_internal/workflow/statemachine/decision_manager.py +157 -0
  19. cadence/_internal/workflow/statemachine/decision_state_machine.py +87 -0
  20. cadence/_internal/workflow/statemachine/event_dispatcher.py +76 -0
  21. cadence/_internal/workflow/statemachine/timer_state_machine.py +73 -0
  22. cadence/_internal/workflow/workflow_engine.py +245 -0
  23. cadence/_internal/workflow/workflow_intance.py +44 -0
  24. cadence/activity.py +255 -0
  25. cadence/api/v1/__init__.py +92 -0
  26. cadence/api/v1/common_pb2.py +90 -0
  27. cadence/api/v1/common_pb2.pyi +200 -0
  28. cadence/api/v1/common_pb2_grpc.py +24 -0
  29. cadence/api/v1/decision_pb2.py +67 -0
  30. cadence/api/v1/decision_pb2.pyi +225 -0
  31. cadence/api/v1/decision_pb2_grpc.py +24 -0
  32. cadence/api/v1/domain_pb2.py +68 -0
  33. cadence/api/v1/domain_pb2.pyi +145 -0
  34. cadence/api/v1/domain_pb2_grpc.py +24 -0
  35. cadence/api/v1/error_pb2.py +59 -0
  36. cadence/api/v1/error_pb2.pyi +82 -0
  37. cadence/api/v1/error_pb2_grpc.py +24 -0
  38. cadence/api/v1/history_pb2.py +134 -0
  39. cadence/api/v1/history_pb2.pyi +780 -0
  40. cadence/api/v1/history_pb2_grpc.py +24 -0
  41. cadence/api/v1/query_pb2.py +49 -0
  42. cadence/api/v1/query_pb2.pyi +59 -0
  43. cadence/api/v1/query_pb2_grpc.py +24 -0
  44. cadence/api/v1/service_domain_pb2.py +76 -0
  45. cadence/api/v1/service_domain_pb2.pyi +164 -0
  46. cadence/api/v1/service_domain_pb2_grpc.py +327 -0
  47. cadence/api/v1/service_meta_pb2.py +41 -0
  48. cadence/api/v1/service_meta_pb2.pyi +17 -0
  49. cadence/api/v1/service_meta_pb2_grpc.py +97 -0
  50. cadence/api/v1/service_visibility_pb2.py +71 -0
  51. cadence/api/v1/service_visibility_pb2.pyi +149 -0
  52. cadence/api/v1/service_visibility_pb2_grpc.py +362 -0
  53. cadence/api/v1/service_worker_pb2.py +116 -0
  54. cadence/api/v1/service_worker_pb2.pyi +350 -0
  55. cadence/api/v1/service_worker_pb2_grpc.py +743 -0
  56. cadence/api/v1/service_workflow_pb2.py +126 -0
  57. cadence/api/v1/service_workflow_pb2.pyi +395 -0
  58. cadence/api/v1/service_workflow_pb2_grpc.py +861 -0
  59. cadence/api/v1/tasklist_pb2.py +78 -0
  60. cadence/api/v1/tasklist_pb2.pyi +147 -0
  61. cadence/api/v1/tasklist_pb2_grpc.py +24 -0
  62. cadence/api/v1/visibility_pb2.py +47 -0
  63. cadence/api/v1/visibility_pb2.pyi +53 -0
  64. cadence/api/v1/visibility_pb2_grpc.py +24 -0
  65. cadence/api/v1/workflow_pb2.py +89 -0
  66. cadence/api/v1/workflow_pb2.pyi +365 -0
  67. cadence/api/v1/workflow_pb2_grpc.py +24 -0
  68. cadence/client.py +382 -0
  69. cadence/data_converter.py +78 -0
  70. cadence/error.py +111 -0
  71. cadence/metrics/__init__.py +12 -0
  72. cadence/metrics/constants.py +136 -0
  73. cadence/metrics/metrics.py +56 -0
  74. cadence/metrics/prometheus.py +165 -0
  75. cadence/sample/__init__.py +1 -0
  76. cadence/sample/client_example.py +15 -0
  77. cadence/sample/grpc_usage_example.py +230 -0
  78. cadence/sample/simple_usage_example.py +155 -0
  79. cadence/signal.py +174 -0
  80. cadence/worker/__init__.py +13 -0
  81. cadence/worker/_activity.py +60 -0
  82. cadence/worker/_base_task_handler.py +71 -0
  83. cadence/worker/_decision.py +62 -0
  84. cadence/worker/_decision_task_handler.py +285 -0
  85. cadence/worker/_poller.py +64 -0
  86. cadence/worker/_registry.py +245 -0
  87. cadence/worker/_types.py +26 -0
  88. cadence/worker/_worker.py +56 -0
  89. cadence/workflow.py +271 -0
  90. cadence_python_client-0.1.0.dist-info/METADATA +180 -0
  91. cadence_python_client-0.1.0.dist-info/RECORD +95 -0
  92. cadence_python_client-0.1.0.dist-info/WHEEL +5 -0
  93. cadence_python_client-0.1.0.dist-info/licenses/LICENSE +201 -0
  94. cadence_python_client-0.1.0.dist-info/licenses/NOTICE +19 -0
  95. cadence_python_client-0.1.0.dist-info/top_level.txt +1 -0
cadence/client.py ADDED
@@ -0,0 +1,382 @@
1
+ import os
2
+ import socket
3
+ import uuid
4
+ from datetime import timedelta
5
+ from typing import TypedDict, Unpack, Any, cast, Union
6
+
7
+ from grpc import ChannelCredentials, Compression
8
+ from google.protobuf.duration_pb2 import Duration
9
+
10
+ from cadence._internal.rpc.error import CadenceErrorInterceptor
11
+ from cadence._internal.rpc.retry import RetryInterceptor
12
+ from cadence._internal.rpc.yarpc import YarpcMetadataInterceptor
13
+ from cadence.api.v1.service_domain_pb2_grpc import DomainAPIStub
14
+ from cadence.api.v1.service_worker_pb2_grpc import WorkerAPIStub
15
+ from grpc.aio import Channel, ClientInterceptor, secure_channel, insecure_channel
16
+ from cadence.api.v1.service_workflow_pb2_grpc import WorkflowAPIStub
17
+ from cadence.api.v1.service_workflow_pb2 import (
18
+ SignalWorkflowExecutionRequest,
19
+ StartWorkflowExecutionRequest,
20
+ StartWorkflowExecutionResponse,
21
+ SignalWithStartWorkflowExecutionRequest,
22
+ SignalWithStartWorkflowExecutionResponse,
23
+ )
24
+ from cadence.api.v1.common_pb2 import WorkflowType, WorkflowExecution
25
+ from cadence.api.v1.tasklist_pb2 import TaskList
26
+ from cadence.data_converter import DataConverter, DefaultDataConverter
27
+ from cadence.metrics import MetricsEmitter, NoOpMetricsEmitter
28
+ from cadence.workflow import WorkflowDefinition
29
+
30
+
31
+ class StartWorkflowOptions(TypedDict, total=False):
32
+ """Options for starting a workflow execution."""
33
+
34
+ task_list: str
35
+ execution_start_to_close_timeout: timedelta
36
+ workflow_id: str
37
+ task_start_to_close_timeout: timedelta
38
+ cron_schedule: str
39
+
40
+
41
+ def _validate_and_apply_defaults(options: StartWorkflowOptions) -> StartWorkflowOptions:
42
+ """Validate required fields and apply defaults to StartWorkflowOptions."""
43
+ if not options.get("task_list"):
44
+ raise ValueError("task_list is required")
45
+
46
+ execution_timeout = options.get("execution_start_to_close_timeout")
47
+ if not execution_timeout:
48
+ raise ValueError("execution_start_to_close_timeout is required")
49
+ if execution_timeout <= timedelta(0):
50
+ raise ValueError("execution_start_to_close_timeout must be greater than 0")
51
+
52
+ # Apply default for task_start_to_close_timeout if not provided (matching Go/Java clients)
53
+ task_timeout = options.get("task_start_to_close_timeout")
54
+ if task_timeout is None:
55
+ options["task_start_to_close_timeout"] = timedelta(seconds=10)
56
+ elif task_timeout <= timedelta(0):
57
+ raise ValueError("task_start_to_close_timeout must be greater than 0")
58
+
59
+ return options
60
+
61
+
62
+ class ClientOptions(TypedDict, total=False):
63
+ domain: str
64
+ target: str
65
+ data_converter: DataConverter
66
+ identity: str
67
+ service_name: str
68
+ caller_name: str
69
+ channel_arguments: dict[str, Any]
70
+ credentials: ChannelCredentials | None
71
+ compression: Compression
72
+ metrics_emitter: MetricsEmitter
73
+ interceptors: list[ClientInterceptor]
74
+
75
+
76
+ _DEFAULT_OPTIONS: ClientOptions = {
77
+ "data_converter": DefaultDataConverter(),
78
+ "identity": f"{os.getpid()}@{socket.gethostname()}",
79
+ "service_name": "cadence-frontend",
80
+ "caller_name": "cadence-client",
81
+ "channel_arguments": {},
82
+ "credentials": None,
83
+ "compression": Compression.NoCompression,
84
+ "metrics_emitter": NoOpMetricsEmitter(),
85
+ "interceptors": [],
86
+ }
87
+
88
+
89
+ class Client:
90
+ def __init__(self, **kwargs: Unpack[ClientOptions]) -> None:
91
+ self._options = _validate_and_copy_defaults(ClientOptions(**kwargs))
92
+ self._channel = _create_channel(self._options)
93
+ self._worker_stub = WorkerAPIStub(self._channel)
94
+ self._domain_stub = DomainAPIStub(self._channel)
95
+ self._workflow_stub = WorkflowAPIStub(self._channel)
96
+
97
+ @property
98
+ def data_converter(self) -> DataConverter:
99
+ return self._options["data_converter"]
100
+
101
+ @property
102
+ def domain(self) -> str:
103
+ return self._options["domain"]
104
+
105
+ @property
106
+ def identity(self) -> str:
107
+ return self._options["identity"]
108
+
109
+ @property
110
+ def domain_stub(self) -> DomainAPIStub:
111
+ return self._domain_stub
112
+
113
+ @property
114
+ def worker_stub(self) -> WorkerAPIStub:
115
+ return self._worker_stub
116
+
117
+ @property
118
+ def workflow_stub(self) -> WorkflowAPIStub:
119
+ return self._workflow_stub
120
+
121
+ @property
122
+ def metrics_emitter(self) -> MetricsEmitter:
123
+ return self._options["metrics_emitter"]
124
+
125
+ async def ready(self) -> None:
126
+ await self._channel.channel_ready()
127
+
128
+ async def close(self) -> None:
129
+ await self._channel.close()
130
+
131
+ async def __aenter__(self) -> "Client":
132
+ return self
133
+
134
+ async def __aexit__(self, exc_type, exc_val, exc_tb) -> None:
135
+ await self.close()
136
+
137
+ def _build_start_workflow_request(
138
+ self,
139
+ workflow: Union[str, WorkflowDefinition],
140
+ args: tuple[Any, ...],
141
+ options: StartWorkflowOptions,
142
+ ) -> StartWorkflowExecutionRequest:
143
+ """Build a StartWorkflowExecutionRequest from parameters."""
144
+ # Generate workflow ID if not provided
145
+ workflow_id = options.get("workflow_id") or str(uuid.uuid4())
146
+
147
+ # Determine workflow type name
148
+ if isinstance(workflow, str):
149
+ workflow_type_name = workflow
150
+ else:
151
+ # For WorkflowDefinition, use the name property
152
+ workflow_type_name = workflow.name
153
+
154
+ # Encode input arguments
155
+ input_payload = None
156
+ if args:
157
+ try:
158
+ input_payload = self.data_converter.to_data(list(args))
159
+ except Exception as e:
160
+ raise ValueError(f"Failed to encode workflow arguments: {e}")
161
+
162
+ # Convert timedelta to protobuf Duration
163
+ execution_timeout = Duration()
164
+ execution_timeout.FromTimedelta(options["execution_start_to_close_timeout"])
165
+
166
+ task_timeout = Duration()
167
+ task_timeout.FromTimedelta(options["task_start_to_close_timeout"])
168
+
169
+ # Build the request
170
+ request = StartWorkflowExecutionRequest(
171
+ domain=self.domain,
172
+ workflow_id=workflow_id,
173
+ workflow_type=WorkflowType(name=workflow_type_name),
174
+ task_list=TaskList(name=options["task_list"]),
175
+ identity=self.identity,
176
+ request_id=str(uuid.uuid4()),
177
+ )
178
+
179
+ # Set required timeout fields
180
+ request.execution_start_to_close_timeout.CopyFrom(execution_timeout)
181
+ request.task_start_to_close_timeout.CopyFrom(task_timeout)
182
+
183
+ # Set optional fields
184
+ if input_payload:
185
+ request.input.CopyFrom(input_payload)
186
+ if options.get("cron_schedule"):
187
+ request.cron_schedule = options["cron_schedule"]
188
+
189
+ return request
190
+
191
+ async def start_workflow(
192
+ self,
193
+ workflow: Union[str, WorkflowDefinition],
194
+ *args,
195
+ **options_kwargs: Unpack[StartWorkflowOptions],
196
+ ) -> WorkflowExecution:
197
+ """
198
+ Start a workflow execution asynchronously.
199
+
200
+ Args:
201
+ workflow: WorkflowDefinition or workflow type name string
202
+ *args: Arguments to pass to the workflow
203
+ **options_kwargs: StartWorkflowOptions as keyword arguments
204
+
205
+ Returns:
206
+ WorkflowExecution with workflow_id and run_id
207
+
208
+ Raises:
209
+ ValueError: If required parameters are missing or invalid
210
+ Exception: If the gRPC call fails
211
+ """
212
+ # Convert kwargs to StartWorkflowOptions and validate
213
+ options = _validate_and_apply_defaults(StartWorkflowOptions(**options_kwargs))
214
+
215
+ # Build the gRPC request
216
+ request = self._build_start_workflow_request(workflow, args, options)
217
+
218
+ # Execute the gRPC call
219
+ try:
220
+ response: StartWorkflowExecutionResponse = (
221
+ await self.workflow_stub.StartWorkflowExecution(request)
222
+ )
223
+
224
+ # Emit metrics if available
225
+ if self.metrics_emitter:
226
+ # TODO: Add workflow start metrics similar to Go client
227
+ pass
228
+
229
+ execution = WorkflowExecution()
230
+ execution.workflow_id = request.workflow_id
231
+ execution.run_id = response.run_id
232
+ return execution
233
+ except Exception:
234
+ raise
235
+
236
+ async def signal_workflow(
237
+ self,
238
+ workflow_id: str,
239
+ run_id: str,
240
+ signal_name: str,
241
+ *signal_args: Any,
242
+ ) -> None:
243
+ """
244
+ Send a signal to a running workflow execution.
245
+
246
+ Args:
247
+ workflow_id: The workflow ID
248
+ run_id: The run ID (can be empty string to signal current run)
249
+ signal_name: Name of the signal
250
+ *signal_args: Arguments to pass to the signal handler
251
+
252
+ Raises:
253
+ ValueError: If signal encoding fails
254
+ Exception: If the gRPC call fails
255
+ """
256
+ signal_payload = None
257
+ if signal_args:
258
+ try:
259
+ signal_payload = self.data_converter.to_data(list[Any](signal_args))
260
+ except Exception as e:
261
+ raise ValueError(f"Failed to encode signal input: {e}")
262
+
263
+ workflow_execution = WorkflowExecution()
264
+ workflow_execution.workflow_id = workflow_id
265
+ if run_id:
266
+ workflow_execution.run_id = run_id
267
+
268
+ signal_request = SignalWorkflowExecutionRequest(
269
+ domain=self.domain,
270
+ workflow_execution=workflow_execution,
271
+ identity=self.identity,
272
+ request_id=str(uuid.uuid4()),
273
+ signal_name=signal_name,
274
+ )
275
+
276
+ if signal_payload:
277
+ signal_request.signal_input.CopyFrom(signal_payload)
278
+
279
+ await self.workflow_stub.SignalWorkflowExecution(signal_request)
280
+
281
+ async def signal_with_start_workflow(
282
+ self,
283
+ workflow: Union[str, WorkflowDefinition],
284
+ signal_name: str,
285
+ signal_args: list[Any],
286
+ *workflow_args: Any,
287
+ **options_kwargs: Unpack[StartWorkflowOptions],
288
+ ) -> WorkflowExecution:
289
+ """
290
+ Signal a workflow execution, starting it if it is not already running.
291
+
292
+ Args:
293
+ workflow: WorkflowDefinition or workflow type name string
294
+ signal_name: Name of the signal
295
+ signal_args: List of arguments to pass to the signal handler
296
+ *workflow_args: Arguments to pass to the workflow if it needs to be started
297
+ **options_kwargs: StartWorkflowOptions as keyword arguments
298
+
299
+ Returns:
300
+ WorkflowExecution with workflow_id and run_id
301
+
302
+ Raises:
303
+ ValueError: If required parameters are missing or invalid
304
+ Exception: If the gRPC call fails
305
+ """
306
+ # Convert kwargs to StartWorkflowOptions and validate
307
+ options = _validate_and_apply_defaults(StartWorkflowOptions(**options_kwargs))
308
+
309
+ # Build the start workflow request
310
+ start_request = self._build_start_workflow_request(
311
+ workflow, workflow_args, options
312
+ )
313
+
314
+ # Encode signal input
315
+ signal_payload = None
316
+ if signal_args:
317
+ try:
318
+ signal_payload = self.data_converter.to_data(signal_args)
319
+ except Exception as e:
320
+ raise ValueError(f"Failed to encode signal input: {e}")
321
+
322
+ # Build the SignalWithStartWorkflowExecution request
323
+ request = SignalWithStartWorkflowExecutionRequest(
324
+ start_request=start_request,
325
+ signal_name=signal_name,
326
+ )
327
+
328
+ if signal_payload:
329
+ request.signal_input.CopyFrom(signal_payload)
330
+
331
+ # Execute the gRPC call
332
+ try:
333
+ response: SignalWithStartWorkflowExecutionResponse = (
334
+ await self.workflow_stub.SignalWithStartWorkflowExecution(request)
335
+ )
336
+
337
+ execution = WorkflowExecution()
338
+ execution.workflow_id = start_request.workflow_id
339
+ execution.run_id = response.run_id
340
+ return execution
341
+ except Exception:
342
+ raise
343
+
344
+
345
+ def _validate_and_copy_defaults(options: ClientOptions) -> ClientOptions:
346
+ if "target" not in options:
347
+ raise ValueError("target must be specified")
348
+
349
+ if "domain" not in options:
350
+ raise ValueError("domain must be specified")
351
+
352
+ # Set default values for missing options
353
+ for key, value in _DEFAULT_OPTIONS.items():
354
+ if key not in options:
355
+ cast(dict, options)[key] = value
356
+
357
+ return options
358
+
359
+
360
+ def _create_channel(options: ClientOptions) -> Channel:
361
+ interceptors = list(options["interceptors"])
362
+ interceptors.append(
363
+ YarpcMetadataInterceptor(options["service_name"], options["caller_name"])
364
+ )
365
+ interceptors.append(RetryInterceptor())
366
+ interceptors.append(CadenceErrorInterceptor())
367
+
368
+ if options["credentials"]:
369
+ return secure_channel(
370
+ options["target"],
371
+ options["credentials"],
372
+ options["channel_arguments"],
373
+ options["compression"],
374
+ interceptors,
375
+ )
376
+ else:
377
+ return insecure_channel(
378
+ options["target"],
379
+ options["channel_arguments"],
380
+ options["compression"],
381
+ interceptors,
382
+ )
@@ -0,0 +1,78 @@
1
+ from abc import abstractmethod
2
+ from typing import Protocol, List, Type, Any
3
+
4
+ from cadence.api.v1.common_pb2 import Payload
5
+ from json import JSONDecoder
6
+ from msgspec import json, convert
7
+
8
+ _SPACE = " ".encode()
9
+
10
+
11
+ class DataConverter(Protocol):
12
+ @abstractmethod
13
+ def from_data(self, payload: Payload, type_hints: List[Type | None]) -> List[Any]:
14
+ raise NotImplementedError()
15
+
16
+ @abstractmethod
17
+ def to_data(self, values: List[Any]) -> Payload:
18
+ raise NotImplementedError()
19
+
20
+
21
+ class DefaultDataConverter(DataConverter):
22
+ def __init__(self) -> None:
23
+ self._encoder = json.Encoder()
24
+ # Need to use std lib decoder in order to decode the custom whitespace delimited data format
25
+ self._decoder = JSONDecoder(strict=False)
26
+
27
+ def from_data(self, payload: Payload, type_hints: List[Type | None]) -> List[Any]:
28
+ if not payload.data:
29
+ return DefaultDataConverter._convert_into([], type_hints)
30
+
31
+ payload_str = payload.data.decode()
32
+
33
+ return self._decode_whitespace_delimited(payload_str, type_hints)
34
+
35
+ def _decode_whitespace_delimited(
36
+ self, payload: str, type_hints: List[Type | None]
37
+ ) -> List[Any]:
38
+ results: List[Any] = []
39
+ start, end = 0, len(payload)
40
+ while start < end and len(results) < len(type_hints):
41
+ remaining = payload[start:end]
42
+ (value, value_end) = self._decoder.raw_decode(remaining)
43
+ start += value_end + 1
44
+ results.append(value)
45
+
46
+ return DefaultDataConverter._convert_into(results, type_hints)
47
+
48
+ @staticmethod
49
+ def _convert_into(values: List[Any], type_hints: List[Type | None]) -> List[Any]:
50
+ results: List[Any] = []
51
+ for i, type_hint in enumerate(type_hints):
52
+ if not type_hint:
53
+ value = values[i]
54
+ elif i < len(values):
55
+ value = convert(values[i], type_hint)
56
+ else:
57
+ value = DefaultDataConverter._get_default(type_hint)
58
+
59
+ results.append(value)
60
+
61
+ return results
62
+
63
+ @staticmethod
64
+ def _get_default(type_hint: Type) -> Any:
65
+ if type_hint in (int, float):
66
+ return 0
67
+ if type_hint is bool:
68
+ return False
69
+ return None
70
+
71
+ def to_data(self, values: List[Any]) -> Payload:
72
+ result = bytearray()
73
+ for index, value in enumerate(values):
74
+ self._encoder.encode_into(value, result, -1)
75
+ if index < len(values) - 1:
76
+ result += _SPACE
77
+
78
+ return Payload(data=bytes(result))
cadence/error.py ADDED
@@ -0,0 +1,111 @@
1
+ import grpc
2
+
3
+
4
+ class ActivityFailure(Exception):
5
+ def __init__(self, message: str) -> None:
6
+ super().__init__(message)
7
+
8
+
9
+ class WorkflowFailure(Exception):
10
+ def __init__(self, message: str) -> None:
11
+ super().__init__(message)
12
+
13
+
14
+ class CadenceRpcError(Exception):
15
+ def __init__(self, message: str, code: grpc.StatusCode, *args):
16
+ super().__init__(message, code, *args)
17
+ self.code = code
18
+
19
+
20
+ class WorkflowExecutionAlreadyStartedError(CadenceRpcError):
21
+ def __init__(
22
+ self, message: str, code: grpc.StatusCode, start_request_id: str, run_id: str
23
+ ) -> None:
24
+ super().__init__(message, code, start_request_id, run_id)
25
+ self.start_request_id = start_request_id
26
+ self.run_id = run_id
27
+
28
+
29
+ class EntityNotExistsError(CadenceRpcError):
30
+ def __init__(
31
+ self,
32
+ message: str,
33
+ code: grpc.StatusCode,
34
+ current_cluster: str,
35
+ active_cluster: str,
36
+ active_clusters: list[str],
37
+ ) -> None:
38
+ super().__init__(
39
+ message, code, current_cluster, active_cluster, active_clusters
40
+ )
41
+ self.current_cluster = current_cluster
42
+ self.active_cluster = active_cluster
43
+ self.active_clusters = active_clusters
44
+
45
+
46
+ class WorkflowExecutionAlreadyCompletedError(CadenceRpcError):
47
+ pass
48
+
49
+
50
+ class DomainNotActiveError(CadenceRpcError):
51
+ def __init__(
52
+ self,
53
+ message: str,
54
+ code: grpc.StatusCode,
55
+ domain: str,
56
+ current_cluster: str,
57
+ active_cluster: str,
58
+ active_clusters: list[str],
59
+ ) -> None:
60
+ super().__init__(
61
+ message, code, domain, current_cluster, active_cluster, active_clusters
62
+ )
63
+ self.domain = domain
64
+ self.current_cluster = current_cluster
65
+ self.active_cluster = active_cluster
66
+ self.active_clusters = active_clusters
67
+
68
+
69
+ class ClientVersionNotSupportedError(CadenceRpcError):
70
+ def __init__(
71
+ self,
72
+ message: str,
73
+ code: grpc.StatusCode,
74
+ feature_version: str,
75
+ client_impl: str,
76
+ supported_versions: str,
77
+ ) -> None:
78
+ super().__init__(
79
+ message, code, feature_version, client_impl, supported_versions
80
+ )
81
+ self.feature_version = feature_version
82
+ self.client_impl = client_impl
83
+ self.supported_versions = supported_versions
84
+
85
+
86
+ class FeatureNotEnabledError(CadenceRpcError):
87
+ def __init__(self, message: str, code: grpc.StatusCode, feature_flag: str) -> None:
88
+ super().__init__(message, code, feature_flag)
89
+ self.feature_flag = feature_flag
90
+
91
+
92
+ class CancellationAlreadyRequestedError(CadenceRpcError):
93
+ pass
94
+
95
+
96
+ class DomainAlreadyExistsError(CadenceRpcError):
97
+ pass
98
+
99
+
100
+ class LimitExceededError(CadenceRpcError):
101
+ pass
102
+
103
+
104
+ class QueryFailedError(CadenceRpcError):
105
+ pass
106
+
107
+
108
+ class ServiceBusyError(CadenceRpcError):
109
+ def __init__(self, message: str, code: grpc.StatusCode, reason: str) -> None:
110
+ super().__init__(message, code, reason)
111
+ self.reason = reason
@@ -0,0 +1,12 @@
1
+ """Metrics collection components for Cadence client."""
2
+
3
+ from .metrics import MetricsEmitter, NoOpMetricsEmitter, MetricType
4
+ from .prometheus import PrometheusMetrics, PrometheusConfig
5
+
6
+ __all__ = [
7
+ "MetricsEmitter",
8
+ "NoOpMetricsEmitter",
9
+ "MetricType",
10
+ "PrometheusMetrics",
11
+ "PrometheusConfig",
12
+ ]