2323import json
2424import datetime
2525import inspect
26- from typing import DefaultDict , List , Any , Dict , Optional , Tuple , Union
26+ from typing import DefaultDict , List , Any , Dict , Optional , Tuple , Union , Callable
2727from uuid import UUID , uuid5 , NAMESPACE_URL , NAMESPACE_OID
2828from datetime import timezone
2929
3535from .utils .entity_utils import EntityId
3636from azure .functions ._durable_functions import _deserialize_custom_object
3737from 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
3840
3941
4042class DurableOrchestrationContext :
@@ -144,13 +146,14 @@ def _set_is_replaying(self, is_replaying: bool):
144146 """
145147 self ._is_replaying = is_replaying
146148
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 :
148150 """Schedule an activity for execution.
149151
150152 Parameters
151153 ----------
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.
154157 input_: Optional[Any]
155158 The JSON-serializable input to pass to the activity function.
156159
@@ -159,19 +162,31 @@ def call_activity(self, name: str, input_: Optional[Any] = None) -> TaskBase:
159162 Task
160163 A Durable Task that completes when the called activity function completes or fails.
161164 """
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+
162176 action = CallActivityAction (name , input_ )
163177 task = self ._generate_task (action )
164178 return task
165179
166180 def call_activity_with_retry (self ,
167- name : str , retry_options : RetryOptions ,
181+ name : Union [ str , Callable ] , retry_options : RetryOptions ,
168182 input_ : Optional [Any ] = None ) -> TaskBase :
169183 """Schedule an activity for execution with retry options.
170184
171185 Parameters
172186 ----------
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.
175190 retry_options: RetryOptions
176191 The retry options for the activity function.
177192 input_: Optional[Any]
@@ -183,6 +198,17 @@ def call_activity_with_retry(self,
183198 A Durable Task that completes when the called activity function completes or
184199 fails completely.
185200 """
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+
186212 action = CallActivityWithRetryAction (name , retry_options , input_ )
187213 task = self ._generate_task (action , retry_options )
188214 return task
@@ -222,13 +248,13 @@ def call_http(self, method: str, uri: str, content: Optional[str] = None,
222248 return task
223249
224250 def call_sub_orchestrator (self ,
225- name : str , input_ : Optional [Any ] = None ,
251+ name : Union [ str , Callable ] , input_ : Optional [Any ] = None ,
226252 instance_id : Optional [str ] = None ) -> TaskBase :
227253 """Schedule sub-orchestration function named `name` for execution.
228254
229255 Parameters
230256 ----------
231- name: str
257+ name: Union[ str, Callable]
232258 The name of the orchestrator function to call.
233259 input_: Optional[Any]
234260 The JSON-serializable input to pass to the orchestrator function.
@@ -240,19 +266,30 @@ def call_sub_orchestrator(self,
240266 Task
241267 A Durable Task that completes when the called sub-orchestrator completes or fails.
242268 """
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+
243280 action = CallSubOrchestratorAction (name , input_ , instance_id )
244281 task = self ._generate_task (action )
245282 return task
246283
247284 def call_sub_orchestrator_with_retry (self ,
248- name : str , retry_options : RetryOptions ,
285+ name : Union [ str , Callable ] , retry_options : RetryOptions ,
249286 input_ : Optional [Any ] = None ,
250287 instance_id : Optional [str ] = None ) -> TaskBase :
251288 """Schedule sub-orchestration function named `name` for execution, with retry-options.
252289
253290 Parameters
254291 ----------
255- name: str
292+ name: Union[ str, Callable]
256293 The name of the activity function to schedule.
257294 retry_options: RetryOptions
258295 The settings for retrying this sub-orchestrator in case of a failure.
@@ -266,6 +303,17 @@ def call_sub_orchestrator_with_retry(self,
266303 Task
267304 A Durable Task that completes when the called sub-orchestrator completes or fails.
268305 """
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+
269317 action = CallSubOrchestratorWithRetryAction (name , retry_options , input_ , instance_id )
270318 task = self ._generate_task (action , retry_options )
271319 return task
@@ -628,3 +676,30 @@ def _add_to_open_tasks(self, task: TaskBase):
628676 else :
629677 for child in task .children :
630678 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