Metadata-Version: 2.4
Name: httpx-debug
Version: 0.1.0
Summary: Failure-aware curl output for httpx: reproduce any (failing) request as a paste-able, secret-redacted curl command — printed or copied to your clipboard, even over SSH.
Project-URL: Homepage, https://github.com/mayurrawte/httpx-debug
Author: Mayur Rawte
License-Expression: MIT
License-File: LICENSE
Keywords: clipboard,curl,debug,debugging,devtools,httpx
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Software Development :: Debuggers
Requires-Python: >=3.9
Requires-Dist: httpx>=0.23
Description-Content-Type: text/markdown

# httpx-debug

**Reproduce any httpx request — especially the failing ones — as a paste-able curl command.**

Stop print-debugging your HTTP calls:

```python
# before
print(self._headers())
print(params)
print(self.base_url)
print(path)

# after
import httpx_debug
httpx_debug.attach(client)   # failing requests print a ready-to-paste curl command
```

When a request fails (4xx / 5xx / timeout / connection error), `httpx-debug` prints — and optionally copies to your clipboard — the exact `curl` command that reproduces it, with your secrets redacted:

```
# httpx-debug: 401 Unauthorized ← POST https://api.example.com/v1/users
curl -X POST -H 'Authorization: <redacted>' -H 'Content-Type: application/json' -d '{"name": "mayur"}' https://api.example.com/v1/users
```

## Why not curlify?

[curlify](https://pypi.org/project/curlify/) and friends convert a request object to curl — and that's it. `httpx-debug` is the debugging layer around that:

- **Failure-aware** — attach once, hear about it only when something breaks. No call-site changes.
- **Secret redaction by default** — `Authorization`, `Cookie`, `X-API-Key`, … become `<redacted>` so you can paste the command into an issue or Slack without leaking credentials.
- **Clipboard, even over SSH** — uses `pbcopy`/`xclip`/`wl-copy`/`clip` locally and falls back to the OSC 52 terminal escape, which copies to your *local* clipboard from a remote shell.
- **Async-first** — works identically with `httpx.Client` and `httpx.AsyncClient`.
- **Sees exceptions too** — the drop-in `httpx_debug.Client` / `AsyncClient` also capture timeouts and connection errors, not just error status codes.

Works anywhere httpx does — including under the **OpenAI and Anthropic SDKs**, which accept a custom `http_client`. Ever wondered what your LLM call actually sends over the wire?

```python
import httpx, httpx_debug, openai

http_client = httpx.Client()
httpx_debug.attach(http_client, on="all")
client = openai.OpenAI(http_client=http_client)
```

## Install

```bash
pip install httpx-debug
```

Zero dependencies beyond httpx itself.

## Usage

### Convert a request to curl

```python
import httpx_debug

curl = httpx_debug.to_curl(request)                 # secrets redacted
curl = httpx_debug.to_curl(request, redact=False)   # raw
```

### Copy to clipboard

```python
httpx_debug.copy(request)   # returns True if a clipboard was reachable
```

### Attach to any client (yours or an SDK's)

```python
client = httpx.AsyncClient()
httpx_debug.attach(client)              # print curl for 4xx/5xx responses
httpx_debug.attach(client, on="all")    # ...or for every request
httpx_debug.attach(client, copy=True)   # also copy to clipboard
httpx_debug.detach(client)              # remove
```

`attach` uses httpx event hooks, so it can't observe transport exceptions (timeouts, DNS failures). For those, use the drop-in clients:

### Drop-in clients (also capture timeouts & connection errors)

```python
client = httpx_debug.AsyncClient(base_url="https://api.example.com")
# behaves exactly like httpx.AsyncClient, plus curl output on any failure
```

### Options

| Option | Default | |
|---|---|---|
| `on` | `"error"` | `"error"` (status ≥ 400 + exceptions) or `"all"` |
| `copy` | `False` | also copy the curl command to the clipboard |
| `redact` | `True` | replace sensitive header values with `<redacted>` |
| `copy_redact` | follows `redact` | redaction for the *clipboard* copy specifically |
| `redact_headers` | built-in set | which headers count as sensitive (lowercase) |
| `file` | `sys.stderr` | where to print |

On the drop-in clients the same options are prefixed: `debug_on`, `debug_copy`, `debug_redact`, `debug_copy_redact`, `debug_redact_headers`, `debug_file`.

### Print safe, copy replayable

The printed command ends up in logs and screenshots — keep it redacted. The clipboard's job is *replaying* the request, which needs real credentials:

```python
httpx_debug.attach(client, copy=True, copy_redact=False)
# terminal: Authorization: <redacted>     ← safe to paste in an issue
# clipboard: Authorization: Bearer sk-…   ← actually runs
```

Raw copying is always opt-in — remember clipboard managers keep history.

## Notes

- Streamed request bodies are never consumed by httpx-debug; the curl output notes `# request body was streamed and is not shown`.
- Non-UTF-8 bodies are omitted with a byte-count comment.
- Debug tooling must never break your app: clipboard and hook failures are swallowed.

## Roadmap

- pytest plugin: show the curl of the last failed request under the traceback
- response capture alongside the request
- `requests` support
- a Node.js sibling for server-side `fetch`

## License

MIT
