Add azure-functions-durable v2: a durabletask-based rewrite of Durable Functions for Python#155
Conversation
- Still needs eventSent and eventRecieved implementations
- Add new_uuid method to OrchestrationContext for deterministic replay-safe UUIDs - Fix entity locking behavior for Functions - Align _RuntimeOrchestrationContext param names with OrchestrationContext - Remap __init__.py files for new module - Update version to 0.0.1dev0 - Add docstrings to missing methods - Move code for executing orchestrators/entities to DurableFunctionsWorker - Add function metadata to triggers for detection by extension
…ions-support # Conflicts: # durabletask/client.py # durabletask/internal/shared.py # durabletask/worker.py # pyproject.toml
andystaples
left a comment
There was a problem hiding this comment.
Review summary
Nice work — this mirrors the JS provider (microsoft/durabletask-js#282) closely and the structure is sound. One robustness-parity note surfaced from comparing the two, inline below.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 89 out of 92 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
azure-functions-durable/azure/durable_functions/orchestrator.py:33
self.durable_contextis assigned inhandle()but never declared on the class. With the azure-functions-durable pyright config set tostrict, this will be reported as an unknown attribute (and it also makes the public attribute surface unclear).
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 89 out of 92 changed files in this pull request and generated 7 comments.
Comments suppressed due to low confidence (1)
azure-functions-durable/CHANGELOG.md:10
- Changelog entries are expected under an
## Unreleasedheading so future user-facing changes have a stable place to land before a release is cut. This file starts directly at## 2.0.0b1, which will make the next set of changes harder to track consistently.
## 2.0.0b1
First preview (beta) release of `azure-functions-durable` 2.x — a ground-up
andystaples
left a comment
There was a problem hiding this comment.
Consolidated review — azure-functions-durable v2 (2.0.0b1)
Reviewed at 6baa78a. Re-ran the unit suite (158 passed) and verified every item against the current code and the rewritten CHANGELOG. The rewrite is solid and v1 compat coverage is broad — but one blocker remains, and a couple of packaging/CI gaps should close before the b1 is public.
Resolved since my first pass (thanks):
- ✅ Explicit
set_result(None)now wins over the return value (_UNSETsentinel + test). - ✅ Status payloads (
input_/output/custom_status) now return raw JSON with a graceful non-JSON fallback for failed instances (+ tests). - ✅ 3.13 requirement, OpenAI-Agents removal, and classic-model drop are documented as intentional breaks.
- ✅ Confirmed the package is type-checked in CI (
typecheck.yml→pyright-azurefunctions, strict/3.13) — scratch my earlier "not type-checked" concern.
Must-fix before public preview:
- 🔴 Managed Identity durable HTTP sends
Authorization: ******(token discarded) — and the CHANGELOG advertises it as working. - 🟠 Its unit test locks in the bug (asserts
== "******"). - 🟠 E2E never actually runs in CI.
- 🟡 Missing
README.md/LICENSEin the package → blank PyPI page + no license shipped.
Line-linked items below, ordered by severity (🔴 blocker · 🟠 high · 🟡 medium · 🟢 low/nit).
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 91 out of 94 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (2)
tests/azure-functions-durable/test_worker_compat.py:118
- Test should assert the specific exception type raised by DurableFunctionsWorker for malformed inputs. The worker raises ValueError when no ExecutionStarted event exists, so catching a generic Exception makes the test less precise and could hide unrelated failures.
azure-functions-durable/azure/durable_functions/internal/azurefunctions_grpc_interceptor.py:23 - The x-user-agent header is constructed from the installed 'azure-functions-durable' distribution version, but the value is prefixed with 'durabletask-python/'. This makes telemetry/debugging confusing because the header no longer identifies the actual calling SDK/package.
user_agent = f"durabletask-python/{sdk_version}"
return [
("taskhub", taskhub_name),
("x-user-agent", user_agent)] # 'user-agent' is a reserved header in grpc, so we use 'x-user-agent' instead
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 92 out of 95 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (2)
.github/workflows/typecheck.yml:68
- The
pyright-azurefunctionsjob installsazure-functions-durablewithout--no-deps, which will force pip to resolveazure-functions>=2.2.0b6. This contradicts the repo’s own note that 2.2.0b6 isn’t available on PyPI and can make the typecheck workflow fail even when the code is correct.
- name: Install packages and dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install -e ".[azure-blob-payloads,opentelemetry]"
pip install -e ./azure-functions-durable
pip install pyright
eng/templates/build.yml:107
- The build pipeline now installs the
azure-functions-durablewheel (python -m pip install "${AF_WHEEL}"), which will attempt to resolveazure-functions>=2.2.0b6from the configured feed. This will break the build/release pipeline if thatazure-functionsversion isn’t present in the feed (and this PR states it isn’t published to PyPI).
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 92 out of 95 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (3)
azure-functions-durable/azure/durable_functions/orchestrator.py:33
Orchestrator.handle()assignsself.durable_context, but the attribute is not declared in__init__. Under strict pyright this is typically flagged as an unknown member assignment/access. Declare the attribute (with an appropriate optional type) in the constructor so type checking matches runtime behavior.
.github/workflows/typecheck.yml:68- This job installs
./azure-functions-durablewith dependencies. However,azure-functions-durable/pyproject.tomlrequiresazure-functions>=2.2.0b6, andnoxfile.pyin this PR states that version is not published to PyPI. If that's still true, this workflow will fail during dependency resolution. Consider installingazure-functions-durablewith--no-depshere (and relying onrequirements.txt/a local build forazure-functions) so type checking can still run in public CI.
- name: Install packages and dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install -e ".[azure-blob-payloads,opentelemetry]"
pip install -e ./azure-functions-durable
pip install pyright
azure-functions-durable/pyproject.toml:34
- The package declares a hard dependency on
azure-functions>=2.2.0b6, butnoxfile.pyin this PR explicitly saysazure-functions>=2.2.0b6is not published to PyPI. If that’s still accurate,pip install azure-functions-durablewill fail for users and in any CI job that installs dependencies from PyPI. Either the dependency version needs to be adjusted to an actually published release, or the build/release pipeline needs to ensure that version is available in the package index being used.
|
|
||
| if response is None: | ||
| raise RuntimeError("Orchestrator execution did not produce a response.") | ||
| # The Python worker returns the input as type "json", so double-encoding is necessary |
|
|
||
| if response is None: | ||
| raise RuntimeError("Entity execution did not produce a response.") | ||
| # The Python worker returns the input as type "json", so double-encoding is necessary |
Summary
Introduces
azure-functions-durable2.x as a new sub-package in this repository — a ground-up rewrite of the Azure Durable Functions Python SDK, built on top of thedurabletaskSDK. Existing v1 apps keep working through a compatibility layer, while new apps can adopt durabletask-native APIs directly.Why build on durabletask
The 1.x SDK implemented the Durable Functions out-of-process model directly, with its own orchestration action/state model and a JSON protocol tailored to the classic Durable Functions host extension. 2.x instead builds on the
durabletaskruntime — the same gRPC-based core that powers the Durable Task Scheduler (DTS) and the modern Durable Functions host. That means:What's included
DFApp/Blueprint(the Python v2 model).def orchestrator(ctx, input)) and class-based entities (DurableEntity), alongside supported v1-style single-argument functions.DurableOrchestrationContext/DurableEntityContext, deprecatedDurableOrchestrationClientmethod aliases (emittingDeprecationWarning), return-type wrappers (DurableOrchestrationStatus,PurgeHistoryResult,EntityStateResponse),HttpManagementPayload,EntityId,RetryOptions, and thecall_httpdurable HTTP API.rewind_orchestration, plus opt-inDFApp.configure_scheduled_tasks()andDFApp.configure_history_export().azure-functions-durablepackage, unit and end-to-end test suites, and strict pyright type-checking for the new package.Programming models supported
def o(ctx, input)def e(ctx, input)/DurableEntityclassdef o(context)def e(context)Breaking changes vs.
azure-functions-durable1.xfunction.jsonprogramming model is not supported.schedule_new_orchestration,get_orchestration_state,wait_for_orchestration_completion); the v1 names remain as deprecated aliases.Status
Preview (
2.0.0b1). APIs may change before the stable2.0.0. See the package CHANGELOG for the full set of additions, compatibility notes, and known limitations.