Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
Use Durable Functions, a feature of Azure Functions, to write stateful serverless workflows in Python. In this quickstart, you clone and run a sample app that demonstrates two common orchestration patterns:
- Function chaining: Calls activities sequentially (Tokyo → Seattle → London).
- Fan-out/fan-in: Calls activities in parallel across five cities, then aggregates the results.
By the end, you'll have both orchestrations running locally with the Durable Task Scheduler emulator and be able to view their status in the dashboard.
- Clone and prepare the Hello Cities sample project.
- Set up the Durable Task Scheduler emulator and Azurite for local development.
- Run the function app and trigger both orchestrations.
- Review orchestration status and output in the Durable Task Scheduler dashboard.
Prerequisites
- Python 3.9+ installed.
- Azure Functions Core Tools v4 or later.
- Docker for running the emulator and Azurite.
- Clone the Durable Task Scheduler GitHub repository to use the quickstart sample.
Set up the Durable Task Scheduler emulator
The Durable Task Scheduler emulator provides a local development environment so you can test orchestrations without an Azure subscription. The Functions host also requires Azurite for local storage.
Start both containers:
docker run -d --name dtsemulator -p 8080:8080 -p 8082:8082 \
mcr.microsoft.com/dts/dts-emulator:latest
docker run -d --name azurite -p 10000:10000 -p 10001:10001 -p 10002:10002 \
mcr.microsoft.com/azure-storage/azurite
Tip
Once the emulator is running, you can access the Durable Task Scheduler dashboard at http://localhost:8082 to monitor orchestrations.
Run the quickstart sample
Navigate to the Hello Cities sample directory:
cd samples/durable-functions/python/hello-citiesCreate a virtual environment and install dependencies:
python -m venv .venv .venv\Scripts\activate pip install -r requirements.txtVerify that the
local.settings.jsonfile contains the following configuration:{ "IsEncrypted": false, "Values": { "AzureWebJobsStorage": "UseDevelopmentStorage=true", "FUNCTIONS_WORKER_RUNTIME": "python", "DURABLE_TASK_SCHEDULER_CONNECTION_STRING": "Endpoint=http://localhost:8080;TaskHub=default;Authentication=None" } }Start the function app:
func startIn a separate terminal, trigger the function chaining orchestration:
$response = Invoke-RestMethod -Method POST -Uri http://localhost:7071/api/StartChaining $responseThe response contains status URLs for the orchestration instance. Copy the
statusQueryGetUrivalue and run it to check the result:Invoke-RestMethod -Uri $response.statusQueryGetUriTrigger the fan-out/fan-in orchestration:
$response = Invoke-RestMethod -Method POST -Uri http://localhost:7071/api/StartFanOutFanIn Invoke-RestMethod -Uri $response.statusQueryGetUri
Expected output
The POST request returns a JSON response with status URLs. For example:
{
"id": "<instanceId>",
"statusQueryGetUri": "http://localhost:7071/runtime/webhooks/durabletask/instances/<instanceId>?code=...",
"sendEventPostUri": "...",
"terminatePostUri": "...",
"purgeHistoryDeleteUri": "..."
}
When you query statusQueryGetUri and the orchestration's runtimeStatus is Completed, you can find the greeting results in the output field. The chaining orchestration returns:
{
"name": "chaining_orchestration",
"runtimeStatus": "Completed",
"output": ["Hello Tokyo!", "Hello Seattle!", "Hello London!"]
}
The fan-out/fan-in orchestration returns:
{
"name": "fan_out_fan_in_orchestration",
"runtimeStatus": "Completed",
"output": ["Hello Tokyo!", "Hello Seattle!", "Hello London!", "Hello Paris!", "Hello Berlin!"]
}
Tip
If runtimeStatus shows Running or Pending, wait a moment and query the statusQueryGetUri again.
Open the Durable Task Scheduler dashboard at http://localhost:8082 to view the orchestration status and execution history.
Understand the code
The sample uses the Python v2 programming model with decorators, where all functions are defined in a single file (function_app.py).
Activity function
The say_hello activity takes a city name and returns a greeting:
@app.activity_trigger(input_name="city")
def say_hello(city: str) -> str:
"""Activity function that returns a greeting for a city."""
logging.info(f"Saying hello to {city}.")
return f"Hello {city}!"
Orchestrator functions
The chaining orchestrator calls say_hello sequentially for three cities:
@app.orchestration_trigger(context_name="context")
def chaining_orchestration(context: df.DurableOrchestrationContext):
"""Function chaining orchestration: calls activities sequentially."""
result1 = yield context.call_activity("say_hello", "Tokyo")
result2 = yield context.call_activity("say_hello", "Seattle")
result3 = yield context.call_activity("say_hello", "London")
return [result1, result2, result3]
The fan-out/fan-in orchestrator schedules activities in parallel:
@app.orchestration_trigger(context_name="context")
def fan_out_fan_in_orchestration(context: df.DurableOrchestrationContext):
"""Fan-out/Fan-in orchestration: calls activities in parallel."""
cities = ["Tokyo", "Seattle", "London", "Paris", "Berlin"]
# Fan-out: schedule all activities in parallel
parallel_tasks = []
for city in cities:
task = context.call_activity("say_hello", city)
parallel_tasks.append(task)
# Fan-in: wait for all to complete
results = yield context.task_all(parallel_tasks)
return results
Client functions
HTTP-triggered client functions start each orchestration. For example, the chaining starter:
@app.route(route="StartChaining", methods=["POST"])
@app.durable_client_input(client_name="client")
async def start_chaining(req: func.HttpRequest, client) -> func.HttpResponse:
"""HTTP trigger to start the function chaining orchestration."""
instance_id = await client.start_new("chaining_orchestration")
logging.info(f"Started chaining orchestration with ID = '{instance_id}'.")
return client.create_check_status_response(req, instance_id)
Configuration
The sample uses the Durable Task Scheduler emulator as its storage backend. This is configured in host.json:
{
"version": "2.0",
"logging": {
"logLevel": {
"DurableTask.Core": "Warning"
}
},
"extensions": {
"durableTask": {
"hubName": "default",
"storageProvider": {
"type": "azureManaged",
"connectionStringName": "DURABLE_TASK_SCHEDULER_CONNECTION_STRING"
}
}
},
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle",
"version": "[4.*, 5.0.0)"
}
}
Clean up resources
Stop the emulator containers when you're done:
docker stop dtsemulator azurite && docker rm dtsemulator azurite
To deactivate the Python virtual environment:
deactivate
Next steps
- Learn about common Durable Functions app patterns.
- Learn about Durable Functions storage providers.