Skip to content

Commit 60789e3

Browse files
lesnik512claude
andcommitted
fix: Response.json() honors declared charset
Routes the body through self.text instead of json.loads(self.content), so a declared charset (e.g. iso-8859-1) is respected before JSON parsing. ASCII / UTF-8 bodies are unchanged. Docstring now explicitly documents the json.JSONDecodeError raise contract. Wrapping JSONDecodeError in a domain exception is left to a future response-API revision. Closes deferred-work entries: "Response.json() raises raw and ignores charset" (retro) and "Response.json() honor declared charset" (1-2). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent bb567f0 commit 60789e3

2 files changed

Lines changed: 19 additions & 2 deletions

File tree

src/httpware/response.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,13 @@ def text(self) -> str:
4848
return self.content.decode("utf-8")
4949

5050
def json(self) -> Any: # noqa: ANN401
51-
"""Parse `content` as JSON."""
52-
return json.loads(self.content)
51+
"""Parse `content` as JSON using the declared charset (default UTF-8).
52+
53+
Raises:
54+
json.JSONDecodeError: if the body is not valid JSON.
55+
56+
"""
57+
return json.loads(self.text)
5358

5459
def with_headers(self, headers: Mapping[str, str]) -> Self:
5560
"""Return a copy with the given headers merged in (incoming keys override existing)."""

tests/test_response.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,18 @@ def test_response_json_parses_body() -> None:
8484
assert resp.json() == {"a": 1, "b": [2, 3]}
8585

8686

87+
def test_response_json_uses_declared_charset() -> None:
88+
body = '{"name": "café"}'.encode("iso-8859-1")
89+
resp = Response(
90+
status=HTTPStatus.OK,
91+
headers={"content-type": "application/json; charset=iso-8859-1"},
92+
content=body,
93+
url="/",
94+
elapsed=0.0,
95+
)
96+
assert resp.json() == {"name": "café"}
97+
98+
8799
def test_response_equality_on_identical_fields() -> None:
88100
r1 = Response(status=200, headers={"a": "1"}, content=b"x", url="/", elapsed=0.5)
89101
r2 = Response(status=200, headers={"a": "1"}, content=b"x", url="/", elapsed=0.5)

0 commit comments

Comments
 (0)