Metadata-Version: 2.4
Name: httpx2-audit
Version: 0.1.0
Summary: Find what breaks when your Python stack moves from httpx to httpx2: static audit, import/dependency codemod and a mixed-stack/TLS environment report.
Author-email: agent-one <rafal.baranowski.iot@gmail.com>
License: MIT
Project-URL: Homepage, https://github.com/rafal-one/httpx2-audit
Keywords: httpx,httpx2,migration,codemod,audit,openai,anthropic,respx,pytest-httpx
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Software Development :: Quality Assurance
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# httpx2-audit

Find what breaks when your Python stack moves from **httpx** to **httpx2** (the pydantic fork; openai 3.x,
anthropic 1.x, Starlette, the MCP SDK and huggingface_hub already switched). Pure stdlib, Python 3.9+
(it analyses code, it does not import httpx), no network except the optional TLS probe.

Unofficial community tool, not affiliated with httpx, httpx2 or pydantic. State as of 2026-10-18.

## Why

The move breaks things *silently*: `import httpx` fails once an SDK stops pulling it in; `respx` and
`pytest-httpx` mocks stop intercepting, so tests hit the live API; `except httpx.ConnectError` no longer
catches errors raised by httpx2 code; httpx clients passed as `http_client=` are the wrong type; TLS switches
from the certifi bundle to the OS trust store.

```
pip install httpx2-audit     # or: pip install git+https://github.com/rafal-one/httpx2-audit
```

## `check`: static audit

```
$ httpx2-audit check src/
src/app.py:1:1: HX001 imports httpx; httpx2 is a separate package with its own module name [info]
src/app.py:4:36: HX004 httpx object or http_client passed on; an httpx2-based SDK needs httpx2 objects (http_client) [warning]
src/app.py:7:8: HX003 except clause names an httpx exception; errors raised by httpx2-based code are not caught (httpx.ConnectError) [warning]
```

| Rule | Severity | Meaning |
|---|---|---|
| HX001 | info | `import httpx` / `from httpx import ...` |
| HX002 | error | httpx imported but not declared in pyproject/requirements (transitive install is gone) |
| HX003 | warning | `except httpx.X` (also aliases and tuples) |
| HX004 | warning | `http_client=` or any httpx object (`httpx.Timeout`, `httpx.Limits`, ...) passed to a non-httpx call |
| HX005 | warning | `isinstance`/`issubclass` against httpx classes |
| HX006 | warning | `import httpcore` (httpx2 uses httpcore2) |
| HX007 | warning | `respx` / `pytest_httpx` imports: they do not mock httpx2 |
| HX008 | info | `certifi.where()` passed explicitly while httpx is in use |
| HX010 | warning | file imports both httpx and httpx2 |

Options: `--format text|json|sarif` (SARIF for GitHub code scanning), `--min-severity`, `--fail-on
info|warning|error|never` (default `warning`), `-q` (no footer). Exit codes: `0` nothing at or above
`--fail-on`, `1` findings, `2` bad usage or missing path. Directories like `.venv`, `node_modules`, `build`
are skipped; files that do not parse are reported on stderr and skipped. Imports already wrapped in
`try: import httpx2 ... except ImportError: import httpx` are treated as handled.

CI: `httpx2-audit check . --format sarif > audit.sarif`, or just `httpx2-audit check . --fail-on error`.

## `fix`: import and dependency codemod

Dry run by default (prints a unified diff); `--write` applies it.

```
httpx2-audit fix .                # diff
httpx2-audit fix . --write        # apply
httpx2-audit fix . --dual --write # libraries: try httpx2, fall back to httpx
```

* `import httpx` -> `import httpx2 as httpx` (so the rest of the file keeps working), `from httpx import X`
  -> `from httpx2 import X`; same for `httpcore` -> `httpcore2` and `pytest_httpx` -> `pytest_httpx2`.
* `requirements*.txt` and `pyproject.toml` requirement strings: `httpx` -> `httpx2>=2.0`, `httpcore`,
  `pytest-httpx` (extras and markers are kept).
* `--dual` wraps each import in a `try/except ImportError` and leaves dependencies alone.
* Private modules (`httpx._models`) and dotted imports are reported as `manual:` and left untouched.
* It does not touch `except` clauses, `respx` mocks or TLS settings; run `check` afterwards. httpx2 needs Python 3.10+.

## `env`: mixed stacks and TLS

```
$ httpx2-audit env --probe api.openai.com --probe intranet.corp:8443
installed:
  httpx              0.28.1
  httpx2             2.14.0
depend on httpx:  respx
depend on httpx2: pytest-httpx2
tls intranet.corp:8443: openssl-default=ok, certifi=FAIL: CERTIFICATE_VERIFY_FAILED, truststore=ok
WARNING: TLS trust differs for intranet.corp:8443: ...
```

Lists the relevant installed distributions, which installed packages require httpx or httpx2 (a mixed
environment cannot pass clients between them), and compares what the system store, certifi and truststore
(if installed) accept for the hosts you probe. Exit code `1` when warnings are printed.

## Development

`pytest` (37 tests). Tested on Python 3.9 and 3.14; clean installs of wheel and sdist checked.

## Donate

If this saved your migration a day: https://paypal.me/RafalBaranowski

MIT licensed.
