Skip to content

[HTTPXodus] migrate httpx to httpx2 (hard switch; closes #5258) - #5351

Closed
ProgrammerPlus1998 wants to merge 4 commits into
python-telegram-bot:masterfrom
ProgrammerPlus1998:httpxodus/httpx2-migration
Closed

ProgrammerPlus1998 wants to merge 4 commits into
python-telegram-bot:masterfrom
ProgrammerPlus1998:httpxodus/httpx2-migration

Conversation

@ProgrammerPlus1998

@ProgrammerPlus1998 ProgrammerPlus1998 commented Sep 3, 2026 •

Copy link
Copy Markdown

Closes #5258

🏷️ Part of HTTPXodus — a community effort to help major Python projects plan their path off the stalled httpx stable line onto httpx2, the actively maintained fork by Pydantic Services.

What this PR does

Complete hard switch from httpx to httpx2:

  • Direct import httpx2 in telegram/request/_httpxrequest.py and telegram/ext/_applicationbuilder.py — no try/except dual-import, no aliasing
  • All httpx.X references updated to httpx2.X, including test monkeypatch targets (monkeypatch.setattr(httpx2, ...))
  • pyproject.toml: httpx2>=2.12.0 unconditional (requires-python is already >=3.10); http2/socks extras now pull httpx2[http2] / httpx2[socks]; the httpcore>=1.0.9 workaround pin is dropped (httpx2 depends on httpcore2, which pins correctly)
  • Doc links updated to the httpx2 repository
  • uv.lock regenerated (adds httpx2's own dependencies — truststore, httpx2-jsfetch — which were missing)

Diff summary

File Change
pyproject.toml httpx2>=2.12.0 unconditioned; extras switched to httpx2[...]; drop httpcore pin
src/telegram/request/_httpxrequest.py import httpx2; httpx2.X throughout; doc links fixed
src/telegram/ext/_applicationbuilder.py Same
tests/request/test_request.py, tests/ext/test_applicationbuilder.py, tests/test_bot.py, tests/test_official/scraper.py Direct import httpx2; monkeypatch targets renamed
tests/auxil/networking.py from httpx2 import AsyncClient, AsyncHTTPTransport, Response
uv.lock Re-locked

The string-matching against "httpx[http2]" / "httpx[socks]" in _httpxrequest.py is intentionally kept: httpx2 2.12.0 still emits those exact messages when the extras are missing.

Test results

Full suite (pytest tests, -n auto, per-test timeout 120s), httpx2 2.12.0:

  • 8261 passed, 30 skipped, 19 failed
  • The 19 failures are environmental/pre-existing, none caused by this change:
    • 11 × TestUpdater/TestApplication webhook tests time out — also time out on the base commit with real httpx in the same environment (verified by control run); local webhook harness issue on this machine
    • 3 × live Telegram API tests (User_personal_channel_missing, Bot_score_not_modified, and test_get_chat — the public test bot's profile currently shows vandalized text)
    • 2 × DNS assertions affected by the local proxy's fake-IP range
    • 3 × related cascading errors
  • The migration-relevant files are fully green: tests/request/test_request.py + tests/ext/test_applicationbuilder.py — 229 passed, 2 skipped, 5 xfailed

Notes for reviewer

  • ⚠️ TLS behavior change: httpx2 verifies TLS against the OS trust store (via truststore) instead of the bundled certifi. Deployments that relied on certifi's CA bundle (minimal containers, corporate proxies) may need SSL_CERT_FILE / SSL_CERT_DIR after the switch. Worth a line in the changelog.

Happy to revise per review — and equally happy to close this PR if the maintainers would rather wait for httpx 1.0 stable. 🙏

Adopt the actively maintained httpx2 (Pydantic Services fork, with original
httpx author Tom Christie involved) alongside the existing httpx pin. The
dual import keeps every downstream user's import path unchanged and avoids
a hard breaking change for code that type-annotates against httpx.Proxy /
httpx.URL / httpx.Timeout in the public ApplicationBuilder surface.

- pyproject.toml: add httpx2 >= 2.12.0 (python_version >= '3.10') alongside
  the existing httpx >=0.27,<0.29 pin. PTB already requires python >=3.10,
  so the marker is documentation, not a constraint.
- src/telegram/request/_httpxrequest.py and
  src/telegram/ext/_applicationbuilder.py: dual import that prefers
  httpx2 and falls back to httpx on ModuleNotFoundError, matching the
  pattern used by the other HTTPXodus migrations (AutoGPT, mem0, Starlette).
- tests/{auxil/networking.py,ext/test_applicationbuilder.py,request/test_request.py,
  test_bot.py,test_official/scraper.py}: same dual import so test
  side-effects and isinstance checks resolve to the same module the SUT
  imports.

Refs: python-telegram-bot#5258
@ProgrammerPlus1998
ProgrammerPlus1998 force-pushed the httpxodus/httpx2-migration branch from 6c83b97 to 0775a55 Compare September 3, 2026 09:39
@ProgrammerPlus1998 ProgrammerPlus1998 changed the title [HTTPXodus] migrate httpx to httpx2 with dual import migrate httpx to httpx2 with dual import (closes #5258) Sep 3, 2026
@ProgrammerPlus1998
ProgrammerPlus1998 force-pushed the httpxodus/httpx2-migration branch from fb3f1d6 to ea47457 Compare September 4, 2026 08:39
Repair syntactically broken import shims left in _httpxrequest.py,
_applicationbuilder.py and four test files; rename remaining bare
'httpx' monkeypatch targets; switch the http2/socks extras to
httpx2[...]; drop the obsolete httpcore pin (httpx2 uses httpcore2);
replace dead python-httpx2.org links; re-lock (adds httpx2's missing
truststore/httpx2-jsfetch deps).

Refs: python-telegram-bot#5258
Comment thread src/telegram/ext/_applicationbuilder.py Outdated
import httpx
try:
import httpx2
except ModuleNotFoundError:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems...wrong? The indentation is off and the except does not have any block. Also I would expect it to be either a plain import of httpx2, or have a fallback like

...
except ModuleNotFoundError:
    import httpx as httpx2 # or vice versa

@ProgrammerPlus1998 ProgrammerPlus1998 changed the title migrate httpx to httpx2 with dual import (closes #5258) [HTTPXodus] migrate httpx to httpx2 (hard switch; closes #5258) Sep 10, 2026
@ProgrammerPlus1998

Copy link
Copy Markdown
Author

You're absolutely right — that hunk was a broken leftover from the earlier dual-import revision, and it was worse than it looked: six files had mangled try/except shims, so the package didn't even import at that revision.

Fixed in 579d823: it's now exactly what you expected — a plain import httpx2, no fallback anywhere (in _applicationbuilder.py, _httpxrequest.py, and the test files). The same commit also switches the http2/socks extras to httpx2[...], drops the obsolete httpcore pin, and re-locks. Full suite: 8261 passed; the remaining failures are environmental on my machine (webhook harness timeouts also occur on the base commit; live-API tests).

@harshil21

Copy link
Copy Markdown
Member

Not looking for slop / unprompted PRs

@harshil21 harshil21 closed this Sep 30, 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.

Consider replacing httpx with httpx2

3 participants