Durable Functions for Python
Durable Functions is an extension of Azure Functions that enables developers to write stateful functions in a serverless environment. It allows defining stateful workflows using orchestrator functions and stateful entities with entity functions. The extension automatically manages state, checkpoints, and restarts, abstracting away complex state management concerns. The current stable version is 1.5.0, with ongoing active development and regular releases.
Warnings
- gotcha Orchestrator functions MUST be deterministic. Avoid using non-deterministic APIs like `datetime.now()`, `datetime.utcnow()`, `random.*`, static variables, or environment variables directly within orchestrators. Instead, use `context.current_utc_datetime` for time or pass non-deterministic values via activity function results or as inputs to the orchestrator. Violating this can lead to `NonDeterministicOrchestrationException` due to replay mismatches.
- breaking Version 1.2.7 of `azure-functions-durable` was yanked from PyPI due to a critical startup error. Installing or running this specific version will lead to failures.
- gotcha The Python version requirement has been updated. Older versions (e.g., Python 3.6) are no longer supported. The library now requires Python 3.9 or higher.
- gotcha Local development of Durable Functions requires a storage emulator (like Azurite) or a connection to an Azure Storage account. Without it, functions will fail to start or store state.
Install
-
pip install azure-functions-durable
Imports
- DurableOrchestrationContext
from azure.durable_functions import DurableOrchestrationContext
- DurableOrchestrationClient
from azure.durable_functions import DurableOrchestrationClient
- Blueprint
from azure.durable_functions import Blueprint
- orchestration_trigger
from azure.durable_functions import orchestration_trigger
- activity_trigger
from azure.durable_functions import activity_trigger
- durable_client_input
from azure.durable_functions import durable_client_input
Quickstart
import logging
import os
import azure.functions as func
import azure.durable_functions as df
# Instantiate a Durable Functions Blueprint
bp = df.Blueprint()
# An HTTP-triggered function that starts an instance of the orchestration
@bp.route(route="startOrchestrator")
@bp.durable_client_input(client_name="client")
async def start_orchestrator(req: func.HttpRequest, client: df.DurableOrchestrationClient):
orchestration_id = await client.start_new("my_orchestrator", None, "World")
logging.info(f"Started orchestration with ID = '{orchestration_id}'.")
return client.create_check_status_response(req, orchestration_id)
# The orchestrator function, which orchestrates calls to other functions
@bp.orchestration_trigger(context_name="context")
def my_orchestrator(context: df.DurableOrchestrationContext):
# The orchestrator is deterministic, so use context.call_activity for side-effects
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]
# An activity function, which performs the actual work
@bp.activity_trigger(input_name="city")
def say_hello(city: str) -> str:
logging.info(f"Saying hello to {city}.")
return f"Hello {city}!"
# In your main function_app.py, register the blueprint:
# import azure.functions as func
# from your_module_name import bp
# app = func.FunctionApp()
# app.register_functions(bp)