Skip to content

v0.2: thin httpx2 wrapper rewrite#20

Merged
lesnik512 merged 20 commits into
mainfrom
feat/v0.2-thin-httpx2-wrapper
Jun 3, 2026
Merged

v0.2: thin httpx2 wrapper rewrite#20
lesnik512 merged 20 commits into
mainfrom
feat/v0.2-thin-httpx2-wrapper

Conversation

@lesnik512

Copy link
Copy Markdown
Member

Summary

Reframes httpware as a thin opinionated wrapper around httpx2. Per the spec at planning/specs/2026-06-03-thin-httpx2-wrapper-design.md.

What stays:

  • AsyncClient with get/post/put/patch/delete/head/options/request (response_model overloads → typed decoding).
  • Middleware chain composed at __init__ (retyped on httpx2.Request/httpx2.Response), @before_request / @after_response / @on_error decorators.
  • Status-keyed exception tree (NotFoundError, RateLimitedError, ...) auto-raised on 4xx/5xx.
  • Pydantic (default) + msgspec (opt-in) decoders.

What's gone:

  • Custom Request / Response / StreamResponse / Limits / Timeout / ClientConfig value types — use httpx2.* directly.
  • Transport protocol + Httpx2Transport + RecordedTransport — tests inject httpx2.MockTransport via the new httpx2_client= kwarg.
  • Auth coercion layer — pass httpx2.Auth (e.g., httpx2.BasicAuth) directly.
  • with_options view pattern.
  • The "no httpx2 leakage" CI invariant (and its test) — httpx2.Request/httpx2.Response are now part of the public surface.

Numbers: 18 commits, +1537/−3986 net (~2.4k LOC deleted). Five protocol seams collapse to three. 27-story roadmap shrinks to ~16. 119 tests pass at 100% coverage.

Version bumped to 0.2.0; release notes drafted at planning/specs/2026-06-03-release-notes-0.2.0.md.

Test plan

  • just test — 119 passed, 100% coverage.
  • just lint-ci — eof-fixer / ruff format / ruff check / ty all clean.
  • import httpware in a clean subprocess does not pull msgspec (optional-extras isolation).
  • Public-API test asserts no 0.1 names leak (Request, Response, Transport, etc.).
  • Migration example in release notes verified by inspection.

🤖 Generated with Claude Code

lesnik512 and others added 20 commits June 3, 2026 23:03
Twenty-three TDD tasks covering tear-down, errors, middleware, AsyncClient
construction/send/per-method/response_model/middleware-wiring/lifecycle/
typing, docs (CLAUDE.md, engineering.md, deferred-work sweep), version bump,
release notes, and final integration sweep. Single structural PR per spec.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Remove Request/Response/Config value types, Transport protocol,
Httpx2Transport, RecordedTransport, auth coercion, and the no-leakage
CI invariant. Decoders survive. New AsyncClient/errors/middleware land
in subsequent commits.
…coverage gaps

Wrap IPv6 hostnames in brackets after urlsplit strips them, preventing
malformed URLs like https://::1:8080/x. Add four tests covering: port
preservation, username-only userinfo, at-sign in query string only, and
IPv6 host with credentials.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace three `# noqa: PLR2004` suppressions with named constants. Matches
the constant-extraction pattern already used in test_errors.py.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
In client.py: drop _CLIENT_ERROR_LOW/_SERVER_ERROR_LOW/_SERVER_ERROR_HIGH
in favor of HTTPStatus.BAD_REQUEST/INTERNAL_SERVER_ERROR direct comparison.
600 is the only literal that stays (no stdlib equivalent for the synthetic
5xx upper bound); single noqa with justification, same pattern as the 0.1
transport.

In tests/test_middleware.py: replace _STATUS_OK/_STATUS_UPGRADED/
_STATUS_SERVICE_UNAVAILABLE with HTTPStatus.OK / HTTPStatus.IM_USED /
HTTPStatus.SERVICE_UNAVAILABLE.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add AsyncClient.send() (with response_model overloads) and build_request()
delegating to the wrapped httpx2.AsyncClient. Tests cover 2xx passthrough,
typed 4xx/5xx subclass raises, fallback to ClientStatusError/ServerStatusError,
3xx passthrough, timeout→TimeoutError, connect→TransportError, and
closed-client→TransportError.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…options/request)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…lient

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add targeted tests for uncovered branches in client.py (cookies/limits/auth
kwargs, InvalidURL exception mapping, RuntimeError re-raise, and all
_request_with_body optional-kwarg branches) and in middleware/__init__.py
(__repr__ methods on before_request/after_response/on_error wrappers).
Apply # pragma: no cover to test-helper methods that are structural
placeholders never invoked (protocol-check __call__, default-arg guards,
and the unreachable swallow_all body in the CancelledError test).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The engineering doc is a planning/architecture reference (alongside
specs/, plans/, deferred-work.md), not user-facing documentation in
docs/. Move it under planning/ and update the live references in
CLAUDE.md and the v0.2 spec.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@lesnik512 lesnik512 merged commit 3edef19 into main Jun 3, 2026
5 checks passed
@lesnik512 lesnik512 deleted the feat/v0.2-thin-httpx2-wrapper branch June 3, 2026 21:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant