Skip to content

feat: accept injected httpx2 clients#2747

Draft
dsfaccini wants to merge 2 commits into
googleapis:mainfrom
dsfaccini:httpx2-support
Draft

feat: accept injected httpx2 clients#2747
dsfaccini wants to merge 2 commits into
googleapis:mainfrom
dsfaccini:httpx2-support

Conversation

@dsfaccini

Copy link
Copy Markdown

Draft / RFC — not for direct merge. A minimal, duck-typed demonstration to help move #2680 forward. This repo mirrors an internal source of truth via Copybara and doesn't merge external PRs directly, so the value here is making the change concrete, not landing it.

httpx2 is Pydantic's actively-maintained, API-compatible fork of httpx. Because it lives in a separate import namespace, httpx2.* classes fail isinstance(x, httpx.*). Handing the SDK an httpx2 client via HttpOptions(httpx_async_client=...) hits two walls today:

  • Wall 1 — rejected at construction. HttpOptions.httpx_async_client / httpx_client are typed via the HttpxClient / HttpxAsyncClient aliases in generated google/genai/types.py; with arbitrary_types_allowed=True, Pydantic emits an is_instance_of(httpx.AsyncClient) check, so an httpx2 client raises ValidationError.
  • Wall 2 — response not processed. Hand-written isinstance(x, httpx.Response/Headers) checks and the retry-exception tuple in errors.py / _api_client.py don't recognize httpx2 objects. The async success path is the worst case: its status_code == 200 early-return is nested inside isinstance(response, httpx.Response), so every async httpx2 response falls through to "Unsupported response type".

Behind a guarded import httpx2 (a no-op when httpx2 isn't installed), this widens the runtime checks:

  • Hand-written (the real, stick-able change): errors.py raise_for_response + raise_for_async_response; _api_client.py HttpResponse.__init__ headers check, _iter_response_stream + _aiter_response_stream (validity guard + iterator selection), and the retry_args retry predicate — each via one widened tuple per concept.
  • Generated (cannot land as-is): types.py widens the HttpxClient / HttpxAsyncClient aliases to Union[httpx.*, httpx2.*]. This file is generated ("DO NOT EDIT"), so the real Wall 1 fix must happen in the generator template; it's included here only so the demonstration runs end-to-end.

Adds a test (guarded by pytest.importorskip("httpx2")) covering both walls with real httpx2 objects, and declares httpx2 as a test dependency (py3.10+, which required bumping the anyio / idna pins it needs).

Scope / known limitation: this covers the models.generate_content(_stream) path (sync + async, streaming + non-streaming). The injected client is also forwarded into the experimental client.agents / interactions / webhooks / triggers surfaces (the _gaos sub-SDK), which still couple to httpx and are not handled here — that's a separate, larger generated-code change. Deliberately does not touch the default-client base classes or the client-arg filter.

httpx2 (https://github.com/pydantic/httpx2) is a drop-in fork of httpx under
a separate import namespace, so httpx2 classes fail isinstance(x, httpx.*)
and are rejected by HttpOptions. Behind a guarded `import httpx2`:

- errors.py / _api_client.py: consolidate the duplicated httpx response
  checks into widened tuples and swap the runtime isinstance sites for
  response, headers, and the transient-exception retry predicate.
- types.py (generated): widen the HttpxClient / HttpxAsyncClient aliases so
  HttpOptions(httpx_async_client=...) accepts an httpx2 client instead of
  raising ValidationError.

Add a test guarded by pytest.importorskip("httpx2") and declare httpx2 as a
test dependency (py3.10+).
@google-cla

google-cla Bot commented Jul 20, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@dsfaccini dsfaccini changed the title Demo: support injecting a Pydantic httpx2 client (#2680) feat: accept injected httpx2 clients Jul 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant