You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+7-7Lines changed: 7 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -7,7 +7,7 @@
7
7
8
8
**Async HTTP client framework for Python.**
9
9
10
-
`httpware` is a thin opinionated wrapper around `httpx2`. It re-exports `httpx2.Request`/`httpx2.Response`, adds a middleware chain composed at client construction, supports opt-in typed response decoding (pydantic and msgspec are both extras), and raises a status-keyed exception tree automatically on 4xx/5xx. It also ships a small resilience suite — `Retry` middleware with a Finagle-style `RetryBudget`, plus a `Bulkhead` concurrency limiter — under `httpware.middleware.resilience`.
10
+
`httpware` is a thin opinionated wrapper around `httpx2`. It re-exports `httpx2.Request`/`httpx2.Response`, adds a middleware chain composed at client construction, supports opt-in typed response decoding (pydantic and msgspec are both extras), and raises a status-keyed exception tree automatically on 4xx/5xx. It also ships a small resilience suite — `AsyncRetry` middleware with a Finagle-style `RetryBudget`, plus an `AsyncBulkhead` concurrency limiter — under `httpware.middleware.resilience`.
11
11
12
12
> **Status:** Pre-1.0. Public API is subject to change between minor releases until v1.0.
13
13
@@ -44,18 +44,18 @@ async def main() -> None:
44
44
45
45
### With resilience middleware
46
46
47
-
Compose resilience middleware at construction; `Bulkhead` goes outside `Retry` so one slot covers all retry attempts.
47
+
Compose resilience middleware at construction; `AsyncBulkhead` goes outside `AsyncRetry` so one slot covers all retry attempts.
48
48
49
49
```python
50
-
from httpware import AsyncClient, Bulkhead, Retry
50
+
from httpware import AsyncClient, AsyncBulkhead, AsyncRetry
51
51
52
52
53
53
asyncdefmain() -> None:
54
54
asyncwith AsyncClient(
55
55
base_url="https://api.example.com",
56
56
middleware=[
57
-
Bulkhead(max_concurrent=10), # cap total in-flight
user =await client.get("/users/1", response_model=User)
@@ -80,15 +80,15 @@ async def main() -> None:
80
80
81
81
`stream()` auto-raises `StatusError` subclasses on 4xx/5xx with the response body pre-read, so `exc.response.content` is accessible from the caught exception.
82
82
83
-
It does NOT pass through the middleware chain: `Retry`, `Bulkhead`, and any custom middleware are bypassed. (Retry separately refuses to retry any request — stream or non-stream — whose body was an async-iterable, since streams can't replay across attempts.)
83
+
It does NOT pass through the middleware chain: `AsyncRetry`, `AsyncBulkhead`, and any custom middleware are bypassed. (AsyncRetry separately refuses to retry any request — stream or non-stream — whose body was an async-iterable, since streams can't replay across attempts.)
84
84
85
85
## Errors
86
86
87
87
All 4xx/5xx responses raise typed exceptions automatically: `NotFoundError`, `ServiceUnavailableError`, `RateLimitedError`, etc. — all subclasses of `httpware.StatusError`. Transport-layer transient failures raise `NetworkError`; the resilience middleware raise `RetryBudgetExhaustedError` and `BulkheadFullError`. Everything inherits `httpware.ClientError`.
88
88
89
89
## Observability
90
90
91
-
`Retry` and `Bulkhead` emit operational events via two channels — stdlib `logging` records (always on) and OpenTelemetry span events (when `opentelemetry-api` is installed).
91
+
`AsyncRetry` and `AsyncBulkhead` emit operational events via two channels — stdlib `logging` records (always on) and OpenTelemetry span events (when `opentelemetry-api` is installed).
92
92
93
93
Logger names (`httpware.retry`, `httpware.bulkhead`) and event names (`retry.giving_up`, `retry.budget_refused`, `retry.streaming_refused`, `bulkhead.rejected`) are the stable public contract.
Copy file name to clipboardExpand all lines: docs/index.md
+9-9Lines changed: 9 additions & 9 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,6 +1,6 @@
1
1
# httpware
2
2
3
-
A Python async HTTP client framework for building resilient service clients. `httpware` is a thin opinionated wrapper around `httpx2` — it re-exports `httpx2.Request`/`httpx2.Response` as the public request/response surface, adds a middleware chain (with a built-in resilience suite: `Retry` + `RetryBudget`, `Bulkhead`), opt-in typed response decoding, and a status-keyed exception tree raised automatically on 4xx/5xx.
3
+
A Python async HTTP client framework for building resilient service clients. `httpware` is a thin opinionated wrapper around `httpx2` — it re-exports `httpx2.Request`/`httpx2.Response` as the public request/response surface, adds a middleware chain (with a built-in resilience suite: `AsyncRetry` + `RetryBudget`, `AsyncBulkhead`), opt-in typed response decoding, and a status-keyed exception tree raised automatically on 4xx/5xx.
4
4
5
5
> **Status:** Pre-1.0. Public API is subject to change between minor releases until v1.0.
6
6
@@ -42,18 +42,18 @@ asyncio.run(main())
42
42
43
43
### With resilience middleware
44
44
45
-
Compose resilience middleware at construction; `Bulkhead` goes outside `Retry` so one slot covers all retry attempts.
45
+
Compose resilience middleware at construction; `AsyncBulkhead` goes outside `AsyncRetry` so one slot covers all retry attempts.
46
46
47
47
```python
48
-
from httpware import AsyncClient, Bulkhead, Retry
48
+
from httpware import AsyncClient, AsyncBulkhead, AsyncRetry
49
49
50
50
51
51
asyncdefmain() -> None:
52
52
asyncwith AsyncClient(
53
53
base_url="https://api.example.com",
54
54
middleware=[
55
-
Bulkhead(max_concurrent=10), # cap total in-flight
user =await client.get("/users/1", response_model=User)
@@ -76,15 +76,15 @@ async def main() -> None:
76
76
77
77
`stream()` auto-raises `StatusError` subclasses on 4xx/5xx with the response body pre-read, so `exc.response.content` is accessible from the caught exception.
78
78
79
-
It does NOT pass through the middleware chain: `Retry`, `Bulkhead`, and any custom middleware are bypassed. (Retry separately refuses to retry any request — stream or non-stream — whose body was an async-iterable, since streams can't replay across attempts.)
79
+
It does NOT pass through the middleware chain: `AsyncRetry`, `AsyncBulkhead`, and any custom middleware are bypassed. (AsyncRetry separately refuses to retry any request — stream or non-stream — whose body was an async-iterable, since streams can't replay across attempts.)
80
80
81
81
## Errors
82
82
83
83
All 4xx/5xx responses raise typed exceptions automatically: `NotFoundError`, `ServiceUnavailableError`, `RateLimitedError`, etc. — all subclasses of `httpware.StatusError`. Transport-layer transient failures raise `NetworkError`; the resilience middleware raise `RetryBudgetExhaustedError` and `BulkheadFullError`. Everything inherits `httpware.ClientError`.
84
84
85
85
## Observability
86
86
87
-
`Retry` and `Bulkhead` emit operational events via two channels — stdlib `logging` records (always on) and OpenTelemetry span events (when `opentelemetry-api` is installed).
87
+
`AsyncRetry` and `AsyncBulkhead` emit operational events via two channels — stdlib `logging` records (always on) and OpenTelemetry span events (when `opentelemetry-api` is installed).
88
88
89
89
Logger names (`httpware.retry`, `httpware.bulkhead`) and event names (`retry.giving_up`, `retry.budget_refused`, `retry.streaming_refused`, `bulkhead.rejected`) are the stable public contract.
90
90
@@ -106,8 +106,8 @@ When installed, `_emit_event` calls `trace.get_current_span().add_event(name, at
106
106
107
107
## Where to go next
108
108
109
-
-**[Resilience reference](resilience.md)** — every parameter on `Retry`, `RetryBudget`, and `Bulkhead`; the retry-rule matrix; Retry-After parsing; budget sharing.
110
-
-**[Middleware guide](middleware.md)** — write your own middleware. Covers the Middleware Protocol, the phase decorators, a worked Request-ID propagation example, and OpenTelemetry wiring.
109
+
-**[Resilience reference](resilience.md)** — every parameter on `AsyncRetry`, `RetryBudget`, and `AsyncBulkhead`; the retry-rule matrix; Retry-After parsing; budget sharing.
110
+
-**[Middleware guide](middleware.md)** — write your own middleware. Covers the AsyncMiddleware Protocol, the phase decorators, a worked Request-ID propagation example, and OpenTelemetry wiring.
111
111
-**[Errors reference](errors.md)** — the full exception tree, catching strategies, `exc.response.*` access pattern.
112
112
-**[Testing guide](testing.md)** — mock-transport injection pattern for testing code that uses `httpware`.
113
113
-**[Recipes](recipes/modern-di.md)** — wiring `AsyncClient` into a `modern-di` container.
0 commit comments