Remove _merge_url_query once httpx2 merges params into the URL query #146
Description
Context
httpx2 replaces a URL's query string whenever params are sent, including client-level params (pydantic/httpx2#905). httpware works around this in src/httpware/client.py: #145 (0.17.1) added the workaround, and #156 replaced it with _build_request, which strips the URL's query, lets httpx2 build the request, then puts the original query bytes back in front of the params. stream() goes through _stream / _stream_async for the same reason. Semantics match requests and pydantic/httpx2#966: params are appended after the URL's query, duplicate keys are kept, and the URL's query bytes are not re-encoded.
Blocked on
An httpx2 release that merges params into the existing URL query. Candidate fix: pydantic/httpx2#966 (open, unreviewed as of 2026-10-03). An earlier attempt, pydantic/httpx2#1026, was closed unmerged.
When unblocked
- Check upstream semantics still match ours: params appended after the URL query, duplicate keys kept, client params before per-request params,
params={}keeps the URL query, URL query bytes unchanged. - Raise the
httpx2floor inpyproject.tomlto the release containing the fix. - Replace every
_build_request(client, method, url, kwargs)call (build_request,_prepare_requestand the capped stream helpers, in both worlds) withclient.build_request(method, url, **kwargs), and delete_build_request. The uncappedstream()path can go back toself._httpx2_client.stream, dropping_stream/_stream_async. - Keep
tests/test_url_query_merge.py: it passes 50/50 against #966 (2631c38) with the workaround stubbed out, so it should pass unchanged. If a case fails, upstream semantics differ from #966 and that is a behaviour change to call out in the release.
Activity
lesnik512 commentedon Oct 3, 2026
Ran
tests/test_url_query_merge.pyagainst pydantic/httpx2#966 (2631c38) with_merge_url_querystubbed to a pass-through. #966 appends params to the existing query instead of merging, so 3 of 7 cases fail:params-override-same-key(?a=1&b=1+b=2)a=1&b=2a=1&b=1&b=2url-query-overrides-client(?a=1&c=1, clientc=3,b=2)c=1&a=1&b=2a=1&c=1&c=3&b=2client-params-keep-url-queryc=3&a=1a=1&c=3The other four cases pass. With the workaround left in place, the whole suite passes on #966.
If #966 lands as written, removing
_merge_url_querywould mean per-request params no longer override a key already in the URL, and that key would be sent twice.lesnik512 commentedon Oct 4, 2026
Since the analysis above:
requestsalso appends (?a=1+params={"a": "2"}gives?a=1&a=2), so #966's behaviour matchesrequestsand httpware's override was the outlier. #156 switches httpware to append and keeps the URL's query bytes unchanged. With it,tests/test_url_query_merge.pypasses 50/50 against #966 with the workaround stubbed out. Once #156 merges, step 1 here holds as-is, and step 3 means deleting_build_requestand the_stream/_stream_asynchelpers rather than_merge_url_query.lesnik512 commentedon Oct 26, 2026
Checked 2026-10-26: still blocked.
paramsparameter is set, the request parameters in the URL will disappear unexpectedly. pydantic/httpx2#905.#156 is merged here, so once pydantic/httpx2#966 lands, step 3 is deleting
_build_requestand the_stream/_stream_asynchelpers.