Skip to content

0.8.0 — Sync Client + httpx2-aligned naming

Choose a tag to compare

@lesnik512 lesnik512 released this 07 Jun 18:27
d2c24f2

httpware 0.8.0 — Sync Client + httpx2-aligned naming

Breaking release. Renames the async middleware surface to use the Async*/async_* prefix (matching httpx2's convention), drops Retry(attempt_timeout=...), and adds a fully-featured sync Client.

If you have existing async code, migration is one mechanical pass through your imports — see "Breaking changes" below.

What's new

  • Sync Client. Full parity with AsyncClient: typed response decoding, middleware chain, Retry + Bulkhead, stream() context manager, lifecycle (with + close()), and httpx2.Client injection. Designed for CLI tools, scripts, Django sync views, Jupyter, and threaded service workers.
  • Sync Middleware + Next + decorators. from httpware import Middleware, Next, before_request, after_response, on_error. Same protocol shape as async; bodies are sync.
  • Sync Retry and Bulkhead. Same resilience semantics as their async siblings, with time.sleep and threading.Semaphore. Sync Retry shares RetryBudget with async — one instance is safe across both worlds.
  • RetryBudget is now thread-safe via an internal threading.Lock. Async users see no behavioral difference; the overhead is invisible (~50–100 ns per op).
  • Shared helpers in _internal/. map_httpx2_exception, _raise_on_status_error, the streaming-body marker, and the body predicates moved to _internal/exception_mapping.py and _internal/status.py. No public-API change other than the exports listed below.

Breaking changes

Renames

Old name New name
httpware.Middleware httpware.AsyncMiddleware
httpware.Next httpware.AsyncNext
httpware.Retry httpware.AsyncRetry
httpware.Bulkhead httpware.AsyncBulkhead
httpware.before_request httpware.async_before_request
httpware.after_response httpware.async_after_response
httpware.on_error httpware.async_on_error
httpware.middleware.chain.compose httpware.middleware.chain.compose_async

Removals

  • Retry(attempt_timeout=...) / AsyncRetry(attempt_timeout=...) is removed. It used asyncio.timeout to bound the whole attempt as a structured cancellation; this had no clean sync equivalent and is mostly covered by httpx2.Timeout (per-phase I/O bounds) for typical use cases. Users who genuinely need whole-attempt wall-clock bounds can compose their own timeout middleware.

New names that previously meant something else

The unprefixed Middleware, Next, Retry, Bulkhead, before_request, after_response, on_error now refer to sync types. Code that imports them and expects async behavior will break at type-check time (or at the first await site).

Migration

A one-pass sed/regex covers most of the work:

# in your project root:
git ls-files '*.py' | xargs sed -i.bak \
  -e 's/from httpware import \(.*\)\bMiddleware\b/from httpware import \1AsyncMiddleware/g' \
  -e 's/from httpware import \(.*\)\bNext\b/from httpware import \1AsyncNext/g' \
  -e 's/from httpware import \(.*\)\bRetry\b/from httpware import \1AsyncRetry/g' \
  -e 's/from httpware import \(.*\)\bBulkhead\b/from httpware import \1AsyncBulkhead/g' \
  -e 's/from httpware import \(.*\)\bbefore_request\b/from httpware import \1async_before_request/g' \
  -e 's/from httpware import \(.*\)\bafter_response\b/from httpware import \1async_after_response/g' \
  -e 's/from httpware import \(.*\)\bon_error\b/from httpware import \1async_on_error/g'

Then update the symbol references in the file bodies (your type checker will guide you). If you were using Retry(attempt_timeout=...), remove the kwarg and rely on httpx2.Timeout or write a minimal timeout middleware.

References