fix(config): preserve lossy decimal values as strings
This commit is contained in:
@@ -15,6 +15,7 @@ This module provides:
|
||||
"""
|
||||
|
||||
import copy
|
||||
from decimal import Decimal, InvalidOperation
|
||||
from hermes_cli.cli_output import line_input
|
||||
import json
|
||||
import logging
|
||||
@@ -5434,7 +5435,12 @@ def _coerce_int(value: str):
|
||||
|
||||
|
||||
def _coerce_float(value: str):
|
||||
"""Return float(value) for a clean float literal, else None."""
|
||||
"""Return ``float(value)`` when conversion preserves its decimal value.
|
||||
|
||||
Decimal-looking identifiers can be much more precise than a binary float.
|
||||
Silently rounding one here corrupts it before it reaches ``config.yaml``,
|
||||
so values that do not round-trip through ``float`` remain strings.
|
||||
"""
|
||||
try:
|
||||
f = float(value)
|
||||
except (TypeError, ValueError):
|
||||
@@ -5443,6 +5449,11 @@ def _coerce_float(value: str):
|
||||
# and round-trip confusingly through YAML.
|
||||
if f != f or f in (float("inf"), float("-inf")):
|
||||
return None
|
||||
try:
|
||||
if Decimal(value) != Decimal(str(f)):
|
||||
return None
|
||||
except InvalidOperation:
|
||||
return None
|
||||
return f
|
||||
|
||||
|
||||
|
||||
@@ -42,6 +42,18 @@ class TestNumericCoercion:
|
||||
v = _read(tmp_path, "agent", "max_turns")
|
||||
assert v == -2.5 and isinstance(v, float)
|
||||
|
||||
def test_lossy_decimal_identifier_stays_string(self, tmp_path, monkeypatch):
|
||||
monkeypatch.setenv("HERMES_HOME", str(tmp_path))
|
||||
client_id = "123456789012.98765432109876"
|
||||
|
||||
cfg.set_config_value("mcp_servers.example.oauth.client_id", client_id)
|
||||
|
||||
saved = _read(
|
||||
tmp_path, "mcp_servers", "example", "oauth", "client_id"
|
||||
)
|
||||
assert saved == client_id
|
||||
assert isinstance(saved, str)
|
||||
|
||||
|
||||
class TestNullCoercion:
|
||||
@pytest.mark.parametrize("token", ["null", "none", "None", "~"])
|
||||
|
||||
Reference in New Issue
Block a user