2222import json
2323import datetime
2424import inspect
25- from typing import DefaultDict , List , Any , Dict , Optional , Tuple , Union
25+ from typing import DefaultDict , List , Any , Dict , Optional , Tuple , Union , Callable
2626from uuid import UUID , uuid5 , NAMESPACE_URL , NAMESPACE_OID
2727from datetime import timezone
2828
3434from .utils .entity_utils import EntityId
3535from azure .functions ._durable_functions import _deserialize_custom_object
3636from 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
3739
3840
3941class DurableOrchestrationContext :
@@ -143,13 +145,14 @@ def _set_is_replaying(self, is_replaying: bool):
143145 """
144146 self ._is_replaying = is_replaying
145147
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 :
147149 """Schedule an activity for execution.
148150
149151 Parameters
150152 ----------
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.
153156 input_: Optional[Any]
154157 The JSON-serializable input to pass to the activity function.
155158
@@ -158,19 +161,31 @@ def call_activity(self, name: str, input_: Optional[Any] = None) -> TaskBase:
158161 Task
159162 A Durable Task that completes when the called activity function completes or fails.
160163 """
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+
161175 action = CallActivityAction (name , input_ )
162176 task = self ._generate_task (action )
163177 return task
164178
165179 def call_activity_with_retry (self ,
166- name : str , retry_options : RetryOptions ,
180+ name : Union [ str , Callable ] , retry_options : RetryOptions ,
167181 input_ : Optional [Any ] = None ) -> TaskBase :
168182 """Schedule an activity for execution with retry options.
169183
170184 Parameters
171185 ----------
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.
174189 retry_options: RetryOptions
175190 The retry options for the activity function.
176191 input_: Optional[Any]
@@ -182,6 +197,17 @@ def call_activity_with_retry(self,
182197 A Durable Task that completes when the called activity function completes or
183198 fails completely.
184199 """
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+
185211 action = CallActivityWithRetryAction (name , retry_options , input_ )
186212 task = self ._generate_task (action , retry_options )
187213 return task
@@ -221,13 +247,13 @@ def call_http(self, method: str, uri: str, content: Optional[str] = None,
221247 return task
222248
223249 def call_sub_orchestrator (self ,
224- name : str , input_ : Optional [Any ] = None ,
250+ name : Union [ str , Callable ] , input_ : Optional [Any ] = None ,
225251 instance_id : Optional [str ] = None ) -> TaskBase :
226252 """Schedule sub-orchestration function named `name` for execution.
227253
228254 Parameters
229255 ----------
230- name: str
256+ name: Union[ str, Callable]
231257 The name of the orchestrator function to call.
232258 input_: Optional[Any]
233259 The JSON-serializable input to pass to the orchestrator function.
@@ -239,19 +265,30 @@ def call_sub_orchestrator(self,
239265 Task
240266 A Durable Task that completes when the called sub-orchestrator completes or fails.
241267 """
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+
242279 action = CallSubOrchestratorAction (name , input_ , instance_id )
243280 task = self ._generate_task (action )
244281 return task
245282
246283 def call_sub_orchestrator_with_retry (self ,
247- name : str , retry_options : RetryOptions ,
284+ name : Union [ str , Callable ] , retry_options : RetryOptions ,
248285 input_ : Optional [Any ] = None ,
249286 instance_id : Optional [str ] = None ) -> TaskBase :
250287 """Schedule sub-orchestration function named `name` for execution, with retry-options.
251288
252289 Parameters
253290 ----------
254- name: str
291+ name: Union[ str, Callable]
255292 The name of the activity function to schedule.
256293 retry_options: RetryOptions
257294 The settings for retrying this sub-orchestrator in case of a failure.
@@ -265,6 +302,17 @@ def call_sub_orchestrator_with_retry(self,
265302 Task
266303 A Durable Task that completes when the called sub-orchestrator completes or fails.
267304 """
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+
268316 action = CallSubOrchestratorWithRetryAction (name , retry_options , input_ , instance_id )
269317 task = self ._generate_task (action , retry_options )
270318 return task
@@ -627,3 +675,30 @@ def _add_to_open_tasks(self, task: TaskBase):
627675 else :
628676 for child in task .children :
629677 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