Skip to content

feat(story-2.3): Request/Response immutability helper expansion#10

Merged
lesnik512 merged 7 commits into
mainfrom
story/2-3-request-immutability-helpers
May 31, 2026
Merged

feat(story-2.3): Request/Response immutability helper expansion#10
lesnik512 merged 7 commits into
mainfrom
story/2-3-request-immutability-helpers

Conversation

@lesnik512
Copy link
Copy Markdown
Member

Summary

  • Adds 5 helpers to Request: with_headers (merge), with_cookie / with_cookies (singular add/replace + plural merge), with_extension / with_extensions (same pattern, value type Any).
  • Adds 2 helpers to Response: with_headers (merge) and with_status (replace).
  • Convention: singular with_X(name, value) adds or replaces one entry; plural with_Xs(items) merges with incoming keys overriding.
  • Existing helpers untouched, including with_query's REPLACE semantics. The asymmetry vs with_headers MERGE is justified by usage patterns, HTTP semantics, and the singular-helper escape hatch (full rationale in the spec).
  • 14 new tests (10 on Request, 4 on Response); 100% line coverage on new helpers; just test shows 198 passed.

Out of scope (subsequent stories): auth coercion (2-4), AsyncClient wiring (2-5), StreamResponse.with_* (Story 4-1), case-insensitive header keys (existing deferred-work entry).

Spec + plan: docs/superpowers/specs/2026-05-31-request-immutability-helpers-design.md, docs/superpowers/plans/2026-05-31-request-immutability-helpers-plan.md.

Test plan

  • just test — 198 passed, 1 deselected, 100% line coverage.
  • just lint-ci clean.
  • tests/test_no_httpx2_leakage.py still passes.
  • CI green on all matrix entries (3.11/3.12/3.13/3.14 + lint).

🤖 Generated with Claude Code

lesnik512 and others added 7 commits May 31, 2026 18:23
Adds 5 helpers to Request (with_headers, with_cookie, with_cookies,
with_extension, with_extensions) and 2 to Response (with_headers,
with_status) so middleware can rewrite requests and responses
ergonomically without falling back to dataclasses.replace.

Pragmatic scope per engineering.md roadmap: archive's list plus
cookies and extensions. Existing helpers (with_header, with_url,
with_body, with_query) untouched — including with_query's REPLACE
semantics, which the spec justifies vs with_headers' MERGE on three
grounds (usage patterns, HTTP semantics, escape-hatch availability).

Strict epic boundary — auth coercion (2-4), AsyncClient wiring (2-5),
StreamResponse helpers (4-1) deferred.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds Request.with_headers(headers: Mapping[str, str]) -> Self that
merges the incoming mapping into the existing headers; incoming keys
override existing. Four tests cover add, override, preserve-others,
and empty-input semantics.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds Request.with_cookie(name, value) -> Self and
Request.with_cookies(cookies: Mapping) -> Self. Singular adds/replaces
one cookie; plural merges a mapping with incoming keys overriding.
Three tests cover the add, replace, and merge cases.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds Request.with_extension(name, value: Any) -> Self and
Request.with_extensions(extensions: Mapping[str, Any]) -> Self.
Extensions hold opaque user payloads (transport hints, debug
attachments) — the Any value type is intentional and noqa'd. Three
tests cover add-single, merge-plural, and Any-value-type behavior.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds Response.with_headers(headers: Mapping[str, str]) -> Self and
Response.with_status(status: int) -> Self for ergonomic Response
rewriting from middleware. Both use the existing dataclasses.replace
pattern. with_status applies no validation by design — value objects
don't enforce protocol semantics.

Adds `import dataclasses` and `Self` to response.py's typing imports.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@lesnik512 lesnik512 self-assigned this May 31, 2026
@lesnik512 lesnik512 merged commit 9a3f4c4 into main May 31, 2026
5 checks passed
@lesnik512 lesnik512 deleted the story/2-3-request-immutability-helpers branch May 31, 2026 15:42
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