From 7f0b53c3ebc5bd63f293874f3488b72d101dcf4a Mon Sep 17 00:00:00 2001 From: Sasa Junuzovic <44276455+microsasa@users.noreply.github.com> Date: Sun, 19 Apr 2026 00:42:49 -0700 Subject: [PATCH] fix: guard parse_token_int against IEEE 754 special floats (#875) Add explicit math.isfinite() check in parse_token_int so float('inf'), float('-inf'), and float('nan') are rejected before reaching Pydantic int coercion. Previously these values were only handled incidentally via float.is_integer() returning False. - Add math.isfinite guard in parse_token_int (models.py) - Update parse_token_int docstring to document IEEE special float rule - Add parametrized test for _extract_output_tokens with special floats - Extend _EQUIVALENCE_CASES cross-check with inf, -inf, and nan Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/copilot_usage/models.py | 4 ++++ tests/copilot_usage/test_parser.py | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/src/copilot_usage/models.py b/src/copilot_usage/models.py index 8ff71044..d8d20f9d 100644 --- a/src/copilot_usage/models.py +++ b/src/copilot_usage/models.py @@ -6,6 +6,7 @@ """ import builtins +import math from datetime import UTC, datetime from enum import StrEnum from pathlib import Path @@ -220,6 +221,7 @@ def parse_token_int(raw: object) -> int | None: Rules: - ``bool`` / ``str`` → ``None`` (invalid, not coerced) + - IEEE 754 special ``float`` (``inf``, ``-inf``, ``nan``) → ``None`` - non-whole ``float`` → ``None`` - zero or negative ``int`` / ``float`` → ``None`` - positive whole-number ``float`` → coerced to ``int`` @@ -229,6 +231,8 @@ def parse_token_int(raw: object) -> int | None: if isinstance(raw, (bool, str)): return None if isinstance(raw, float): + if not math.isfinite(raw): + return None if not raw.is_integer(): return None tokens = int(raw) diff --git a/tests/copilot_usage/test_parser.py b/tests/copilot_usage/test_parser.py index 95a3a91f..76568f72 100644 --- a/tests/copilot_usage/test_parser.py +++ b/tests/copilot_usage/test_parser.py @@ -4948,6 +4948,11 @@ def test_returns_none_for_missing_key(self) -> None: ) assert _extract_output_tokens(ev) is None + @pytest.mark.parametrize("special", [float("inf"), float("nan"), float("-inf")]) + def test_returns_none_for_ieee_special_floats(self, special: float) -> None: + """IEEE 754 special floats (inf, nan, -inf) are not valid token counts.""" + assert _extract_output_tokens(_make_assistant_event(special)) is None + _EXTRACT_TOKENS_CASES: list[tuple[str, object, int | None]] = [ ("valid_int", 42, 42), @@ -5021,6 +5026,9 @@ def test_missing_key_no_pydantic(self) -> None: ("large_positive_int", 1234), ("whole_float", 1234.0), ("fractional_float", 3.14), + ("float_inf", float("inf")), + ("float_neg_inf", float("-inf")), + ("float_nan", float("nan")), ]