Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: Publish Package

on:
release:
types:
- published

jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: extractions/setup-just@v2
- uses: astral-sh/setup-uv@v3
- run: just publish
env:
PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
29 changes: 0 additions & 29 deletions CHANGELOG.md

This file was deleted.

3 changes: 1 addition & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ just test # pytest with coverage
2. **Branch from `main`**, use a descriptive name (`feat/retry-budget-jitter`, `fix/transport-cancel-leak`).
3. **Run `just lint` and `just test`** locally before pushing. CI will reject changes that fail either.
4. **Add tests** for any code change. Property-based tests (via Hypothesis) are required for concurrency-sensitive code (retry budget, bulkhead, retry interleaving).
5. **Update `CHANGELOG.md`** in the `Unreleased` section.
6. **Open a pull request** against `main`. PR titles use conventional-commits style (`feat:`, `fix:`, `docs:`, `refactor:`, `test:`, `chore:`).
5. **Open a pull request** against `main`. PR titles use conventional-commits style (`feat:`, `fix:`, `docs:`, `refactor:`, `test:`, `chore:`).

## Code style

Expand Down
37 changes: 15 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
[![Python versions](https://img.shields.io/pypi/pyversions/httpware.svg)](https://pypi.org/project/httpware/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

**Resilience-first async HTTP client framework for Python.**
**Async HTTP client framework for Python.**

`httpware` is to Python what Polly is to .NET and resilience4j is to the JVM — a canonical resilience-first HTTP framework. The public API is transport-agnostic (the underlying client is `httpx2` by default, sitting behind a swappable `Transport` protocol). Retries, timeouts, bulkheads, and a Finagle-style **retry budget** ship as composable middleware. Tests use a `RecordedTransport` and never see the underlying client.
`httpware` is a typed, async HTTP client library built on `httpx2` with a protocol-based seam so the transport is swappable. Middleware composes via an onion model. Pydantic and msgspec response decoding ship out of the box. `RecordedTransport` replaces respx for transport-level tests.

> **Status:** Pre-1.0. Public API is subject to change between minor releases until v1.0. See [CHANGELOG.md](./CHANGELOG.md).
> **Status:** Pre-1.0 (0.1.0 alpha). Public API is subject to change between minor releases until v1.0. Resilience middleware (retry / timeout / bulkhead), streaming, and observability are not yet shipped — track progress on GitHub.

## Install

Expand All @@ -20,12 +20,11 @@ pip install httpware
Optional extras:

```bash
pip install httpware[msgspec] # msgspec ResponseDecoder
pip install httpware[otel] # OpenTelemetry instrumentation
pip install httpware[niquests] # niquests transport
pip install httpware[all] # all of the above
pip install httpware[msgspec] # MsgspecDecoder
```

(`otel`, `niquests`, and `all` extras are declared but their integrations have not shipped yet.)

## Quickstart

```python
Expand All @@ -44,25 +43,19 @@ async def main() -> None:
print(user.name)
```

## Highlights

- **Transport-agnostic API.** No `httpx2` symbols leak through `httpware`. Swap to a different backend with one constructor argument.
- **Onion middleware** with phase shortcuts (`@before_request`, `@after_response`, `@on_error`). Built-in middleware: `Retry`, `RetryBudget`, `Bulkhead`, `Timeout`, `Observability`.
- **Retry budget by default** — token-bucket admission control (Finagle defaults). Caps retry storms before they happen.
- **Pluggable validation.** Default pydantic decoder with cached `TypeAdapter`; msgspec decoder via extras; bring your own.
- **`RecordedTransport` for tests.** A 3-line fixture replaces respx routes and transport-level mocking.
- **Status-keyed exceptions** with plain fields (`status: int`, `body: bytes`, `headers`, `json`). No transport exception types in user code.
- **First-class OpenTelemetry** instrumentation via `httpware[otel]`.

## Documentation
## What ships in 0.1.0

Full docs (in progress): https://httpware.readthedocs.io
- **`AsyncClient`** — eight HTTP method shortcuts (`get`, `post`, `put`, `patch`, `delete`, `head`, `options`, `request`) with typed `response_model` overloads; per-call overrides for `headers`, `params`, `cookies`, `timeout`, `json`, `content`; httpx-style `base_url` join; `with_options(...)` returns a view sharing the same transport.
- **Transport-agnostic seam.** `httpx2` is confined to `httpware.transports.httpx2.Httpx2Transport`. Implement the `Transport` protocol to swap backends.
- **Middleware foundation.** `Middleware` protocol, `Next` type alias, recursive-closure `compose()` chain composition, and phase decorators (`@before_request`, `@after_response`, `@on_error`).
- **Pluggable response decoding.** `PydanticDecoder` (default) with cached `TypeAdapter`; `MsgspecDecoder` via `httpware[msgspec]`.
- **`RecordedTransport`** — built-in test double with a route table, observed-request list, and `aclose_calls` counter.
- **Status-keyed exception hierarchy** — `StatusError`, 4xx / 5xx subclasses, plain typed fields (`status: int`, `body: bytes`, `headers`, `json`, `request_method`, `request_url`). Pickleable; userinfo redacted in `__repr__`.
- **No `httpx2` exception types** leak through `httpware`. The transport seam maps them to `httpware` exceptions.

## Part of `modern-python`

Browse the full list of templates and libraries in
[`modern-python`](https://github.com/modern-python) — see the org profile for the
categorized index.
Browse the full list of templates and libraries in [`modern-python`](https://github.com/modern-python) — see the org profile for the categorized index.

## License

Expand Down
Loading
Loading