23
23
import json
24
24
import datetime
25
25
import inspect
26
- from typing import DefaultDict , List , Any , Dict , Optional , Tuple , Union
26
+ from typing import DefaultDict , List , Any , Dict , Optional , Tuple , Union , Callable
27
27
from uuid import UUID , uuid5 , NAMESPACE_URL , NAMESPACE_OID
28
28
from datetime import timezone
29
29
35
35
from .utils .entity_utils import EntityId
36
36
from azure .functions ._durable_functions import _deserialize_custom_object
37
37
from azure .durable_functions .constants import DATETIME_STRING_FORMAT
38
+ from azure .durable_functions .decorators .metadata import OrchestrationTrigger , ActivityTrigger
39
+ from azure .functions .decorators .function_app import FunctionBuilder
38
40
39
41
40
42
class DurableOrchestrationContext :
@@ -144,13 +146,14 @@ def _set_is_replaying(self, is_replaying: bool):
144
146
"""
145
147
self ._is_replaying = is_replaying
146
148
147
- def call_activity (self , name : str , input_ : Optional [Any ] = None ) -> TaskBase :
149
+ def call_activity (self , name : Union [ str , Callable ] , input_ : Optional [Any ] = None ) -> TaskBase :
148
150
"""Schedule an activity for execution.
149
151
150
152
Parameters
151
153
----------
152
- name: str
153
- The name of the activity function to call.
154
+ name: str | Callable
155
+ Either the name of the activity function to call, as a string or,
156
+ in the Python V2 programming model, the activity function itself.
154
157
input_: Optional[Any]
155
158
The JSON-serializable input to pass to the activity function.
156
159
@@ -159,19 +162,31 @@ def call_activity(self, name: str, input_: Optional[Any] = None) -> TaskBase:
159
162
Task
160
163
A Durable Task that completes when the called activity function completes or fails.
161
164
"""
165
+ if isinstance (name , Callable ) and not isinstance (name , FunctionBuilder ):
166
+ error_message = "The `call_activity` API received a `Callable` without an " \
167
+ "associated Azure Functions trigger-type. " \
168
+ "Please ensure you're using the Python programming model V2 " \
169
+ "and that your activity function is annotated with the `activity_trigger`" \
170
+ "decorator. Otherwise, provide in the name of the activity as a string."
171
+ raise ValueError (error_message )
172
+
173
+ if isinstance (name , FunctionBuilder ):
174
+ name = self ._get_function_name (name , ActivityTrigger )
175
+
162
176
action = CallActivityAction (name , input_ )
163
177
task = self ._generate_task (action )
164
178
return task
165
179
166
180
def call_activity_with_retry (self ,
167
- name : str , retry_options : RetryOptions ,
181
+ name : Union [ str , Callable ] , retry_options : RetryOptions ,
168
182
input_ : Optional [Any ] = None ) -> TaskBase :
169
183
"""Schedule an activity for execution with retry options.
170
184
171
185
Parameters
172
186
----------
173
- name: str
174
- The name of the activity function to call.
187
+ name: str | Callable
188
+ Either the name of the activity function to call, as a string or,
189
+ in the Python V2 programming model, the activity function itself.
175
190
retry_options: RetryOptions
176
191
The retry options for the activity function.
177
192
input_: Optional[Any]
@@ -183,6 +198,17 @@ def call_activity_with_retry(self,
183
198
A Durable Task that completes when the called activity function completes or
184
199
fails completely.
185
200
"""
201
+ if isinstance (name , Callable ) and not isinstance (name , FunctionBuilder ):
202
+ error_message = "The `call_activity` API received a `Callable` without an " \
203
+ "associated Azure Functions trigger-type. " \
204
+ "Please ensure you're using the Python programming model V2 " \
205
+ "and that your activity function is annotated with the `activity_trigger`" \
206
+ "decorator. Otherwise, provide in the name of the activity as a string."
207
+ raise ValueError (error_message )
208
+
209
+ if isinstance (name , FunctionBuilder ):
210
+ name = self ._get_function_name (name , ActivityTrigger )
211
+
186
212
action = CallActivityWithRetryAction (name , retry_options , input_ )
187
213
task = self ._generate_task (action , retry_options )
188
214
return task
@@ -222,13 +248,13 @@ def call_http(self, method: str, uri: str, content: Optional[str] = None,
222
248
return task
223
249
224
250
def call_sub_orchestrator (self ,
225
- name : str , input_ : Optional [Any ] = None ,
251
+ name : Union [ str , Callable ] , input_ : Optional [Any ] = None ,
226
252
instance_id : Optional [str ] = None ) -> TaskBase :
227
253
"""Schedule sub-orchestration function named `name` for execution.
228
254
229
255
Parameters
230
256
----------
231
- name: str
257
+ name: Union[ str, Callable]
232
258
The name of the orchestrator function to call.
233
259
input_: Optional[Any]
234
260
The JSON-serializable input to pass to the orchestrator function.
@@ -240,19 +266,30 @@ def call_sub_orchestrator(self,
240
266
Task
241
267
A Durable Task that completes when the called sub-orchestrator completes or fails.
242
268
"""
269
+ if isinstance (name , Callable ) and not isinstance (name , FunctionBuilder ):
270
+ error_message = "The `call_activity` API received a `Callable` without an " \
271
+ "associated Azure Functions trigger-type. " \
272
+ "Please ensure you're using the Python programming model V2 " \
273
+ "and that your activity function is annotated with the `activity_trigger`" \
274
+ "decorator. Otherwise, provide in the name of the activity as a string."
275
+ raise ValueError (error_message )
276
+
277
+ if isinstance (name , FunctionBuilder ):
278
+ name = self ._get_function_name (name , OrchestrationTrigger )
279
+
243
280
action = CallSubOrchestratorAction (name , input_ , instance_id )
244
281
task = self ._generate_task (action )
245
282
return task
246
283
247
284
def call_sub_orchestrator_with_retry (self ,
248
- name : str , retry_options : RetryOptions ,
285
+ name : Union [ str , Callable ] , retry_options : RetryOptions ,
249
286
input_ : Optional [Any ] = None ,
250
287
instance_id : Optional [str ] = None ) -> TaskBase :
251
288
"""Schedule sub-orchestration function named `name` for execution, with retry-options.
252
289
253
290
Parameters
254
291
----------
255
- name: str
292
+ name: Union[ str, Callable]
256
293
The name of the activity function to schedule.
257
294
retry_options: RetryOptions
258
295
The settings for retrying this sub-orchestrator in case of a failure.
@@ -266,6 +303,17 @@ def call_sub_orchestrator_with_retry(self,
266
303
Task
267
304
A Durable Task that completes when the called sub-orchestrator completes or fails.
268
305
"""
306
+ if isinstance (name , Callable ) and not isinstance (name , FunctionBuilder ):
307
+ error_message = "The `call_activity` API received a `Callable` without an " \
308
+ "associated Azure Functions trigger-type. " \
309
+ "Please ensure you're using the Python programming model V2 " \
310
+ "and that your activity function is annotated with the `activity_trigger`" \
311
+ "decorator. Otherwise, provide in the name of the activity as a string."
312
+ raise ValueError (error_message )
313
+
314
+ if isinstance (name , FunctionBuilder ):
315
+ name = self ._get_function_name (name , OrchestrationTrigger )
316
+
269
317
action = CallSubOrchestratorWithRetryAction (name , retry_options , input_ , instance_id )
270
318
task = self ._generate_task (action , retry_options )
271
319
return task
@@ -628,3 +676,30 @@ def _add_to_open_tasks(self, task: TaskBase):
628
676
else :
629
677
for child in task .children :
630
678
self ._add_to_open_tasks (child )
679
+
680
+ def _get_function_name (self , name : FunctionBuilder ,
681
+ trigger_type : Union [OrchestrationTrigger , ActivityTrigger ]):
682
+ try :
683
+ if (isinstance (name ._function ._trigger , trigger_type )):
684
+ name = name ._function ._name
685
+ return name
686
+ else :
687
+ if (trigger_type == OrchestrationTrigger ):
688
+ trigger_type = "OrchestrationTrigger"
689
+ else :
690
+ trigger_type = "ActivityTrigger"
691
+ error_message = "Received function with Trigger-type `" \
692
+ + name ._function ._trigger .type \
693
+ + "` but expected `" + trigger_type + "`. Ensure your " \
694
+ "function is annotated with the `" + trigger_type + \
695
+ "` decorator or directly pass in the name of the " \
696
+ "function as a string."
697
+ raise ValueError (error_message )
698
+ except AttributeError as e :
699
+ e .message = "Durable Functions SDK internal error: an " \
700
+ "expected attribute is missing from the `FunctionBuilder` " \
701
+ "object in the Python V2 programming model. Please report " \
702
+ "this bug in the Durable Functions Python SDK repo: " \
703
+ "https://github.com/Azure/azure-functions-durable-python.\n " \
704
+ "Error trace: " + e .message
705
+ raise e
0 commit comments