[python] avoid etag/match_condition clientName collision with multiple etag headers (#10816)
# Problem PR #10494 (which added support for custom etag wire names) broke operations that have **more than one `Azure.Core.eTag`-typed header**. The Azure Storage Blob `copyFromUrl` operation is a concrete example — it carries: - standard `If-Match` / `If-None-Match` - custom `x-ms-source-if-match` / `x-ms-source-if-none-match` The emitter in `http.ts` stamps `etagRole = "ifMatch"` / `"ifNoneMatch"` onto **every** one of those headers. Then in `preprocess/__init__.py`, `update_parameter` blindly applies `headers_convert(ETAG_MATCH_DATA)` / `ETAG_NONE_MATCH_DATA` to every parameter with that role — overwriting `clientName` to `"etag"` and `"match_condition"` on **both** pairs. The operation ends up with two parameters named `etag` and two named `match_condition`. The slot-picker in `update_client` already chose one ifMatch/ifNoneMatch slot per operation, but it did nothing to prevent the per-parameter rename on the others. # Fix In `preprocess/__init__.py` `update_client`: 1. Collect **all** `ifMatch` and `ifNoneMatch` candidates per operation, not just the first. 2. New `_pick_etag_slot` helper prefers the standard `If-Match` / `If-None-Match` wire names over custom etag headers (matches pre-PR-10494 behaviour when both are present). 3. Strip `etagRole` from non-selected candidates so `update_parameter` leaves their natural `clientName` intact (e.g. `source_if_match`, `source_if_none_match`). # Result For `copyFromUrl`: - `If-Match` / `If-None-Match` → promoted to the `etag` / `match_condition` pair (unchanged behaviour vs. pre-PR-10494). - `x-ms-source-if-match` / `x-ms-source-if-none-match` → keep their natural `source_if_match` / `source_if_none_match` client names. No more clientName collisions. # Tests Adds `tests/unit/test_preprocess_etag.py` with six tests: - `test_etag_role_preserved_when_only_standard_pair_present` - `test_etag_role_preserved_when_only_custom_pair_present` - `test_standard_etag_wins_over_custom_when_both_present` (regression) - `test_first_custom_pair_chosen_when_multiple_custom_pairs_present` - `test_synthetic_partner_still_works_with_only_one_custom_etag` - `test_full_update_yaml_does_not_collide_client_names` (end-to-end) Verified the three multi-etag tests **fail** on `upstream/main` and pass with this change. All existing unit tests still pass. # Spec referenced The original repro is `copyFromUrl` in `specification/storage/Microsoft.BlobStorage/routes.tsp` of `Azure/azure-rest-api-specs`, which composes `SourceIfMatchParameter`, `SourceIfNoneMatchParameter`, `IfMatchParameter`, and `IfNoneMatchParameter` (all `Azure.Core.eTag`). --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
L
Libba Lawrence committed
66c5e87189dabecad413135a70b8f33e1a26f83f
Parent: 8701e7f
Committed by GitHub <noreply@github.com>
on 6/1/2026, 3:19:25 PM