Skip to content

Remove _merge_url_query once httpx2 merges params into the URL query #146

Description

@lesnik512

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

  1. 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.
  2. Raise the httpx2 floor in pyproject.toml to the release containing the fix.
  3. Replace every _build_request(client, method, url, kwargs) call (build_request, _prepare_request and the capped stream helpers, in both worlds) with client.build_request(method, url, **kwargs), and delete _build_request. The uncapped stream() path can go back to self._httpx2_client.stream, dropping _stream / _stream_async.
  4. 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

  1. lesnik512 commented on Oct 3, 2026

    @lesnik512
    MemberAuthor

    Ran tests/test_url_query_merge.py against pydantic/httpx2#966 (2631c38) with _merge_url_query stubbed to a pass-through. #966 appends params to the existing query instead of merging, so 3 of 7 cases fail:

    Case Ours #966 Kind
    params-override-same-key (?a=1&b=1 + b=2) a=1&b=2 a=1&b=1&b=2 Different meaning
    url-query-overrides-client (?a=1&c=1, client c=3, b=2) c=1&a=1&b=2 a=1&c=1&c=3&b=2 Different meaning
    client-params-keep-url-query c=3&a=1 a=1&c=3 Order only

    The other four cases pass. With the workaround left in place, the whole suite passes on #966.

    If #966 lands as written, removing _merge_url_query would mean per-request params no longer override a key already in the URL, and that key would be sent twice.

  2. added
    blockedWaiting on an external dependency or upstream change
    on Oct 3, 2026
  3. lesnik512 commented on Oct 4, 2026

    @lesnik512
    MemberAuthor

    Since the analysis above: requests also appends (?a=1 + params={"a": "2"} gives ?a=1&a=2), so #966's behaviour matches requests and 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.py passes 50/50 against #966 with the workaround stubbed out. Once #156 merges, step 1 here holds as-is, and step 3 means deleting _build_request and the _stream/_stream_async helpers rather than _merge_url_query.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    blockedWaiting on an external dependency or upstream change

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions