Skip to content

Commit 87943be

Browse files
tcoratgerclaude
andauthored
refactor(base): raise when to_json is called with mode or by_alias (leanEthereum#794)
The previous behavior silently popped mode and by_alias from kwargs so the serializer could pin them to "json" and True. A caller passing either almost certainly expected the override to apply, so silently honoring the pin produced output that did not match their request — a footgun. Reject the kwargs with a clear TypeError instead. Other kwargs still forward unchanged. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent da21079 commit 87943be

2 files changed

Lines changed: 22 additions & 27 deletions

File tree

src/lean_spec/base.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,22 @@ class CamelModel(BaseModel):
2121
)
2222

2323
def to_json(self, **kwargs: Any) -> dict[str, Any]:
24-
"""Serialize to a JSON-encodable dict with camelCase keys.
24+
"""
25+
Serialize to a JSON-encodable dict with camelCase keys.
26+
27+
Serialization mode is pinned to JSON.
28+
Alias style is pinned to camelCase.
29+
A caller that overrides either almost certainly expects the override to apply.
30+
The override is rejected to avoid silently surprising the caller.
2531
26-
Always uses JSON mode and camelCase aliases regardless of kwargs.
27-
Callers cannot override mode or by_alias — these are stripped
28-
silently to guarantee correct test vector output.
32+
Raises:
33+
TypeError: If mode or by_alias is passed as a keyword argument.
2934
"""
30-
kwargs.pop("mode", None)
31-
kwargs.pop("by_alias", None)
35+
if "mode" in kwargs or "by_alias" in kwargs:
36+
raise TypeError(
37+
"to_json() does not accept 'mode' or 'by_alias'; "
38+
"mode is pinned to 'json' and by_alias to True"
39+
)
3240

3341
return self.model_dump(
3442
mode="json",

tests/lean_spec/test_base.py

Lines changed: 8 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -40,32 +40,19 @@ def test_to_json_converts_snake_case_to_camel(self) -> None:
4040

4141
assert result == {"firstName": "Alice", "currentSlot": 42}
4242

43-
def test_to_json_strips_mode_kwarg(self) -> None:
44-
"""Passing mode= does not override the JSON serialization mode.
45-
46-
The method always uses mode='json'. If a caller accidentally
47-
passes mode='python', the pop() silently discards it.
48-
"""
43+
def test_to_json_rejects_mode_kwarg(self) -> None:
44+
"""Overriding serialization mode raises an error instead of being silently accepted."""
4945
model = SampleCamelModel(first_name="Bob", current_slot=7)
5046

51-
# Passing mode= should be silently ignored.
52-
result = model.to_json(mode="python")
53-
54-
assert result == {"firstName": "Bob", "currentSlot": 7}
47+
with pytest.raises(TypeError, match="does not accept 'mode' or 'by_alias'"):
48+
model.to_json(mode="python")
5549

56-
def test_to_json_strips_by_alias_kwarg(self) -> None:
57-
"""Passing by_alias= does not override the alias behavior.
58-
59-
The method always uses by_alias=True (camelCase). If a caller
60-
passes by_alias=False, the pop() silently discards it.
61-
"""
50+
def test_to_json_rejects_by_alias_kwarg(self) -> None:
51+
"""Overriding alias style raises an error instead of being silently accepted."""
6252
model = SampleCamelModel(first_name="Carol", current_slot=0)
6353

64-
# Passing by_alias=False should be silently ignored.
65-
result = model.to_json(by_alias=False)
66-
67-
# Keys are still camelCase despite the caller's request.
68-
assert result == {"firstName": "Carol", "currentSlot": 0}
54+
with pytest.raises(TypeError, match="does not accept 'mode' or 'by_alias'"):
55+
model.to_json(by_alias=False)
6956

7057
def test_to_json_forwards_extra_kwargs(self) -> None:
7158
"""Other kwargs (e.g., exclude_defaults) pass through to model_dump.

0 commit comments

Comments
 (0)