Skip to content

Commit 88df692

Browse files
committed
apply the same NaN/inf fallback in the FigureWidget serializer
_py_to_js sends 1-D float arrays to the frontend as binary buffers that become typed arrays as well, so a FigureWidget hits the same stacking reset. Send arrays with non-finite values as lists with null instead, and turn bare non-finite floats into null too, which also keeps jupyter_client from falling back to its non-compliant JSON path for them.
1 parent 4293d56 commit 88df692

2 files changed

Lines changed: 33 additions & 0 deletions

File tree

plotly/serializers.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import math
2+
13
from .basedatatypes import Undefined
24
from .optional_imports import get_module
35

@@ -38,6 +40,11 @@ def _py_to_js(v, widget_manager):
3840
# Handle numpy array
3941
# ------------------
4042
elif np is not None and isinstance(v, np.ndarray):
43+
# NaN/inf can't go out as JSON and plotly.js does not clean them out
44+
# of typed arrays, so send them as null like the JSON encoders do
45+
if v.dtype.kind == "f" and not np.isfinite(v).all():
46+
return np.where(np.isfinite(v), v.astype(object), None).tolist()
47+
4148
# Convert 1D numpy arrays with numeric types to memoryviews with
4249
# datatype and shape metadata.
4350
if (
@@ -53,6 +60,11 @@ def _py_to_js(v, widget_manager):
5360
# Convert all other numpy arrays to lists
5461
return v.tolist()
5562

63+
# Handle non-finite floats
64+
# ------------------------
65+
elif isinstance(v, float) and not math.isfinite(v):
66+
return None
67+
5668
# Handle Undefined
5769
# ----------------
5870
if v is Undefined:

tests/test_optional/test_graph_objs/test_b64_non_finite.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import plotly.graph_objects as go
99
import plotly.io as pio
1010
from _plotly_utils.utils import to_typed_array_spec
11+
from plotly.serializers import _py_to_js
1112

1213
try:
1314
import orjson # noqa: F401
@@ -73,3 +74,23 @@ def test_to_typed_array_spec_finite():
7374
"dtype": "f8",
7475
"bdata": "AAAAAAAA8D8AAAAAAAAAQA==",
7576
}
77+
78+
79+
def test_widget_serializer_non_finite():
80+
assert _py_to_js(np.array([1.0, np.nan, np.inf]), None) == [1.0, None, None]
81+
assert _py_to_js(np.array([[1.0, np.nan], [2.0, 3.0]]), None) == [
82+
[1.0, None],
83+
[2.0, 3.0],
84+
]
85+
assert _py_to_js({"y": [1.0, float("nan"), -float("inf")]}, None) == {
86+
"y": [1.0, None, None]
87+
}
88+
89+
90+
def test_widget_serializer_finite_array_as_buffer():
91+
v = np.array([1.0, 2.0])
92+
93+
result = _py_to_js(v, None)
94+
assert result["dtype"] == "float64"
95+
assert result["shape"] == (2,)
96+
assert bytes(result["buffer"]) == v.tobytes()

0 commit comments

Comments
 (0)