From 2ba7882653500a35a4be3ef4785fd95c11199480 Mon Sep 17 00:00:00 2001 From: fan wu <336798967+FanWu-ai@users.noreply.github.com> Date: Sun, 11 Oct 2026 10:25:45 -0700 Subject: [PATCH] Reset per-report state when reusing a Speedscope renderer AI assistance: OpenAI Codex was used for investigation, implementation, regression tests, and automated validation. --- pyinstrument/renderers/speedscope.py | 2 ++ test/test_renderers.py | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/pyinstrument/renderers/speedscope.py b/pyinstrument/renderers/speedscope.py index 6bb20581..37d0f5b6 100644 --- a/pyinstrument/renderers/speedscope.py +++ b/pyinstrument/renderers/speedscope.py @@ -207,6 +207,8 @@ def render_frame(self, frame: Frame | None) -> list[SpeedscopeEvent]: return events_array def render(self, session: Session): + self._event_time = 0.0 + self._frame_to_index.clear() frame = self.preprocess(session.root_frame()) id_: str = time.strftime("%Y-%m-%dT%H-%M-%S", time.localtime(session.start_time)) diff --git a/test/test_renderers.py b/test/test_renderers.py index 87ba4456..9270c0c0 100644 --- a/test/test_renderers.py +++ b/test/test_renderers.py @@ -89,6 +89,29 @@ def test_show_all_doesnt_crash( renderer.render(profiler_session) +def test_speedscope_renderer_reused_with_same_session(profiler_session): + renderer = renderers.SpeedscopeRenderer() + first_output = renderer.render(profiler_session) + assert renderer.render(profiler_session) == first_output + + +@pytest.mark.parametrize("empty", [False, True]) +def test_speedscope_renderer_reused_with_different_session(profiler_session, empty): + session_data = profiler_session.to_json() + session_data.update( + frame_records=[] if empty else [(["other\x00other.py\x001"], 0.25)], + start_call_stack=[], + duration=0 if empty else 0.25, + sample_count=0 if empty else 1, + cpu_time=0 if empty else 0.25, + ) + next_session = Session.from_json(session_data) + + renderer = renderers.SpeedscopeRenderer() + renderer.render(profiler_session) + assert renderer.render(next_session) == renderers.SpeedscopeRenderer().render(next_session) + + @pytest.mark.parametrize("flat_time", ["self", "total"]) def test_console_renderer_flat_doesnt_crash(profiler_session, flat_time): renderer = renderers.ConsoleRenderer(flat=True, flat_time=flat_time)