[python] support custom wire names for etags defined with Azure.Core.eTag#10494
[python] support custom wire names for etags defined with Azure.Core.eTag#10494iscai-msft wants to merge 5 commits intomicrosoft:mainfrom
Azure.Core.eTag#10494Conversation
commit: |
|
All changed packages have been documented.
Show changes
|
|
You can try these changes here
|
There was a problem hiding this comment.
Pull request overview
This PR fixes how the Python HTTP client generator handles custom ETag-typed header parameters (e.g. x-ms-if-blob-match, x-ms-if-blob-none-match) by centralizing ETag header classification and ensuring serialization uses the real HTTP header name.
Changes:
- Add a shared
_etag_kind()helper to consistently classify ETag headers asif-matchvsif-none-match. - Preserve the actual header name via
originalWireNameduring preprocessing and use it during header serialization. - Emit an
isEtagflag from the TypeScript emitter so preprocessing can detect custom ETag-typed headers.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/http-client-python/generator/pygen/preprocess/init.py | Centralizes ETag header classification, fixes slot pairing, and preserves originalWireName during header conversion. |
| packages/http-client-python/generator/pygen/codegen/serializers/parameter_serializer.py | Builds ETag header serialization using original_wire_name instead of hard-coded If-Match/If-None-Match. |
| packages/http-client-python/generator/pygen/codegen/models/parameter.py | Adds original_wire_name field to parameter model for downstream serialization. |
| packages/http-client-python/emitter/src/http.ts | Emits isEtag for header parameters by detecting Azure.Core.eTag scalar types. |
| .chronus/changes/python-etagMatchCondition-2026-04-24-18-22-00.md | Changelog entry documenting the fix. |
| # For standard headers, use "if-match". For custom etag | ||
| # headers, we can't derive the partner's real wire name, | ||
| # so fall back to the standard "If-Match" header. | ||
| property_if_match["wireName"] = "if-match" | ||
| property_if_match.pop("isEtag", None) | ||
| if not property_if_none_match and property_if_match: | ||
| property_if_none_match = property_if_match.copy() | ||
| property_if_none_match["wireName"] = "if-none-match" |
There was a problem hiding this comment.
Synthetic partner parameters are created with wireName set to lowercase ("if-match" / "if-none-match"). Since this PR now uses originalWireName to determine the actual HTTP header name during serialization, these synthetic partners will generate lowercase header keys (e.g. "if-match") rather than the canonical "If-Match" / "If-None-Match" (and the nearby comment explicitly says to fall back to the standard header). Consider setting the synthetic partner wireName to the canonical casing (or explicitly setting originalWireName to If-Match / If-None-Match) before conversion so generated code stays consistent/stable.
| # For standard headers, use "if-match". For custom etag | |
| # headers, we can't derive the partner's real wire name, | |
| # so fall back to the standard "If-Match" header. | |
| property_if_match["wireName"] = "if-match" | |
| property_if_match.pop("isEtag", None) | |
| if not property_if_none_match and property_if_match: | |
| property_if_none_match = property_if_match.copy() | |
| property_if_none_match["wireName"] = "if-none-match" | |
| # For standard headers, use "If-Match". For custom etag | |
| # headers, we can't derive the partner's real wire name, | |
| # so fall back to the standard "If-Match" header. | |
| property_if_match["wireName"] = "If-Match" | |
| property_if_match["originalWireName"] = "If-Match" | |
| property_if_match.pop("isEtag", None) | |
| if not property_if_none_match and property_if_match: | |
| property_if_none_match = property_if_match.copy() | |
| property_if_none_match["wireName"] = "If-None-Match" | |
| property_if_none_match["originalWireName"] = "If-None-Match" |
| elif ( | ||
| self.version_tolerant | ||
| and yaml_data["location"] == "header" | ||
| and yaml_data.get("isEtag") | ||
| and wire_name_lower not in HEADERS_CONVERT_IN_METHOD | ||
| ): | ||
| # Custom etag-typed headers (e.g. x-ms-if-blob-match) get the same | ||
| # etag/match_condition treatment as standard If-Match/If-None-Match. | ||
| kind = _etag_kind(yaml_data) | ||
| if kind == "if-none-match": | ||
| headers_convert(yaml_data, HEADERS_CONVERT_IN_METHOD["if-none-match"]) | ||
| else: | ||
| headers_convert(yaml_data, HEADERS_CONVERT_IN_METHOD["if-match"]) |
There was a problem hiding this comment.
The new custom etag header flow (isEtag + _etag_kind + originalWireName-based serialization) doesn’t appear to have coverage in the existing generator/unit tests. It would be good to add a test case that feeds a custom etag-typed header like x-ms-if-blob-none-match through preprocessing and asserts that (1) it’s classified into the correct slot and paired correctly, and (2) the generated serializer emits the custom header name rather than If-None-Match.
Custom headers typed as Azure.Core.eTag (e.g. x-ms-blob-if-match, x-ms-source-if-match) now get the same etag/match_condition treatment as standard If-Match/If-None-Match headers. The original wire name is preserved via originalWireName so the correct HTTP header is sent. - Add isEtag flag in emitter for Azure.Core.eTag-typed headers - Add _is_etag_match/_is_etag_none_match helpers in preprocessor - Preserve originalWireName in headers_convert for custom headers - Use originalWireName in serializer for dynamic header name - Use identity-based removal for etag param reordering Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
15ed29c to
f9b7c14
Compare
Move etagRole determination into the emitter's getEtagRole() function. Standard If-Match/If-None-Match headers work with any type. Non-standard headers (e.g. x-ms-blob-if-match) require Azure.Core.eTag type. Simplify preprocess _get_etag_role to just read the etagRole field. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Azure.Core.eTag
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
msyyc
left a comment
There was a problem hiding this comment.
Overall looks good to me. Maybe we could ask TCGC to provide a helper function to help judge etag type in the future.
Problem
Custom etag-typed headers (e.g.
x-ms-if-blob-match,x-ms-if-blob-none-match) were being assigned to the wrong if-match/if-none-match slot during preprocessing. The slot assignment and the per-parameter conversion used different logic to classify headers, and synthetic partners inherited incorrect wire names.Changes
_etag_kind()helper — centralizes etag classification usingnone-matchsubstring (stricter than the previousnonecheck which could false-positive on unrelated headers)_etag_kind()to correctly classify custom etag headers before pairingisEtagfrom synthetic clones to prevent re-conversion with wrongoriginalWireName, falls back to standardIf-Match/If-None-Matchwire names_etag_kind()logic