From 0e050b683f8f57f8900881982e9ae565ab630a7d Mon Sep 17 00:00:00 2001 From: Solaris-star <820622658@qq.com> Date: Tue, 21 Jul 2026 11:31:51 +0800 Subject: [PATCH] fix(io): force UTF-8 for write_json/read_json file I/O Path.write_text/read_text without encoding use the locale default, which on Windows is often cp1252 and raises UnicodeEncodeError/UnicodeDecodeError for non-ASCII figure text. Always pass encoding="utf-8" so JSON round-trips match Linux/macOS. Fixes #5665 Signed-off-by: Solaris-star <820622658@qq.com> --- plotly/io/_json.py | 8 ++++++-- tests/test_io/test_to_from_json.py | 14 ++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/plotly/io/_json.py b/plotly/io/_json.py index 0e741711f4f..c3967f0456d 100644 --- a/plotly/io/_json.py +++ b/plotly/io/_json.py @@ -295,7 +295,9 @@ def write_json(fig, file, validate=True, pretty=False, remove_uids=True, engine= else: # We previously succeeded in interpreting `file` as a pathlib object. # Now we can use `write_bytes()`. - path.write_text(json_str) + # Explicit UTF-8: Windows default encodings (e.g. cp1252) raise + # UnicodeEncodeError for non-ASCII figure text (#5665). + path.write_text(json_str, encoding="utf-8") def from_json_plotly(value, engine=None): @@ -464,7 +466,9 @@ def read_json(file, output_type="Figure", skip_invalid=False, engine=None): # Read file contents into JSON string # ----------------------------------- if path is not None: - json_str = path.read_text() + # Explicit UTF-8 so files written by write_json decode correctly on + # Windows (default locale encoding is often not UTF-8) (#5665). + json_str = path.read_text(encoding="utf-8") else: json_str = file.read() diff --git a/tests/test_io/test_to_from_json.py b/tests/test_io/test_to_from_json.py index 21b9473b397..cb9d948cb60 100644 --- a/tests/test_io/test_to_from_json.py +++ b/tests/test_io/test_to_from_json.py @@ -271,3 +271,17 @@ def test_to_dict_empty_np_array_int64(): ) # to_dict() should not raise an exception fig.to_dict() + + +def test_write_read_json_utf8_non_ascii(tmp_path): + """write_json/read_json must use UTF-8 so Windows cp1252 locales work (#5665).""" + fig = go.Figure(layout_title_text="μ 中文") + path = tmp_path / "fig_utf8.json" + pio.write_json(fig, path) + # File bytes must be valid UTF-8 containing the title characters + raw = path.read_bytes() + raw.decode("utf-8") + assert "μ".encode("utf-8") in raw or "\\u03bc".encode("ascii") in raw + loaded = pio.read_json(path) + title = loaded.layout.title.text + assert "μ" in title and "中文" in title