Fix durableClient binding FunctionLoadError on Python 3.14 (PEP 649/749)#611
Open
nachogarcia wants to merge 1 commit into
Open
Fix durableClient binding FunctionLoadError on Python 3.14 (PEP 649/749)#611nachogarcia wants to merge 1 commit into
nachogarcia wants to merge 1 commit into
Conversation
`_add_rich_client` makes the worker's `durableClient` binding type-check pass by forcing the client parameter's annotation to `str` (`user_code.__annotations__[parameter_name] = str`) before wrapping the user function with `@functools.wraps(user_code)`. On Python 3.14, PEP 649/749 changed how annotations are carried: `functools.WRAPPER_ASSIGNMENTS` now copies `__annotate__` instead of the `__annotations__` dict. The copied `__annotate__` lazily re-derives the *original* `DurableOrchestrationClient` annotation, overriding the `str` patch. The worker then reads the wrapper's annotation as `DurableOrchestrationClient`, `durableClient` binding validation fails, `FunctionLoadError` is raised, and the host 503s on startup. On Python <=3.13 this worked because `wraps` copied the already-patched `__annotations__` dict. Re-apply the `str` override on the wrapper after `wraps` runs, and drop `__annotate__` so both the dict-based reader and the `__annotate__`-based reader (which the worker uses on 3.14) agree. The pre-wrap patch is kept for the <=3.13 path. The rich `DurableOrchestrationClient` is still constructed at call time and passed to user code unchanged. Adds a regression test asserting the built wrapper's client-parameter annotation reads as `str` via both `__annotations__` and, on 3.14, `annotationlib.get_annotations(..., format=FORWARDREF)`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Author
|
@microsoft-github-policy-service agree company="Refine-Technologies-Inc" |
Contributor
|
@nachogarcia the command you issued was incorrect. Please try again. Examples are: and |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
On Python 3.14, a function app that uses
@app.durable_client_input(...)fails to start: thedurableClientbinding fails type validation in the Functions Python worker, raisingFunctionLoadError, and the host returns 503. The same app works on Python ≤3.13.Fixes #596.
Root cause
DFApp._add_rich_clientmakes the worker's binding type-check pass by forcing the client parameter's annotation tostr:…and then wraps the user function with
@functools.wraps(user_code)intodf_client_middleware.PEP 649 / PEP 749 (Python 3.14) changed how annotations are stored: functions now carry an
__annotate__callable, andfunctools.WRAPPER_ASSIGNMENTScopies__annotate__instead of the__annotations__dict. The copied__annotate__lazily re-derives the originalDurableOrchestrationClientannotation, overriding thestrpatch. The worker reads the wrapper's annotation asDurableOrchestrationClient,durableClientbinding validation fails, and startup 503s.On Python ≤3.13 there is no
__annotate__, sowrapscopied the already-patched__annotations__dict and everything worked.Fix
After the
@wrapswrapper is created, re-apply thestroverride on the wrapper and drop its__annotate__, so both the dict-based reader and the__annotate__-based reader (used by the worker on 3.14) agree:The pre-wrap patch is kept for the ≤3.13 path. The block is a guarded no-op on ≤3.13 (
hasattr(__annotate__)is False there). Runtime behavior is unchanged: the richDurableOrchestrationClientis still constructed from the starter string and passed to user code — only the annotation the worker inspects is forced tostr.Testing
test_durable_client_input_annotation_overridden_to_strasserts the built wrapper's client-parameter annotation reads asstrvia both__annotations__and (on 3.14)annotationlib.get_annotations(..., format=Format.FORWARDREF). It fails on 3.14 without the fix and passes with it.flake8 ./azure/clean.🤖 Generated with Claude Code