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
@@ -627,3 +653,30 @@ def _add_to_open_tasks(self, task: TaskBase):
627
653
else :
628
654
for child in task .children :
629
655
self ._add_to_open_tasks (child )
656
+
657
+ def _get_function_name (self , name : FunctionBuilder ,
658
+ trigger_type : Union [OrchestrationTrigger , ActivityTrigger ]):
659
+ try :
660
+ if (isinstance (name ._function ._trigger , trigger_type )):
661
+ name = name ._function ._name
662
+ return name
663
+ else :
664
+ if (trigger_type == OrchestrationTrigger ):
665
+ trigger_type = "OrchestrationTrigger"
666
+ else :
667
+ trigger_type = "ActivityTrigger"
668
+ error_message = "Received function with Trigger-type `" \
669
+ + name ._function ._trigger .type \
670
+ + "` but expected `" + trigger_type + "`. Ensure your " \
671
+ "function is annotated with the `" + trigger_type + \
672
+ "` decorator or directly pass in the name of the " \
673
+ "function as a string."
674
+ raise ValueError (error_message )
675
+ except AttributeError as e :
676
+ e .message = "Durable Functions SDK internal error: an " \
677
+ "expected attribute is missing from the `FunctionBuilder` " \
678
+ "object in the Python V2 programming model. Please report " \
679
+ "this bug in the Durable Functions Python SDK repo: " \
680
+ "https://github.com/Azure/azure-functions-durable-python.\n " \
681
+ "Error trace: " + e .message
682
+ raise e
0 commit comments