From 50b09112b828d183838a4f027d0224c568e87f54 Mon Sep 17 00:00:00 2001 From: Ghraven Date: Sun, 30 Aug 2026 19:18:19 +0800 Subject: [PATCH] fix(streamlit): read state logs as utf-8 --- burr/integrations/streamlit.py | 2 +- tests/integrations/test_streamlit.py | 65 ++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 tests/integrations/test_streamlit.py diff --git a/burr/integrations/streamlit.py b/burr/integrations/streamlit.py index 71b63bce0..c3b1df159 100644 --- a/burr/integrations/streamlit.py +++ b/burr/integrations/streamlit.py @@ -95,7 +95,7 @@ def load_state_from_log_file(jsonl_log_file: str, app: Application) -> AppState: :return: AppState """ out = [] - for i, line in enumerate(open(jsonl_log_file)): + for i, line in enumerate(open(jsonl_log_file, encoding="utf-8")): json_line = json.loads(line) record = Record( state=json_line["state"], diff --git a/tests/integrations/test_streamlit.py b/tests/integrations/test_streamlit.py new file mode 100644 index 000000000..122b0e689 --- /dev/null +++ b/tests/integrations/test_streamlit.py @@ -0,0 +1,65 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +import builtins +import importlib +import json +import sys +import types + + +def test_load_state_from_log_file_reads_utf8(monkeypatch, tmp_path): + monkeypatch.setitem( + sys.modules, + "burr.integrations.hamilton", + types.SimpleNamespace(Hamilton=object, StateSource=object), + ) + monkeypatch.setitem(sys.modules, "graphviz", types.SimpleNamespace(Digraph=object)) + monkeypatch.setitem( + sys.modules, "streamlit", types.SimpleNamespace(session_state={}) + ) + streamlit = importlib.import_module("burr.integrations.streamlit") + + log_file = tmp_path / "state.jsonl" + log_file.write_text( + json.dumps( + { + "state": {"message": "café"}, + "action": "say", + "result": {"ok": True}, + }, + ensure_ascii=False, + ) + + "\n", + encoding="utf-8", + ) + + real_open = builtins.open + + def guarded_open(*args, **kwargs): + assert kwargs.get("encoding") == "utf-8" + return real_open(*args, **kwargs) + + monkeypatch.setattr(builtins, "open", guarded_open) + + app = object() + state = streamlit.load_state_from_log_file(str(log_file), app) + + assert state.app is app + assert state.history[0].state == {"message": "café"} + assert state.history[0].action == "say" + assert state.history[0].result == {"ok": True}