Skip to content

fix: reject non-finite JSON contract values - #361

Merged
codeforester merged 5 commits into
mainfrom
bug/342-20260918-strict-json-numbers
Sep 25, 2026
Merged

codeforester merged 5 commits into
mainfrom
bug/342-20260918-strict-json-numbers

Conversation

@codeforester

Copy link
Copy Markdown
Contributor

Summary

  • Serialize JSON and NDJSON with strict RFC-compliant number handling.
  • Serialize each NDJSON record fully before writing, and document the contract.
  • Add regression tests for nested NaN and infinity across public emitters.

Closes #342

Validation

  • uv run --extra dev --extra typer --extra quality python -m pytest tests/test_output.py tests/test_json_contracts.py -q
  • uv run --extra dev --extra typer --extra quality ruff check lib/python/base_cli/output.py lib/python/base_cli/json_contracts.py tests/test_output.py tests/test_json_contracts.py
  • git diff --check

Comment thread lib/python/base_cli/output.py Outdated
return "true" if value else "false"
if isinstance(value, (Mapping, list, tuple)):
return json.dumps(value, separators=(",", ":"))
return json.dumps(value, separators=(",", ":"), allow_nan=False)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correctness (reproduced): adding allow_nan=False to _cell_value() extends strict-JSON rejection to the CSV/TSV/table rendering path too, and render_records() writes CSV/TSV rows one at a time — so a later record's non-finite value now raises after earlier rows are already flushed to the stream. This reintroduces, for CSV/TSV, exactly the partial-output problem this PR's changelog/docs claim to fix for NDJSON. Pre-diff, the same value just embedded a literal NaN string in the cell and never raised.

Comment thread lib/python/base_cli/json_contracts.py Outdated
redact_json_value(dict(envelope)),
ensure_ascii=False,
separators=(",", ":"),
allow_nan=False,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reuse (root cause of a gap in inspection.py): allow_nan=False was hand-added at six separate call sites across this module and output.py instead of routing through one shared strict-JSON helper. A future emitter added without remembering to pass it by hand can reintroduce silent non-finite leakage — this already happened: render_inspection_json() in lib/python/base_cli/inspection.py was not updated and still uses plain json.dumps() with no allow_nan=False, despite docs/json-contracts.md's new claim that all JSON/NDJSON emitters reject non-finite values. Worth centralizing into one choke point.

@codeforester
codeforester merged commit 5b6273c into main Sep 25, 2026
115 checks passed
@codeforester
codeforester deleted the bug/342-20260918-strict-json-numbers branch September 25, 2026 14:41
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.

bug: reject non-finite numbers in JSON and NDJSON contracts

1 participant