Skip to content

Support httpx2 in the test client - #3291

Merged
Kludex merged 2 commits into
mainfrom
support-httpx2-in-testclient
May 25, 2026
Merged

Support httpx2 in the test client#3291
Kludex merged 2 commits into
mainfrom
support-httpx2-in-testclient

Conversation

@Kludex

@Kludex Kludex commented May 25, 2026

Copy link
Copy Markdown
Owner

starlette.testclient now prefers httpx2 when installed, falling back to httpx. The fallback emits a StarletteDeprecationWarning urging migration; if neither is installed, the existing RuntimeError is raised pointing at httpx2.

Non-breaking: users with only httpx installed keep working, just warned.

AI Disclaimer

This PR was developed with the assistance of either Claude or Codex. I've reviewed and verified the changes.

Kludex added 2 commits May 25, 2026 13:48
Prefer httpx2 in starlette.testclient, falling back to httpx with a StarletteDeprecationWarning. Existing httpx-only users keep working.
@Kludex
Kludex enabled auto-merge (squash) May 25, 2026 11:54
@Kludex
Kludex merged commit 508023b into main May 25, 2026
11 checks passed
@Kludex
Kludex deleted the support-httpx2-in-testclient branch May 25, 2026 11:54
@Kludex

Kludex commented May 25, 2026

Copy link
Copy Markdown
Owner Author

Being a bit more transparent about this, instead of just the LLM generated description:

httpx has become somehow unmaintained (closing issues, discussions, and no release since 2024), and https://github.com/pydantic/httpx2 is the least annoying path forward for every consumer of that package.

@edgarrmondragon

Copy link
Copy Markdown

Should the readme be updated to reflect the preference for httpx2?

bod-zol added a commit to bod-zol/fastapi-easy-responses that referenced this pull request Jun 22, 2026
- add httpx2 as dev dependency, because since starlette Kludex/starlette#3291 (version1.2.0) a warning is issued if the httpx2 module is unavailable for the TestClient
- Kludex/starlette#3119 (v1.3.1) made this a more visible warning
- parallelly in #14 I introduced to handle all warnings as errors in unit tests
- these caused the tests to fail for this version bump
t41372 added a commit to standard-voice/standard_asr that referenced this pull request Jul 16, 2026
…constraint

The dev-only `[tool.uv] constraint-dependencies = ["starlette<1.0"]` pin held
the lock at Starlette 0.52.1 and blocked five open Dependabot alerts (2 high,
2 medium, 1 low) — all Starlette, all fixed in Starlette >= 1.3.1. The pin
existed only because Starlette's `TestClient` moved to the `httpx2` transport
(Starlette 1.2.0, "Support httpx2 in the test client", Kludex/starlette#3291)
while our FastAPI server tests still drove it through the classic `httpx`-based
client.

Migrate to the httpx2 transport and drop the constraint:

- Dev dependency `httpx>=0.28` -> `httpx2>=2.0`; server-test response
  annotations `httpx.Response` -> `httpx2.Response` (tests/test_server.py).
  The TestClient surface is otherwise identical (httpx2 is Pydantic's fork of
  httpx), so the assertions are unchanged.
- Raise the `fastapi` floor 0.118 -> 0.133 in both the `test` group and the
  `[server]` extra: fastapi 0.133 is the first release to drop its own
  `starlette<1.0.0` cap (0.132 still pins it), so it is the floor that admits
  the patched Starlette 1.x line and the floor the lower-bounds CI lane can
  verify. 0.133 subsumes the earlier 0.118 multipart-coercion floor.
- Remove the two now-obsolete `filterwarnings` ignores (the httpx-transport
  and `app=` shortcut deprecations no longer fire under httpx2; `["error", ...]`
  proves it — the suite stays green with them gone).
- Re-resolve the lock: Starlette 0.52.1 -> 1.3.1, httpx dropped, httpx2 2.5.0
  and httpcore2 added, fastapi stays 0.136.3.

Docs/config kept consistent: the advisory in docs/compatibility-advisories.md
moves to "Resolved" (dated); the stale `starlette<1.0` example references in
CONTRIBUTING.md and .github/dependabot.yml are updated.

Gates: ruff clean, pyright strict 0 errors, pytest 1722 passed at 100%
line+branch coverage (py3.13), and the lower-bounds floor stack (fastapi
0.133.0 + starlette 1.3.1 + httpx2 2.0.0, py3.10) passes the full suite.
t41372 added a commit to standard-voice/standard_asr that referenced this pull request Jul 16, 2026
…constraint

The dev-only `[tool.uv] constraint-dependencies = ["starlette<1.0"]` pin held
the lock at Starlette 0.52.1 and blocked five open Dependabot alerts (2 high,
2 medium, 1 low) — all Starlette, all fixed in Starlette >= 1.3.1. The pin
existed only because Starlette's `TestClient` moved to the `httpx2` transport
(Starlette 1.2.0, "Support httpx2 in the test client", Kludex/starlette#3291)
while our FastAPI server tests still drove it through the classic `httpx`-based
client.

Migrate to the httpx2 transport and drop the constraint:

- Dev dependency `httpx>=0.28` -> `httpx2>=2.0`; server-test response
  annotations `httpx.Response` -> `httpx2.Response` (tests/test_server.py).
  The TestClient surface is otherwise identical (httpx2 is Pydantic's fork of
  httpx), so the assertions are unchanged.
- Security floors (the load-bearing rationale): all five GHSAs are fixed only
  on the Starlette 1.x line, with no 0.x backports, so `starlette>=1.3.1` is
  declared directly in the `[server]` extra as a fail-loud floor — an app
  pinning a vulnerable 0.x Starlette now gets a resolver error instead of a
  silently vulnerable server. The `fastapi` floor rises 0.118 -> 0.133 in both
  the `test` group and `[server]`: 0.133 is the first release to drop FastAPI's
  own `starlette<1.0.0` cap (0.132 still pins it), i.e. the lowest fastapi that
  can coexist with the patched line. It subsumes the earlier 0.118
  multipart-coercion floor (kept as recorded history; today's lower-bounds lane
  resolves 0.133 and can no longer re-verify it).
- Remove the two now-obsolete `filterwarnings` ignores (the httpx-transport
  and `app=` shortcut deprecations no longer fire under httpx2; `["error", ...]`
  proves it — the suite stays green with them gone).
- Re-resolve the lock: Starlette 0.52.1 -> 1.3.1, httpx dropped, httpx2 2.5.0,
  httpcore2 2.5.0 and truststore added (httpx2's trust-store chain replaces
  certifi), fastapi stays 0.136.3.

Docs/config kept consistent: the advisory in docs/compatibility-advisories.md
moves to "Resolved" (dated); the stale `starlette<1.0` example references in
CONTRIBUTING.md and .github/dependabot.yml are updated.

Gates: ruff clean, pyright strict 0 errors, pytest 1722 passed at 100%
line+branch coverage (py3.13), and the lower-bounds floor stack (fastapi
0.133.0 + starlette 1.3.1 + httpx2 2.0.0, py3.10) passes the full suite.
t41372 added a commit to standard-voice/standard_asr that referenced this pull request Aug 8, 2026
…constraint

The dev-only `[tool.uv] constraint-dependencies = ["starlette<1.0"]` pin held
the lock at Starlette 0.52.1 and blocked five open Dependabot alerts (2 high,
2 medium, 1 low) — all Starlette, all fixed in Starlette >= 1.3.1. The pin
existed only because Starlette's `TestClient` moved to the `httpx2` transport
(Starlette 1.2.0, "Support httpx2 in the test client", Kludex/starlette#3291)
while our FastAPI server tests still drove it through the classic `httpx`-based
client.

Migrate to the httpx2 transport and drop the constraint:

- Dev dependency `httpx>=0.28` -> `httpx2>=2.0`; server-test response
  annotations `httpx.Response` -> `httpx2.Response` (tests/test_server.py).
  The TestClient surface is otherwise identical (httpx2 is Pydantic's fork of
  httpx), so the assertions are unchanged.
- Security floors (the load-bearing rationale): all five GHSAs are fixed only
  on the Starlette 1.x line, with no 0.x backports, so `starlette>=1.3.1` is
  declared directly in the `[server]` extra as a fail-loud floor — an app
  pinning a vulnerable 0.x Starlette now gets a resolver error instead of a
  silently vulnerable server. The `fastapi` floor rises 0.118 -> 0.133 in both
  the `test` group and `[server]`: 0.133 is the first release to drop FastAPI's
  own `starlette<1.0.0` cap (0.132 still pins it), i.e. the lowest fastapi that
  can coexist with the patched line. It subsumes the earlier 0.118
  multipart-coercion floor (kept as recorded history; today's lower-bounds lane
  resolves 0.133 and can no longer re-verify it).
- Remove the two now-obsolete `filterwarnings` ignores (the httpx-transport
  and `app=` shortcut deprecations no longer fire under httpx2; `["error", ...]`
  proves it — the suite stays green with them gone).
- Re-resolve the lock: Starlette 0.52.1 -> 1.3.1, httpx dropped, httpx2 2.5.0,
  httpcore2 2.5.0 and truststore added (httpx2's trust-store chain replaces
  certifi), fastapi stays 0.136.3.

Docs/config kept consistent: the advisory in docs/compatibility-advisories.md
moves to "Resolved" (dated); the stale `starlette<1.0` example references in
CONTRIBUTING.md and .github/dependabot.yml are updated.

Gates: ruff clean, pyright strict 0 errors, pytest 1722 passed at 100%
line+branch coverage (py3.13), and the lower-bounds floor stack (fastapi
0.133.0 + starlette 1.3.1 + httpx2 2.0.0, py3.10) passes the full suite.
t41372 added a commit to standard-voice/standard_asr that referenced this pull request Aug 8, 2026
…constraint (#40)

The dev-only `[tool.uv] constraint-dependencies = ["starlette<1.0"]` pin held
the lock at Starlette 0.52.1 and blocked five open Dependabot alerts (2 high,
2 medium, 1 low) — all Starlette, all fixed in Starlette >= 1.3.1. The pin
existed only because Starlette's `TestClient` moved to the `httpx2` transport
(Starlette 1.2.0, "Support httpx2 in the test client", Kludex/starlette#3291)
while our FastAPI server tests still drove it through the classic `httpx`-based
client.

Migrate to the httpx2 transport and drop the constraint:

- Dev dependency `httpx>=0.28` -> `httpx2>=2.0`; server-test response
  annotations `httpx.Response` -> `httpx2.Response` (tests/test_server.py).
  The TestClient surface is otherwise identical (httpx2 is Pydantic's fork of
  httpx), so the assertions are unchanged.
- Security floors (the load-bearing rationale): all five GHSAs are fixed only
  on the Starlette 1.x line, with no 0.x backports, so `starlette>=1.3.1` is
  declared directly in the `[server]` extra as a fail-loud floor — an app
  pinning a vulnerable 0.x Starlette now gets a resolver error instead of a
  silently vulnerable server. The `fastapi` floor rises 0.118 -> 0.133 in both
  the `test` group and `[server]`: 0.133 is the first release to drop FastAPI's
  own `starlette<1.0.0` cap (0.132 still pins it), i.e. the lowest fastapi that
  can coexist with the patched line. It subsumes the earlier 0.118
  multipart-coercion floor (kept as recorded history; today's lower-bounds lane
  resolves 0.133 and can no longer re-verify it).
- Remove the two now-obsolete `filterwarnings` ignores (the httpx-transport
  and `app=` shortcut deprecations no longer fire under httpx2; `["error", ...]`
  proves it — the suite stays green with them gone).
- Re-resolve the lock: Starlette 0.52.1 -> 1.3.1, httpx dropped, httpx2 2.5.0,
  httpcore2 2.5.0 and truststore added (httpx2's trust-store chain replaces
  certifi), fastapi stays 0.136.3.

Docs/config kept consistent: the advisory in docs/compatibility-advisories.md
moves to "Resolved" (dated); the stale `starlette<1.0` example references in
CONTRIBUTING.md and .github/dependabot.yml are updated.

Gates: ruff clean, pyright strict 0 errors, pytest 1722 passed at 100%
line+branch coverage (py3.13), and the lower-bounds floor stack (fastapi
0.133.0 + starlette 1.3.1 + httpx2 2.0.0, py3.10) passes the full suite.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants