chore(profile): tolerate null/empty frames in walk_self_time

Some pyinstrument frame trees contain branches where an identifier is
missing (typically at the very top or with certain async boundaries),
which crashed the aggregator with a KeyError mid-run. Short-circuit
on None frames and missing identifiers so a single ugly HTML no
longer kills the summary of the other few hundred.
This commit is contained in:
2026-04-17 22:04:29 -04:00
parent 6c22f9ba59
commit 1f758a3669

View File

@@ -57,7 +57,7 @@ def _is_synthetic(identifier: str) -> bool:
return identifier in _SYNTHETIC or identifier.startswith(("[self]", "[await]"))
def walk_self_time(frame: dict, acc: dict[str, float], parent_ident: str | None = None) -> None:
def walk_self_time(frame: dict | None, acc: dict[str, float], parent_ident: str | None = None) -> None:
"""
Accumulate self-time by frame identifier.
@@ -65,7 +65,11 @@ def walk_self_time(frame: dict, acc: dict[str, float], parent_ident: str | None
execution time. Rolling them into their parent ("self-time of X" vs. a
global `[self]` bucket) is what gives us actionable per-function hotspots.
"""
ident = frame["identifier"]
if not frame:
return
ident = frame.get("identifier")
if not ident:
return
total = frame.get("time", 0.0)
children = frame.get("children") or []
child_total = sum(c.get("time", 0.0) for c in children)