rappel 0.10.0__py3-none-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.

Potentially problematic release.


This version of rappel might be problematic. Click here for more details.

@@ -0,0 +1,460 @@
1
+ """
2
+ @generated by mypy-protobuf. Do not edit manually!
3
+ isort:skip_file
4
+ """
5
+
6
+ import abc
7
+ import collections.abc
8
+ import sys
9
+ import typing
10
+
11
+ import grpc
12
+ import grpc.aio
13
+
14
+ from proto import messages_pb2
15
+
16
+ if sys.version_info >= (3, 13):
17
+ import typing as typing_extensions
18
+ else:
19
+ import typing_extensions
20
+
21
+ _T = typing.TypeVar("_T")
22
+
23
+ class _MaybeAsyncIterator(
24
+ collections.abc.AsyncIterator[_T], collections.abc.Iterator[_T], metaclass=abc.ABCMeta
25
+ ): ...
26
+ class _ServicerContext(grpc.ServicerContext, grpc.aio.ServicerContext): # type: ignore[misc, type-arg]
27
+ ...
28
+
29
+ GRPC_GENERATED_VERSION: str
30
+ GRPC_VERSION: str
31
+ _WorkerBridgeAttachType = typing_extensions.TypeVar(
32
+ "_WorkerBridgeAttachType",
33
+ grpc.StreamStreamMultiCallable[
34
+ messages_pb2.Envelope,
35
+ messages_pb2.Envelope,
36
+ ],
37
+ grpc.aio.StreamStreamMultiCallable[
38
+ messages_pb2.Envelope,
39
+ messages_pb2.Envelope,
40
+ ],
41
+ default=grpc.StreamStreamMultiCallable[
42
+ messages_pb2.Envelope,
43
+ messages_pb2.Envelope,
44
+ ],
45
+ )
46
+
47
+ class WorkerBridgeStub(typing.Generic[_WorkerBridgeAttachType]):
48
+ """=============================================================================
49
+ gRPC Service Definitions
50
+ =============================================================================
51
+
52
+ Bidirectional streaming service for worker communication.
53
+ Workers connect and maintain a persistent stream for action dispatch/results.
54
+ """
55
+
56
+ @typing.overload
57
+ def __init__(
58
+ self: WorkerBridgeStub[
59
+ grpc.StreamStreamMultiCallable[
60
+ messages_pb2.Envelope,
61
+ messages_pb2.Envelope,
62
+ ],
63
+ ],
64
+ channel: grpc.Channel,
65
+ ) -> None: ...
66
+ @typing.overload
67
+ def __init__(
68
+ self: WorkerBridgeStub[
69
+ grpc.aio.StreamStreamMultiCallable[
70
+ messages_pb2.Envelope,
71
+ messages_pb2.Envelope,
72
+ ],
73
+ ],
74
+ channel: grpc.aio.Channel,
75
+ ) -> None: ...
76
+
77
+ Attach: _WorkerBridgeAttachType
78
+
79
+ WorkerBridgeAsyncStub: typing_extensions.TypeAlias = WorkerBridgeStub[
80
+ grpc.aio.StreamStreamMultiCallable[
81
+ messages_pb2.Envelope,
82
+ messages_pb2.Envelope,
83
+ ],
84
+ ]
85
+
86
+ class WorkerBridgeServicer(metaclass=abc.ABCMeta):
87
+ """=============================================================================
88
+ gRPC Service Definitions
89
+ =============================================================================
90
+
91
+ Bidirectional streaming service for worker communication.
92
+ Workers connect and maintain a persistent stream for action dispatch/results.
93
+ """
94
+
95
+ @abc.abstractmethod
96
+ def Attach(
97
+ self,
98
+ request_iterator: _MaybeAsyncIterator[messages_pb2.Envelope],
99
+ context: _ServicerContext,
100
+ ) -> typing.Union[
101
+ collections.abc.Iterator[messages_pb2.Envelope],
102
+ collections.abc.AsyncIterator[messages_pb2.Envelope],
103
+ ]: ...
104
+
105
+ def add_WorkerBridgeServicer_to_server(
106
+ servicer: WorkerBridgeServicer, server: typing.Union[grpc.Server, grpc.aio.Server]
107
+ ) -> None: ...
108
+
109
+ _WorkflowServiceRegisterWorkflowType = typing_extensions.TypeVar(
110
+ "_WorkflowServiceRegisterWorkflowType",
111
+ grpc.UnaryUnaryMultiCallable[
112
+ messages_pb2.RegisterWorkflowRequest,
113
+ messages_pb2.RegisterWorkflowResponse,
114
+ ],
115
+ grpc.aio.UnaryUnaryMultiCallable[
116
+ messages_pb2.RegisterWorkflowRequest,
117
+ messages_pb2.RegisterWorkflowResponse,
118
+ ],
119
+ default=grpc.UnaryUnaryMultiCallable[
120
+ messages_pb2.RegisterWorkflowRequest,
121
+ messages_pb2.RegisterWorkflowResponse,
122
+ ],
123
+ )
124
+
125
+ _WorkflowServiceRegisterWorkflowBatchType = typing_extensions.TypeVar(
126
+ "_WorkflowServiceRegisterWorkflowBatchType",
127
+ grpc.UnaryUnaryMultiCallable[
128
+ messages_pb2.RegisterWorkflowBatchRequest,
129
+ messages_pb2.RegisterWorkflowBatchResponse,
130
+ ],
131
+ grpc.aio.UnaryUnaryMultiCallable[
132
+ messages_pb2.RegisterWorkflowBatchRequest,
133
+ messages_pb2.RegisterWorkflowBatchResponse,
134
+ ],
135
+ default=grpc.UnaryUnaryMultiCallable[
136
+ messages_pb2.RegisterWorkflowBatchRequest,
137
+ messages_pb2.RegisterWorkflowBatchResponse,
138
+ ],
139
+ )
140
+
141
+ _WorkflowServiceWaitForInstanceType = typing_extensions.TypeVar(
142
+ "_WorkflowServiceWaitForInstanceType",
143
+ grpc.UnaryUnaryMultiCallable[
144
+ messages_pb2.WaitForInstanceRequest,
145
+ messages_pb2.WaitForInstanceResponse,
146
+ ],
147
+ grpc.aio.UnaryUnaryMultiCallable[
148
+ messages_pb2.WaitForInstanceRequest,
149
+ messages_pb2.WaitForInstanceResponse,
150
+ ],
151
+ default=grpc.UnaryUnaryMultiCallable[
152
+ messages_pb2.WaitForInstanceRequest,
153
+ messages_pb2.WaitForInstanceResponse,
154
+ ],
155
+ )
156
+
157
+ _WorkflowServiceExecuteWorkflowType = typing_extensions.TypeVar(
158
+ "_WorkflowServiceExecuteWorkflowType",
159
+ grpc.StreamStreamMultiCallable[
160
+ messages_pb2.WorkflowStreamRequest,
161
+ messages_pb2.WorkflowStreamResponse,
162
+ ],
163
+ grpc.aio.StreamStreamMultiCallable[
164
+ messages_pb2.WorkflowStreamRequest,
165
+ messages_pb2.WorkflowStreamResponse,
166
+ ],
167
+ default=grpc.StreamStreamMultiCallable[
168
+ messages_pb2.WorkflowStreamRequest,
169
+ messages_pb2.WorkflowStreamResponse,
170
+ ],
171
+ )
172
+
173
+ _WorkflowServiceRegisterScheduleType = typing_extensions.TypeVar(
174
+ "_WorkflowServiceRegisterScheduleType",
175
+ grpc.UnaryUnaryMultiCallable[
176
+ messages_pb2.RegisterScheduleRequest,
177
+ messages_pb2.RegisterScheduleResponse,
178
+ ],
179
+ grpc.aio.UnaryUnaryMultiCallable[
180
+ messages_pb2.RegisterScheduleRequest,
181
+ messages_pb2.RegisterScheduleResponse,
182
+ ],
183
+ default=grpc.UnaryUnaryMultiCallable[
184
+ messages_pb2.RegisterScheduleRequest,
185
+ messages_pb2.RegisterScheduleResponse,
186
+ ],
187
+ )
188
+
189
+ _WorkflowServiceUpdateScheduleStatusType = typing_extensions.TypeVar(
190
+ "_WorkflowServiceUpdateScheduleStatusType",
191
+ grpc.UnaryUnaryMultiCallable[
192
+ messages_pb2.UpdateScheduleStatusRequest,
193
+ messages_pb2.UpdateScheduleStatusResponse,
194
+ ],
195
+ grpc.aio.UnaryUnaryMultiCallable[
196
+ messages_pb2.UpdateScheduleStatusRequest,
197
+ messages_pb2.UpdateScheduleStatusResponse,
198
+ ],
199
+ default=grpc.UnaryUnaryMultiCallable[
200
+ messages_pb2.UpdateScheduleStatusRequest,
201
+ messages_pb2.UpdateScheduleStatusResponse,
202
+ ],
203
+ )
204
+
205
+ _WorkflowServiceDeleteScheduleType = typing_extensions.TypeVar(
206
+ "_WorkflowServiceDeleteScheduleType",
207
+ grpc.UnaryUnaryMultiCallable[
208
+ messages_pb2.DeleteScheduleRequest,
209
+ messages_pb2.DeleteScheduleResponse,
210
+ ],
211
+ grpc.aio.UnaryUnaryMultiCallable[
212
+ messages_pb2.DeleteScheduleRequest,
213
+ messages_pb2.DeleteScheduleResponse,
214
+ ],
215
+ default=grpc.UnaryUnaryMultiCallable[
216
+ messages_pb2.DeleteScheduleRequest,
217
+ messages_pb2.DeleteScheduleResponse,
218
+ ],
219
+ )
220
+
221
+ _WorkflowServiceListSchedulesType = typing_extensions.TypeVar(
222
+ "_WorkflowServiceListSchedulesType",
223
+ grpc.UnaryUnaryMultiCallable[
224
+ messages_pb2.ListSchedulesRequest,
225
+ messages_pb2.ListSchedulesResponse,
226
+ ],
227
+ grpc.aio.UnaryUnaryMultiCallable[
228
+ messages_pb2.ListSchedulesRequest,
229
+ messages_pb2.ListSchedulesResponse,
230
+ ],
231
+ default=grpc.UnaryUnaryMultiCallable[
232
+ messages_pb2.ListSchedulesRequest,
233
+ messages_pb2.ListSchedulesResponse,
234
+ ],
235
+ )
236
+
237
+ class WorkflowServiceStub(
238
+ typing.Generic[
239
+ _WorkflowServiceRegisterWorkflowType,
240
+ _WorkflowServiceRegisterWorkflowBatchType,
241
+ _WorkflowServiceWaitForInstanceType,
242
+ _WorkflowServiceExecuteWorkflowType,
243
+ _WorkflowServiceRegisterScheduleType,
244
+ _WorkflowServiceUpdateScheduleStatusType,
245
+ _WorkflowServiceDeleteScheduleType,
246
+ _WorkflowServiceListSchedulesType,
247
+ ]
248
+ ):
249
+ """Workflow management service for client operations."""
250
+
251
+ @typing.overload
252
+ def __init__(
253
+ self: WorkflowServiceStub[
254
+ grpc.UnaryUnaryMultiCallable[
255
+ messages_pb2.RegisterWorkflowRequest,
256
+ messages_pb2.RegisterWorkflowResponse,
257
+ ],
258
+ grpc.UnaryUnaryMultiCallable[
259
+ messages_pb2.RegisterWorkflowBatchRequest,
260
+ messages_pb2.RegisterWorkflowBatchResponse,
261
+ ],
262
+ grpc.UnaryUnaryMultiCallable[
263
+ messages_pb2.WaitForInstanceRequest,
264
+ messages_pb2.WaitForInstanceResponse,
265
+ ],
266
+ grpc.StreamStreamMultiCallable[
267
+ messages_pb2.WorkflowStreamRequest,
268
+ messages_pb2.WorkflowStreamResponse,
269
+ ],
270
+ grpc.UnaryUnaryMultiCallable[
271
+ messages_pb2.RegisterScheduleRequest,
272
+ messages_pb2.RegisterScheduleResponse,
273
+ ],
274
+ grpc.UnaryUnaryMultiCallable[
275
+ messages_pb2.UpdateScheduleStatusRequest,
276
+ messages_pb2.UpdateScheduleStatusResponse,
277
+ ],
278
+ grpc.UnaryUnaryMultiCallable[
279
+ messages_pb2.DeleteScheduleRequest,
280
+ messages_pb2.DeleteScheduleResponse,
281
+ ],
282
+ grpc.UnaryUnaryMultiCallable[
283
+ messages_pb2.ListSchedulesRequest,
284
+ messages_pb2.ListSchedulesResponse,
285
+ ],
286
+ ],
287
+ channel: grpc.Channel,
288
+ ) -> None: ...
289
+ @typing.overload
290
+ def __init__(
291
+ self: WorkflowServiceStub[
292
+ grpc.aio.UnaryUnaryMultiCallable[
293
+ messages_pb2.RegisterWorkflowRequest,
294
+ messages_pb2.RegisterWorkflowResponse,
295
+ ],
296
+ grpc.aio.UnaryUnaryMultiCallable[
297
+ messages_pb2.RegisterWorkflowBatchRequest,
298
+ messages_pb2.RegisterWorkflowBatchResponse,
299
+ ],
300
+ grpc.aio.UnaryUnaryMultiCallable[
301
+ messages_pb2.WaitForInstanceRequest,
302
+ messages_pb2.WaitForInstanceResponse,
303
+ ],
304
+ grpc.aio.StreamStreamMultiCallable[
305
+ messages_pb2.WorkflowStreamRequest,
306
+ messages_pb2.WorkflowStreamResponse,
307
+ ],
308
+ grpc.aio.UnaryUnaryMultiCallable[
309
+ messages_pb2.RegisterScheduleRequest,
310
+ messages_pb2.RegisterScheduleResponse,
311
+ ],
312
+ grpc.aio.UnaryUnaryMultiCallable[
313
+ messages_pb2.UpdateScheduleStatusRequest,
314
+ messages_pb2.UpdateScheduleStatusResponse,
315
+ ],
316
+ grpc.aio.UnaryUnaryMultiCallable[
317
+ messages_pb2.DeleteScheduleRequest,
318
+ messages_pb2.DeleteScheduleResponse,
319
+ ],
320
+ grpc.aio.UnaryUnaryMultiCallable[
321
+ messages_pb2.ListSchedulesRequest,
322
+ messages_pb2.ListSchedulesResponse,
323
+ ],
324
+ ],
325
+ channel: grpc.aio.Channel,
326
+ ) -> None: ...
327
+
328
+ RegisterWorkflow: _WorkflowServiceRegisterWorkflowType
329
+
330
+ RegisterWorkflowBatch: _WorkflowServiceRegisterWorkflowBatchType
331
+
332
+ WaitForInstance: _WorkflowServiceWaitForInstanceType
333
+
334
+ ExecuteWorkflow: _WorkflowServiceExecuteWorkflowType
335
+
336
+ RegisterSchedule: _WorkflowServiceRegisterScheduleType
337
+ """Schedule management"""
338
+
339
+ UpdateScheduleStatus: _WorkflowServiceUpdateScheduleStatusType
340
+
341
+ DeleteSchedule: _WorkflowServiceDeleteScheduleType
342
+
343
+ ListSchedules: _WorkflowServiceListSchedulesType
344
+
345
+ WorkflowServiceAsyncStub: typing_extensions.TypeAlias = WorkflowServiceStub[
346
+ grpc.aio.UnaryUnaryMultiCallable[
347
+ messages_pb2.RegisterWorkflowRequest,
348
+ messages_pb2.RegisterWorkflowResponse,
349
+ ],
350
+ grpc.aio.UnaryUnaryMultiCallable[
351
+ messages_pb2.RegisterWorkflowBatchRequest,
352
+ messages_pb2.RegisterWorkflowBatchResponse,
353
+ ],
354
+ grpc.aio.UnaryUnaryMultiCallable[
355
+ messages_pb2.WaitForInstanceRequest,
356
+ messages_pb2.WaitForInstanceResponse,
357
+ ],
358
+ grpc.aio.StreamStreamMultiCallable[
359
+ messages_pb2.WorkflowStreamRequest,
360
+ messages_pb2.WorkflowStreamResponse,
361
+ ],
362
+ grpc.aio.UnaryUnaryMultiCallable[
363
+ messages_pb2.RegisterScheduleRequest,
364
+ messages_pb2.RegisterScheduleResponse,
365
+ ],
366
+ grpc.aio.UnaryUnaryMultiCallable[
367
+ messages_pb2.UpdateScheduleStatusRequest,
368
+ messages_pb2.UpdateScheduleStatusResponse,
369
+ ],
370
+ grpc.aio.UnaryUnaryMultiCallable[
371
+ messages_pb2.DeleteScheduleRequest,
372
+ messages_pb2.DeleteScheduleResponse,
373
+ ],
374
+ grpc.aio.UnaryUnaryMultiCallable[
375
+ messages_pb2.ListSchedulesRequest,
376
+ messages_pb2.ListSchedulesResponse,
377
+ ],
378
+ ]
379
+
380
+ class WorkflowServiceServicer(metaclass=abc.ABCMeta):
381
+ """Workflow management service for client operations."""
382
+
383
+ @abc.abstractmethod
384
+ def RegisterWorkflow(
385
+ self,
386
+ request: messages_pb2.RegisterWorkflowRequest,
387
+ context: _ServicerContext,
388
+ ) -> typing.Union[
389
+ messages_pb2.RegisterWorkflowResponse,
390
+ collections.abc.Awaitable[messages_pb2.RegisterWorkflowResponse],
391
+ ]: ...
392
+ @abc.abstractmethod
393
+ def RegisterWorkflowBatch(
394
+ self,
395
+ request: messages_pb2.RegisterWorkflowBatchRequest,
396
+ context: _ServicerContext,
397
+ ) -> typing.Union[
398
+ messages_pb2.RegisterWorkflowBatchResponse,
399
+ collections.abc.Awaitable[messages_pb2.RegisterWorkflowBatchResponse],
400
+ ]: ...
401
+ @abc.abstractmethod
402
+ def WaitForInstance(
403
+ self,
404
+ request: messages_pb2.WaitForInstanceRequest,
405
+ context: _ServicerContext,
406
+ ) -> typing.Union[
407
+ messages_pb2.WaitForInstanceResponse,
408
+ collections.abc.Awaitable[messages_pb2.WaitForInstanceResponse],
409
+ ]: ...
410
+ @abc.abstractmethod
411
+ def ExecuteWorkflow(
412
+ self,
413
+ request_iterator: _MaybeAsyncIterator[messages_pb2.WorkflowStreamRequest],
414
+ context: _ServicerContext,
415
+ ) -> typing.Union[
416
+ collections.abc.Iterator[messages_pb2.WorkflowStreamResponse],
417
+ collections.abc.AsyncIterator[messages_pb2.WorkflowStreamResponse],
418
+ ]: ...
419
+ @abc.abstractmethod
420
+ def RegisterSchedule(
421
+ self,
422
+ request: messages_pb2.RegisterScheduleRequest,
423
+ context: _ServicerContext,
424
+ ) -> typing.Union[
425
+ messages_pb2.RegisterScheduleResponse,
426
+ collections.abc.Awaitable[messages_pb2.RegisterScheduleResponse],
427
+ ]:
428
+ """Schedule management"""
429
+
430
+ @abc.abstractmethod
431
+ def UpdateScheduleStatus(
432
+ self,
433
+ request: messages_pb2.UpdateScheduleStatusRequest,
434
+ context: _ServicerContext,
435
+ ) -> typing.Union[
436
+ messages_pb2.UpdateScheduleStatusResponse,
437
+ collections.abc.Awaitable[messages_pb2.UpdateScheduleStatusResponse],
438
+ ]: ...
439
+ @abc.abstractmethod
440
+ def DeleteSchedule(
441
+ self,
442
+ request: messages_pb2.DeleteScheduleRequest,
443
+ context: _ServicerContext,
444
+ ) -> typing.Union[
445
+ messages_pb2.DeleteScheduleResponse,
446
+ collections.abc.Awaitable[messages_pb2.DeleteScheduleResponse],
447
+ ]: ...
448
+ @abc.abstractmethod
449
+ def ListSchedules(
450
+ self,
451
+ request: messages_pb2.ListSchedulesRequest,
452
+ context: _ServicerContext,
453
+ ) -> typing.Union[
454
+ messages_pb2.ListSchedulesResponse,
455
+ collections.abc.Awaitable[messages_pb2.ListSchedulesResponse],
456
+ ]: ...
457
+
458
+ def add_WorkflowServiceServicer_to_server(
459
+ servicer: WorkflowServiceServicer, server: typing.Union[grpc.Server, grpc.aio.Server]
460
+ ) -> None: ...
rappel/__init__.py ADDED
@@ -0,0 +1,61 @@
1
+ """Public API for user-defined rappel actions."""
2
+
3
+ from . import bridge # noqa: F401
4
+ from . import workflow_runtime as _workflow_runtime # noqa: F401
5
+ from .actions import (
6
+ ActionResultPayload,
7
+ action,
8
+ deserialize_result_payload,
9
+ serialize_error_payload,
10
+ serialize_result_payload,
11
+ )
12
+ from .dependencies import Depend, provide_dependencies
13
+ from .exceptions import (
14
+ ExhaustedRetries,
15
+ ExhaustedRetriesError,
16
+ ScheduleAlreadyExistsError,
17
+ )
18
+ from .ir_builder import UnsupportedPatternError, build_workflow_ir
19
+ from .registry import registry
20
+ from .schedule import (
21
+ ScheduleInfo,
22
+ delete_schedule,
23
+ list_schedules,
24
+ pause_schedule,
25
+ resume_schedule,
26
+ schedule_workflow,
27
+ )
28
+ from .workflow import (
29
+ RetryPolicy,
30
+ Workflow,
31
+ workflow,
32
+ workflow_registry,
33
+ )
34
+
35
+ __all__ = [
36
+ "action",
37
+ "registry",
38
+ "ActionResultPayload",
39
+ "Workflow",
40
+ "workflow",
41
+ "workflow_registry",
42
+ "RetryPolicy",
43
+ "build_workflow_ir",
44
+ "serialize_result_payload",
45
+ "deserialize_result_payload",
46
+ "serialize_error_payload",
47
+ "Depend",
48
+ "provide_dependencies",
49
+ "bridge",
50
+ "ExhaustedRetries",
51
+ "ExhaustedRetriesError",
52
+ "ScheduleAlreadyExistsError",
53
+ "UnsupportedPatternError",
54
+ # Schedule functions
55
+ "schedule_workflow",
56
+ "pause_schedule",
57
+ "resume_schedule",
58
+ "delete_schedule",
59
+ "list_schedules",
60
+ "ScheduleInfo",
61
+ ]
rappel/actions.py ADDED
@@ -0,0 +1,108 @@
1
+ import inspect
2
+ from dataclasses import dataclass
3
+ from functools import wraps
4
+ from typing import Any, Callable, Optional, TypeVar, overload
5
+
6
+ from proto import messages_pb2 as pb2
7
+
8
+ from .dependencies import provide_dependencies
9
+ from .registry import AsyncAction, registry
10
+ from .serialization import dumps, loads
11
+
12
+ TAsync = TypeVar("TAsync", bound=AsyncAction)
13
+
14
+
15
+ @dataclass
16
+ class ActionResultPayload:
17
+ result: Any | None
18
+ error: dict[str, Any] | None
19
+
20
+
21
+ def serialize_result_payload(value: Any) -> pb2.WorkflowArguments:
22
+ """Serialize a successful action result."""
23
+ arguments = pb2.WorkflowArguments()
24
+ entry = arguments.arguments.add()
25
+ entry.key = "result"
26
+ entry.value.CopyFrom(dumps(value))
27
+ return arguments
28
+
29
+
30
+ def serialize_error_payload(_action: str, exc: BaseException) -> pb2.WorkflowArguments:
31
+ """Serialize an error raised during action execution."""
32
+ arguments = pb2.WorkflowArguments()
33
+ entry = arguments.arguments.add()
34
+ entry.key = "error"
35
+ entry.value.CopyFrom(dumps(exc))
36
+ return arguments
37
+
38
+
39
+ def deserialize_result_payload(payload: pb2.WorkflowArguments | None) -> ActionResultPayload:
40
+ """Deserialize WorkflowArguments produced by serialize_result_payload/error."""
41
+ if payload is None:
42
+ return ActionResultPayload(result=None, error=None)
43
+ values = {entry.key: entry.value for entry in payload.arguments}
44
+ if "error" in values:
45
+ error_value = values["error"]
46
+ data = loads(error_value)
47
+ if not isinstance(data, dict):
48
+ raise ValueError("error payload must deserialize to a mapping")
49
+ return ActionResultPayload(result=None, error=data)
50
+ result_value = values.get("result")
51
+ if result_value is None:
52
+ raise ValueError("result payload missing 'result' field")
53
+ return ActionResultPayload(result=loads(result_value), error=None)
54
+
55
+
56
+ @overload
57
+ def action(func: TAsync, /) -> TAsync: ...
58
+
59
+
60
+ @overload
61
+ def action(*, name: Optional[str] = None) -> Callable[[TAsync], TAsync]: ...
62
+
63
+
64
+ def action(
65
+ func: Optional[TAsync] = None,
66
+ *,
67
+ name: Optional[str] = None,
68
+ ) -> Callable[[TAsync], TAsync] | TAsync:
69
+ """Decorator for registering async actions.
70
+
71
+ Actions decorated with @action will automatically resolve Depend() markers
72
+ when called directly (e.g., during pytest runs where workflows bypass the
73
+ gRPC bridge).
74
+ """
75
+
76
+ def decorator(target: TAsync) -> TAsync:
77
+ if not inspect.iscoroutinefunction(target):
78
+ raise TypeError(f"action '{target.__name__}' must be defined with 'async def'")
79
+ action_name = name or target.__name__
80
+ action_module = target.__module__
81
+
82
+ @wraps(target)
83
+ async def wrapper(*args: Any, **kwargs: Any) -> Any:
84
+ # Convert positional args to kwargs based on the signature
85
+ sig = inspect.signature(target)
86
+ params = list(sig.parameters.keys())
87
+ for i, arg in enumerate(args):
88
+ if i < len(params):
89
+ kwargs[params[i]] = arg
90
+
91
+ # Resolve dependencies using the same mechanism as execute_action
92
+ async with provide_dependencies(target, kwargs) as call_kwargs:
93
+ return await target(**call_kwargs)
94
+
95
+ # Copy over the original function's attributes for introspection
96
+ wrapper.__wrapped__ = target # type: ignore[attr-defined]
97
+ wrapper.__rappel_action_name__ = action_name # type: ignore[attr-defined]
98
+ wrapper.__rappel_action_module__ = action_module # type: ignore[attr-defined]
99
+
100
+ # Register the original function (not the wrapper) so execute_action
101
+ # doesn't double-resolve dependencies
102
+ registry.register(action_module, action_name, target)
103
+
104
+ return wrapper # type: ignore[return-value]
105
+
106
+ if func is not None:
107
+ return decorator(func)
108
+ return decorator
Binary file
Binary file
Binary file