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
{{ message }}
Repository navigation
Commit 02d4b54
Browse filesBrowse the repository at this point in the historyBrowse files
- response.py: Response.text falls back to UTF-8 when the declared
charset is unknown (wraps decode in try/except LookupError); class
docstring now documents `elapsed` as wall-clock seconds.
- tests: add quote-stripping coverage (charset="latin-1" / charset='latin-1'),
add UTF-8 fallback test for unknown charset, and tighten equality
tests with !=-against-variants for both Request and Response.
- Story 1.2 doc: AC8 typo ("frozen on all 4 dataclasses" → "all 5");
status → done.
- docs/deferred-work.md: log 10 items deferred from the review (charset
parser robustness, header/URL/with_* validation, Timeout/Limits guards,
multi-valued params, streaming bodies, json() charset, @Final).
Post-patch: just lint-ci clean, 27 tests pass (+3 new), 100% coverage
retained.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
-**Streaming / async-iterable request bodies** — `body: bytes | None` only. Revisit in transport stories. (`src/httpware/request.py:11`)
14
+
-**`with_headers` / `with_cookie` / `with_extension` merge helpers** — only `with_header` (single) and `with_query` (replace) exist. Story 2.3 will fill this in. (`src/httpware/request.py:20-35`)
15
+
-**`Response.json()` honor declared charset** — `json.loads(bytes)` auto-detects only UTF-8/16/32. Real APIs vary. (`src/httpware/response.py:44-45`)
16
+
-**`@final` to prevent subclassing** — frozen+slots subclassing is fragile. No current subclasser; defer until needed. (`src/httpware/request.py`, `response.py`, `config.py`)
17
+
5
18
## Deferred from: code review of story-1-1 (2026-05-13)
Copy file name to clipboardExpand all lines: docs/stories/1-2-core-data-types.md
+25-3Lines changed: 25 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -3,7 +3,7 @@ story_key: 1-2-core-data-types
3
3
epic: 1
4
4
story: 2
5
5
title: Core data types
6
-
status: ready-for-dev
6
+
status: done
7
7
created: 2026-05-13
8
8
input_documents:
9
9
- docs/prd.md
@@ -197,7 +197,7 @@ Followed TDD per module: write failing tests → confirm collection failure (RED
197
197
-**AC5** ✓ `Timeout`, `Limits`, `ClientConfig` in `src/httpware/config.py` are all `@dataclass(frozen=True, slots=True)`. Defaults exactly match the spec. `ClientConfig.timeout` and `.limits` use `field(default_factory=Timeout)` / `field(default_factory=Limits)` — never bare instances. `Redactor` is intentionally not present (Story 5.3).
198
198
-**AC6** ✓ `src/httpware/__init__.py` re-exports all five via explicit absolute imports; `__all__` is the sorted list `["ClientConfig", "Limits", "Request", "Response", "Timeout"]`. Smoke import succeeds.
199
199
-**AC7** ✓ `just lint-ci` reports zero diagnostics across `eof-fixer`, `ruff format --check`, `ruff check --no-fix`, `ty check`.
200
-
-**AC8** ✓ 24 tests cover every documented case: frozen on all 4 dataclasses, `with_*` immutability + new-instance identity + replace-semantics on `with_header`, equality, independent default mappings, charset case-insensitivity (parametrized over three casings), UTF-8 / latin-1 / missing-charset paths, JSON round-trip, default constructor returns documented defaults. `pytest` exits 0; coverage 100%.
200
+
-**AC8** ✓ 24 tests cover every documented case: frozen on all 5 dataclasses, `with_*` immutability + new-instance identity + replace-semantics on `with_header`, equality, independent default mappings, charset case-insensitivity (parametrized over three casings), UTF-8 / latin-1 / missing-charset paths, JSON round-trip, default constructor returns documented defaults. `pytest` exits 0; coverage 100%.
201
201
202
202
**Deviations from the story spec (worth flagging for reviewer):**
203
203
@@ -229,4 +229,26 @@ Followed TDD per module: write failing tests → confirm collection failure (RED
-[x][Review][Patch]`Response.text` now wraps `bytes.decode` in `try/except LookupError` and falls back to UTF-8 when the declared charset is unknown. [`src/httpware/response.py:42-46`] — applied
239
+
-[x][Review][Patch]`Response` class docstring now documents `elapsed` as wall-clock seconds from request send to response receipt. [`src/httpware/response.py:29-32`] — applied
240
+
-[x][Review][Patch] Added `test_response_text_strips_quotes_around_charset` (parametrized over `"latin-1"` and `'latin-1'`) covering the quote-stripping branch. [`tests/test_response.py`] — applied
241
+
-[x][Review][Patch] Equality tests now also assert `!=` against per-field variants (Request and Response). [`tests/test_request.py`, `tests/test_response.py`] — applied
242
+
-[x][Review][Patch] Completion Notes AC8 — "frozen on all 4 dataclasses" → "all 5". [`docs/stories/1-2-core-data-types.md:200`] — applied
-[x][Review][Defer] Header name/value validation (CR/LF injection, `None`, empty string) on `Request.with_header`. [`src/httpware/request.py:21-23`] — deferred to header-handling story (2.3 or later)
246
+
-[x][Review][Defer] URL validation — `with_url("")` accepts empty, `base_url` has no trailing-slash normalization. [`src/httpware/request.py:25-27`, `src/httpware/config.py:27-33`] — deferred, lands when transport composes URLs
247
+
-[x][Review][Defer]`with_query(None)` is currently accepted and breaks downstream iteration. [`src/httpware/request.py:33-35`] — deferred, type system already says `Mapping[str, str]`
248
+
-[x][Review][Defer]`Timeout` / `Limits` accept negative or zero values silently (no `__post_init__`). [`src/httpware/config.py:10-22`] — deferred, validation lands with transport integration
-[x][Review][Defer]`body: bytes | None` precludes streaming / async-iterable bodies. [`src/httpware/request.py:11`] — deferred, intentional minimal scope; revisit in transport stories
251
+
-[x][Review][Defer] No `with_headers` / `with_cookie` / `with_extension` merge helpers; only `with_header` (single) and `with_query` (replace). [`src/httpware/request.py:20-35`] — deferred to Story 2.3 (merge/case-insensitive helpers)
252
+
-[x][Review][Defer]`Response.json()` ignores declared charset; `json.loads(bytes)` auto-detects only UTF-8/16/32 by BOM. [`src/httpware/response.py:44-45`] — deferred, most APIs are UTF-8; revisit if a real backend breaks
253
+
-[x][Review][Defer]`Request.body` / `Response.content` (bytes) render verbatim in the default dataclass `__repr__`, leaking large payloads / secrets into logs. [`src/httpware/request.py`, `src/httpware/response.py`] — deferred to Story 5.3 (`Redactor`)
254
+
-[x][Review][Defer] No `@final` decorator — frozen+slots subclassing is fragile and the spec doesn't bless it; an explicit `@final` would prevent the footgun. [`src/httpware/request.py`, `src/httpware/response.py`, `src/httpware/config.py`] — deferred, no current subclasser
0 commit comments