fix(http-client-python): preserve TypeSpec field order#10715
fix(http-client-python): preserve TypeSpec field order#10715l0lawrence wants to merge 2 commits into
Conversation
…tipart_form_data The generated prepare_multipart_form_data helper used to emit all file parts before all data parts, regardless of how the TypeSpec model declared them. RFC 7578 §5.2 says `the order in which [parts] are sent is significant`, and some streaming server-side parsers (e.g. the Azure AI Foundry hosted-agents `create_agent_version_from_code` endpoint) require JSON metadata parts to precede binary file parts; otherwise they report the metadata part as missing. The helper now takes a single ordered list of `(wire_name, is_file)` tuples and iterates in TypeSpec declaration order. The call site in builder_serializer.py emits that list directly from `model_type.properties` (which already iterates in declared order). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
commit: |
|
All changed packages have been documented.
Show changes
|
|
You can try these changes here
|
prepare_multipart_form_data…ata to satisfy Azure pylint Azure pylint guidelines (C4739/C4741/C4742) require :param/:return:/:rtype: directives on any docstring. Convert the rationale to a leading comment so the helper matches the rest of the file's style (none of the other helpers have docstrings). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
WIP Investigation ## Summary
The generated
prepare_multipart_form_datahelper in the Python emitter usedto emit all file parts before all data parts, regardless of how the
TypeSpec model declared them. This PR makes the helper preserve the
declaration order.
Why this is a bug
are sent is significant."
payload.multipartscenario docs themselves expectdeclaration order — e.g. for
ComplexHttpPartsModelRequest, the documentedrequest body interleaves
id, address, profileImage, previousAddresses, pictures, but the previous Python emitter would have rearranged that toprofileImage, pictures, id, address, previousAddresses.create_agent_version_from_codeendpoint(Azure/azure-sdk-for-python#46932).
Their server stream-parses the multipart body and expects the small JSON
metadata parts before the binary file part; otherwise it reports the
metadata as missing. They had to patch the generated helper via a
post-emitter-fixes.cmdscript to swap the loops.MultiPartFormDataBinaryContentDefinition→_multipartContent.Add(...))and Java (
MultipartFormDataHelper.addText/addFile) already preservedeclared order. Python was the outlier.
Changes
Helper template (
utils.py.jinja2)Previously two separate loops — files first, then data — produced:
Now a single ordered list of
(wire_name, is_file)tuples preservesdeclaration order:
Call site (
builder_serializer.py)model_type.propertiesalready iterates in TypeSpec declaration order, sothis is all that's needed to make the on-wire order match the spec.
Caveat (called out in the changelog)
This is observable on the wire for services whose TypeSpec model declares
data fields before file fields — that order is now preserved. The previous
"all files first" behavior was a bug, the new behavior matches the
TypeSpec contract and RFC 7578, and no published Azure service has been
identified as relying on the old order.
Validation
Regenerated the unbranded
payload-multipartSDK locally and confirmed:ComplexHttpPartsModelRequestnow emits[("id", False), ("address", False), ("profileImage", True), ("previousAddresses", False), ("pictures", True)]— exactly matching the Spector scenarioDoc.
payload-multipartmock-API tests pass (sync + async).tests/unit/test_prepare_multipart_form_data.pycover declared-order preservation, list-valued file fields, JSON
encoding of data fields, and falsy-entry skipping.
npm run lint(pylint 10.00/10) andblack --checkpass on the changedfiles.