From 1fe39e86c14624c8df12a63d3c2de1809f68027a Mon Sep 17 00:00:00 2001 From: boulea7 Date: Sat, 3 Oct 2026 04:39:02 +0800 Subject: [PATCH] Reject non-lowercase special float literals --- tests/test_parser.py | 59 ++++++++++++++++++++++++++++++++++++++++++++ tomlkit/parser.py | 4 +++ 2 files changed, 63 insertions(+) diff --git a/tests/test_parser.py b/tests/test_parser.py index 44d5cd6c..0bcd2b04 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -1,7 +1,10 @@ +import math import sys import pytest +from tomlkit import dumps +from tomlkit import parse from tomlkit.exceptions import EmptyTableNameError from tomlkit.exceptions import InternalParserError from tomlkit.exceptions import InvalidNumberError @@ -246,3 +249,59 @@ def test_parser_accepts_uppercase_exponent_after_leading_zero() -> None: value = Parser(f"a = {raw}").parse()["a"] assert isinstance(value, Float) assert value == float(raw) + + +@pytest.mark.parametrize("sign", ["", "+", "-"]) +@pytest.mark.parametrize( + "raw", + [ + "Inf", + "iNf", + "inF", + "INf", + "InF", + "iNF", + "INF", + "Nan", + "nAn", + "naN", + "NAn", + "NaN", + "nAN", + "NAN", + ], +) +def test_parser_rejects_non_lowercase_special_floats(raw: str, sign: str) -> None: + with pytest.raises(ParseError): + parse(f"v = {sign}{raw}") + + +@pytest.mark.parametrize( + "raw,expected", + [ + ("inf", math.inf), + ("+inf", math.inf), + ("-inf", -math.inf), + ("nan", math.nan), + ("+nan", math.nan), + ("-nan", math.nan), + ("0E2", 0.0), + ("+0E+2", 0.0), + ("-0E-2", -0.0), + ("1_2.3_4E+2", 1234.0), + ("0xDe_Ad", 0xDEAD), + ("0o7_5", 0o75), + ("0b1_0", 0b10), + ("1_000", 1000), + ], +) +def test_parser_preserves_valid_numeric_literals(raw: str, expected: float) -> None: + content = f"v = {raw} # number\n" + document = parse(content) + value = document["v"] + assert isinstance(value, (Integer, Float)) + if math.isnan(expected): + assert math.isnan(value) + else: + assert value == expected + assert dumps(document) == content diff --git a/tomlkit/parser.py b/tomlkit/parser.py index 8c7b1a64..dc08f928 100644 --- a/tomlkit/parser.py +++ b/tomlkit/parser.py @@ -778,6 +778,10 @@ def _parse_number(self, raw: str, trivia: Trivia) -> Item | None: if "_" in clean: return None + # Special float values must be lowercase in the original token. + if clean in ("inf", "nan") and raw != clean: + return None + if clean.endswith(".") or ( not clean.startswith("0x") and clean.split("e", 1)[0].endswith(".") ):