16

My orchestrator receives a payload, with that payload it contains instructions that need to be passed along with other sets of data to activity functions.

how do I pass multiple parameters to an activity function? Or do I have to mash all my data together?

def orchestrator_function(context: df.DurableOrchestrationContext):
    
    # User defined configuration
    instructions: str = context.get_input()

    task_batch = yield context.call_activity("get_tasks", None)
    
    # Need to pass in instructions too
    parallel_tasks = [context.call_activity("perform_task", task) for task in task_batch]

    results = yield context.task_all(parallel_tasks)
    
    return results

The perform_task activity needs both the items from task_batch and the user input instructions

Do I do something in my function.json?

Workaround Not ideal, but I can pass multiple parameters as a single Tuple

something = yield context.call_activity("activity", ("param_1", "param_2"))

I then just need to reference the correct index of the parameter in the activity.

5 Answers 5

18

Seems there's no text-book way to do it. I have opted to give my single parameter a generic name like parameter or payload.

Then when passing in the value in the orchestrator I do it like so:

payload = {"value_1": some_var, "value_2": another_var}
something = yield context.call_activity("activity", payload)

then within the activity function, I unpack it again.

edit: Some buried documentation seems to show that https://learn.microsoft.com/en-us/azure/azure-functions/durable/durable-functions-error-handling?tabs=python

Sign up to request clarification or add additional context in comments.

3 Comments

Fantastic spot!
how to unpack the params in the activity function?
@Pingpong it's just a dictionary, so in my example I would do value = payload["value_1"] and payload gets passed in to the activity as parameter.
11

Just to add to @Ari's great answer, here code to pass data from client function (HTTP request in this case) all the way to activity function:

Client -> Orchestrator -> Activity

Client

async def main(req: func.HttpRequest, starter: str) -> func.HttpResponse:
    client = df.DurableOrchestrationClient(starter)

    req_data = req.get_json()
    img_url = req_data['img_url']
    payload = {"img_url": img_url}
    
    instance_id = await client.start_new(req.route_params["functionName"], None, payload)

    logging.info(f"Started orchestration with ID = '{instance_id}'.")

    return client.create_check_status_response(req, instance_id)

Orchestrator

def orchestrator_function(context: df.DurableOrchestrationContext):
    input_context = context.get_input()
    img_url = input_context.get('img_url')

    some_response= yield context.call_activity('MyActivity', img_url)
    
    return [some_response]

Activity

def main(imgUrl: str) -> str:
    print(f'.... Image URL = {imgUrl}')

    return imgUrl

Comments

2

You can use @dataclass and @dataclass_json class decorators for your input and output types, like this:

@dataclass_json
@dataclass
class Command:
  param1: str
  param2: int


@dataclass_json
@dataclass
class Result:
  val1: str
  val2: int

and then you can use those in Azure Functions, e.g. in Activity ones:

def main(input: DownloadFileRequest) -> DownloadFileResponse:
  # function code
  result: DownloadFileResponse = DownloadFileResponse("some", 123)
  return result

This provides you with a clean API and descriptive code. Much better approach than using dictionaries, at least for me.

Comments

2

I would also suggest the dataclass-wizard as a viable option, and one which should also be a bit more lightweight alternative to dataclasses-json; it is lighter in the sense that it does not use external libraries like marshmallow for generating schemas. It also performs a little better FWIW anyway.

I didn't really understand the schema of the data as outlined in the question unfortunately, thus I decided to roll my own for the purposes of a quick and dirty demo, so check it out:

from dataclasses import dataclass
from dataclass_wizard import JSONWizard

@dataclass
class Batman(JSONWizard):
  suit_color: str
  side: 'Sidekick'


@dataclass
class Sidekick:
  name: str
  mood: str

# usage
bats = Batman.from_dict({'suitColor': 'BLACK',
                         'side': {'Name': 'Robin', 'mood': 'Mildly depressed'}})

print(repr(bats)) 
# prints:
#   Batman(suit_color='BLACK', side=Sidekick(name='Robin', mood='Mildly depressed'))
print(bats.to_dict())
# prints:
#   {'suitColor': 'BLACK', 'side': {'name': 'Robin', 'mood': 'Mildly depressed'}}

Comments

0

I have never used Azure functions in Python, only in C#, but I am assuming that in addition to passing the values as a dict, you could also pass them as a tuple that can be restructured within the function.

1 Comment

As of function runtime 4.34, this will work as long as you don't use type hints with the parameter.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.