Skip to content

HTTPX Compatibility

HTTPXYZ is a fork of HTTPX. While most code can be migrated simply by replacing import httpx with import httpxyz, there are situations where you cannot easily change every dependency — for example, when a third-party library such as RESPX checks isinstance(response, httpx.Response) internally.

To handle this, importing httpxyz automatically registers it under the httpx name in sys.modules:

import sys
import httpxyz

assert sys.modules["httpx"] is sys.modules["httpxyz"]  # True

This means any subsequent import httpx in the same process will resolve to httpxyz, and isinstance checks against httpx classes will pass for httpxyz objects:

import httpx   # resolves to httpxyz after the above
import httpxyz

response = httpxyz.Response(200)
assert isinstance(response, httpx.Response)  # True

Prior art

This pattern — where the installable PyPI name differs from the importable module name, or where a fork registers itself under its predecessor's name — is well-established in the Python ecosystem:

  • Pillow / PIL — The actively maintained fork of the Python Imaging Library. It installs as Pillow on PyPI but exposes the PIL package, so all code written against the original PIL works without modification.
  • opencv-python / cv2 — The PyPI package is opencv-python; the module is cv2.
  • pyyaml / yaml — The PyPI package is pyyaml; the module is yaml.

Important caveat

The aliasing uses sys.modules.setdefault, which means it only takes effect if httpx has not already been imported before httpxyz. If both packages are installed and httpx is imported first, the alias will not be established.

For this reason, ensure that nothing in your codebase (or its dependencies) imports httpx before httpxyz is imported. In practice, the simplest way to guarantee this is to import httpxyz early — for example, at the top of your application's entry point — before any other imports that might pull in httpx.

Using respx in pytest

respx registers itself as a pytest plugin via a pytest11 entry point. Pytest loads all such entry points before it reads your conftest.py, so respx imports the real httpx first and the setdefault becomes a no-op — breaking respx.mock with HTTPXYZ objects.

HTTPXYZ ships its own pytest11 entry point to solve this. Because -p flags in addopts are processed by pytest before entry-point plugins are loaded, adding -p httpxyz guarantees HTTPXYZ wins the race:

# pyproject.toml
[tool.pytest.ini_options]
addopts = "-p httpxyz"

or just add this to your pytest.ini if you're using that.

With this in place:

  • import httpx anywhere in your test suite returns HTTPXYZ.
  • respx sees HTTPXYZ as httpx, so all isinstance checks pass.
  • The respx_mock fixture and @pytest.mark.respx marker remain available.
  • No need for -p no:respx.

Using pytest-httpx in pytest

pytest-httpx has the same import-order problem as respx, but with an additional wrinkle: it directly imports httpcore (the low-level transport layer that httpx uses internally), not just httpx itself. We now also support this from our pytest plugin, which you configure using addopts:

# pyproject.toml
[tool.pytest.ini_options]
addopts = "-p httpxyz"

With this in place:

  • import httpx returns HTTPXYZ.
  • import httpcore returns httpcorexyz.
  • The httpx_mock fixture remains available and works unchanged.

Removing httpx from the dependency graph entirely (uv)

If you use uv and want to remove httpx from your resolved dependencies entirely, you can override pytest-httpx's declared dependency on httpx with a uv metadata override:

# pyproject.toml
[[tool.uv.dependency-metadata]]
name = "pytest-httpx"
requires-dist = ["httpxyz>=0.31", "pytest==8.*"]

This tells uv to treat pytest-httpx as if it depends on httpxyz rather than httpx, so httpx is never added to the lockfile. At runtime the aliases established by -p httpxyz ensure pytest-httpx still works correctly.