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