Skip to content

Commit 8e97187

Browse files
Fix float("inf") and float("nan") JSON serialization breaking reports (#1964)
1 parent c07f8d6 commit 8e97187

File tree

3 files changed

+32
-2
lines changed

3 files changed

+32
-2
lines changed

elementary/monitor/data_monitoring/report/data_monitoring_report.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from elementary.monitor.data_monitoring.schema import FiltersSchema
2222
from elementary.tracking.anonymous_tracking import AnonymousTracking
2323
from elementary.tracking.tracking_interface import Tracking
24+
from elementary.utils import json_utils
2425
from elementary.utils.log import get_logger
2526

2627
logger = get_logger(__name__)
@@ -74,7 +75,7 @@ def generate_report(
7475
with open(template_html_path, "r", encoding="utf-8") as template_html_file:
7576
template_html_code = template_html_file.read()
7677

77-
dumped_output_data = json.dumps(output_data)
78+
dumped_output_data = json.dumps(json_utils.inf_and_nan_to_str(output_data))
7879
encoded_output_data = base64.b64encode(dumped_output_data.encode("utf-8"))
7980
compiled_output_html = (
8081
f"<script>"

elementary/utils/json_utils.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import json
2-
from typing import List, Optional, Union
2+
from typing import Any, List, Optional, Union
3+
4+
import numpy as np
35

46

57
def try_load_json(value: Optional[Union[str, dict, list]]):
@@ -76,3 +78,20 @@ def append_prefix_if_missing(string: str, prefix: str) -> str:
7678
if string.startswith(prefix):
7779
return string
7880
return f"{prefix}{string}"
81+
82+
83+
def inf_and_nan_to_str(obj) -> Any:
84+
"""Replaces occurrences of float("nan") for float("infinity") in the given dict object."""
85+
if isinstance(obj, float):
86+
if np.isinf(obj):
87+
return "Infinity" if obj > 0 else "-Infinity"
88+
elif np.isnan(obj):
89+
return "NaN"
90+
else:
91+
return obj
92+
elif isinstance(obj, dict):
93+
return {k: inf_and_nan_to_str(v) for k, v in obj.items()}
94+
elif isinstance(obj, list):
95+
return [inf_and_nan_to_str(i) for i in obj]
96+
else:
97+
return obj

tests/unit/utils/test_dicts.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import json
22

3+
from elementary.utils import json_utils
34
from elementary.utils.dicts import flatten_dict_by_key, merge_dicts_attribute
45

56

@@ -29,3 +30,12 @@ def test_merge_dicts_attribute():
2930
assert sorted(
3031
merge_dicts_attribute(dicts=[dict_1, dict_2, dict_3], attribute_key="attr")
3132
) == sorted([1, 2, 2, 3])
33+
34+
35+
def test_replace_inf_and_nan():
36+
data = {"values": [1.0, float("inf"), float("-inf"), float("nan")]}
37+
data = dict(a=1.0, b=float("inf"), c=float("-inf"), d=float("nan"))
38+
39+
processed_data = json_utils.inf_and_nan_to_str(data)
40+
json_str = json.dumps(processed_data, sort_keys=True)
41+
assert json_str == '{"a": 1.0, "b": "Infinity", "c": "-Infinity", "d": "NaN"}'

0 commit comments

Comments
 (0)