This is an automated email from the ASF dual-hosted git repository.
ebenizzy pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/hamilton.git
The following commit(s) were added to refs/heads/main by this push:
new 27349233 Serialize NaN and Infinity values as strings (#1322)
27349233 is described below
commit 27349233d4e17f2653355139f8e36718f25dd9ec
Author: Gavin Kistner <[email protected]>
AuthorDate: Mon Jul 14 21:48:03 2025 -0600
Serialize NaN and Infinity values as strings (#1322)
Float values that are "out of range" were previously serialized—to send to
the
tracker/Hamilton UI—in a way that produced invalid JSON (but value JS),
which
caused a runtime error trying to send values to the tracker.
`math.nan`, `math.inf`, and `-math.inf` are serialized by `json.dumps()` to
`NaN`, `Infinity`, and `-Infinity` values by default(NOT as strings).
This commit uses `json.dumps(…, allow_nan=False)`, which raises a
`ValueError`
for such values, and in that case converts them to strings.
The result is that Hamliton UI will show such values as **strings**
`"nan"`, `"inf"`, and `"-inf"`.
This could be confusing only to users who are also using such string values.
Fixes #1310
Co-authored-by: Gavin Kistner <[email protected]>
---
ui/sdk/src/hamilton_sdk/tracking/utils.py | 4 ++--
ui/sdk/tests/tracking/test_utils.py | 25 +++++++++++++++++++++++++
2 files changed, 27 insertions(+), 2 deletions(-)
diff --git a/ui/sdk/src/hamilton_sdk/tracking/utils.py
b/ui/sdk/src/hamilton_sdk/tracking/utils.py
index b0e515d8..fefc3b11 100644
--- a/ui/sdk/src/hamilton_sdk/tracking/utils.py
+++ b/ui/sdk/src/hamilton_sdk/tracking/utils.py
@@ -58,11 +58,11 @@ def make_json_safe(item: Union[dict, list, str, float, int,
bool]) -> Any:
return make_json_safe(item.to_dict())
else:
try:
- json.dumps(item) # Check if item is json serializable
+ json.dumps(item, allow_nan=False) # Check if item is json
serializable
if isinstance(item, str):
# escape null byte -- postgres doesn't like null bytes at
least.
# we might need to escape more things; TBD.
return item.replace("\x00", "\\x00")
return item
- except TypeError:
+ except (TypeError, ValueError):
return str(item) # Convert item to string if not json serializable
diff --git a/ui/sdk/tests/tracking/test_utils.py
b/ui/sdk/tests/tracking/test_utils.py
index eb85d52b..62cafb87 100644
--- a/ui/sdk/tests/tracking/test_utils.py
+++ b/ui/sdk/tests/tracking/test_utils.py
@@ -157,3 +157,28 @@ def test_make_json_safe_str_with_null_byte():
assert isinstance(result, dict)
assert isinstance(result["key2"], str)
assert result["key2"] == "value\\x00"
+
+
+def test_make_json_safe_with_nan():
+ """Test that NaN and Infinity values are properly handled in
make_json_safe"""
+ import math
+
+ # Test with a dictionary
+ input_dict = {"a": math.nan, "b": np.nan, "c": 1.0, "d": math.inf, "e":
-math.inf}
+ result = utils.make_json_safe(input_dict)
+ assert isinstance(result, dict)
+ assert result["a"] == "nan" # NaN should be represented as "nan"
+ assert result["b"] == "nan" # …same for numpy.nan
+ assert result["c"] == 1.0 # Regular float should remain unchanged
+ assert result["d"] == "inf" # Infinity should be represented as "inf"
+ assert result["e"] == "-inf" # Negative infinity should be represented as
"-inf"
+
+ # Test with a list
+ input_list = [math.nan, np.nan, 1.0, math.inf, -math.inf]
+ result = utils.make_json_safe(input_list)
+ assert isinstance(result, list)
+ assert result[0] == "nan" # NaN should be represented as "nan"
+ assert result[1] == "nan" # …same for numpy.nan
+ assert result[2] == 1.0 # Regular float should remain unchanged
+ assert result[3] == "inf" # Infinity should be represented as "inf"
+ assert result[4] == "-inf" # Negative infinity should be represented as
"-inf"