Skip to content

Commit ffdc25f

Browse files
authored
Merge pull request #12 from modern-python/story/1-7-asyncclient
feat(story-1.7): AsyncClient — the v0.1.0 public surface
2 parents 4117384 + 44d216d commit ffdc25f

15 files changed

Lines changed: 3570 additions & 2 deletions

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
2323
- Phase-shortcut decorators `@before_request`, `@after_response`, `@on_error` for lifecycle hooks without authoring a full `Middleware` class. `@on_error` catches `Exception` only (so `asyncio.CancelledError` propagates); its handler may return a `Response` to recover or `None` to re-raise (Story 2.2).
2424
- Request and Response immutability helper expansion: `Request.with_headers`, `with_cookie`, `with_cookies`, `with_extension`, `with_extensions`; `Response.with_headers`, `with_status`. Plural helpers merge mappings (incoming keys override existing); singular helpers add or replace a single entry. No validation, no header-key normalization — matches the existing `with_header` semantics from Story 1.2 (Story 2.3).
2525
- `MsgspecDecoder` opt-in `ResponseDecoder` adapter behind the `[msgspec]` extra at `httpware.decoders.msgspec`; `msgspec.json.decode(content, type=model)` in a single C-level parse pass. Accepts `msgspec.Struct`, dataclasses, attrs, NamedTuples, TypedDicts, and builtin/container types as `model` (pydantic models use `PydanticDecoder` instead). `msgspec.ValidationError` and `msgspec.DecodeError` propagate unchanged. Module import is safe without the extra (gated by `httpware._internal.import_checker.is_msgspec_installed`); only `MsgspecDecoder()` construction raises `ImportError` with an install hint when the extra is missing. `import httpware` does NOT eagerly load `msgspec``MsgspecDecoder` is reachable only via `from httpware.decoders.msgspec import MsgspecDecoder` (Story 1.6).
26+
- `AsyncClient` — the v0.1.0 public surface. Construct with keyword-only `base_url`, `default_headers`, `default_query`, `timeout` (accepts `Timeout` instance, float seconds, or `None`), `limits`, `transport` (defaults to `Httpx2Transport`), `decoder` (defaults to `PydanticDecoder`), and `middleware` (`Sequence[Middleware]`, composed via `httpware._internal.chain.compose` at construction). Eight HTTP method shortcuts (`get`, `post`, `put`, `patch`, `delete`, `head`, `options`, `request`) with `@typing.overload`-based `response_model` typing — passing `response_model=type[T]` returns `T`, otherwise `Response`. Per-call overrides for `headers`, `params`, `cookies`, `timeout`; body params `json` (auto-encoded with `Content-Type: application/json`, typed as `JsonValue` recursive alias) and `content` (raw bytes; mutually exclusive). `base_url` joins with the path using an httpx-style prefix; absolute URLs (`http(s)://`) bypass. `from_url(base_url, **kwargs)` classmethod factory. Async context-manager lifecycle: the original client owns the transport and closes it on `__aexit__`; views returned by `with_options(**overrides)` share the transport and are no-ops on close. `with_options` accepts a keyword allowlist (`base_url`, `default_headers`, `default_query`, `timeout`, `decoder`, `middleware`); `limits` and `transport` are not overridable. Out of scope and deferred: `auth=` (Story 2.4), `data=`/`files=` body params, transport reference-counting, streaming (Epic 4), observability (Epic 5) (Story 1.7).
2627

2728
[Unreleased]: https://github.com/modern-python/httpware/commits/main

0 commit comments

Comments
 (0)