{"spec_id":"flamegraph-basic","library":"altair","language":"python","code":"\"\"\" anyplot.ai\nflamegraph-basic: Flame Graph for Performance Profiling\nLibrary: altair 6.2.1 | Python 3.13.13\nQuality: 91/100 | Updated: 2026-06-08\n\"\"\"\n\nimport os\n\nimport altair as alt\nimport pandas as pd\nfrom PIL import Image\n\n\n# Theme tokens (see prompts/default-style-guide.md \"Theme-adaptive Chrome\")\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nELEVATED_BG = \"#FFFDF6\" if THEME == \"light\" else \"#242420\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\n\n# Imprint warm semantic ramp: amber -> ochre -> matte red. Flame graphs carry a\n# strong, widely-shared warm-palette convention; the spec calls it out directly,\n# so this is a semantic exception (\"Semantic exception\" in the style guide).\n# All three stops are Imprint members (amber anchor + ochre + matte-red).\nWARM_STOPS = [\"#DDCC77\", \"#BD8233\", \"#AE3030\"]\n\n# Data — simulated CPU profiling samples from a Python web request handler.\nstacks = {\n    \"main\": 500,\n    \"main;request_handler\": 420,\n    \"main;request_handler;parse_headers\": 80,\n    \"main;request_handler;parse_headers;decode_utf8\": 45,\n    \"main;request_handler;parse_headers;validate_fields\": 30,\n    \"main;request_handler;route_dispatch\": 60,\n    \"main;request_handler;route_dispatch;regex_match\": 40,\n    \"main;request_handler;process_request\": 250,\n    \"main;request_handler;process_request;db_query\": 140,\n    \"main;request_handler;process_request;db_query;connect_pool\": 25,\n    \"main;request_handler;process_request;db_query;execute_sql\": 90,\n    \"main;request_handler;process_request;db_query;execute_sql;parse_query\": 35,\n    \"main;request_handler;process_request;db_query;execute_sql;fetch_rows\": 45,\n    \"main;request_handler;process_request;db_query;serialize\": 20,\n    \"main;request_handler;process_request;template_render\": 80,\n    \"main;request_handler;process_request;template_render;compile_template\": 30,\n    \"main;request_handler;process_request;template_render;render_html\": 45,\n    \"main;request_handler;process_request;json_encode\": 25,\n    \"main;request_handler;send_response\": 25,\n    \"main;request_handler;send_response;compress_gzip\": 18,\n    \"main;gc_collect\": 50,\n    \"main;gc_collect;mark_sweep\": 35,\n    \"main;gc_collect;compact_heap\": 12,\n    \"main;logger\": 25,\n    \"main;logger;format_message\": 15,\n    \"main;logger;write_file\": 8,\n}\n\ntotal_samples = stacks[\"main\"]\n\n# Pack each frame into an x-span: parent defines the range, children fill it\n# left-to-right sorted widest-first. Standard icicle/flamegraph layout.\npositions = {\"main\": (0, total_samples)}\nrecords = []\nstacks_by_depth = {}\nfor stack_path, value in stacks.items():\n    stacks_by_depth.setdefault(stack_path.count(\";\"), []).append((stack_path, value))\n\nfor depth in sorted(stacks_by_depth):\n    if depth == 0:\n        for stack_path, value in stacks_by_depth[depth]:\n            positions[stack_path] = (0, value)\n            records.append(\n                {\n                    \"x\": 0,\n                    \"x2\": value,\n                    \"depth\": depth,\n                    \"function\": stack_path.split(\";\")[-1],\n                    \"samples\": value,\n                    \"stack\": stack_path,\n                    \"width\": value,\n                }\n            )\n        continue\n    parent_children = {}\n    for stack_path, value in stacks_by_depth[depth]:\n        parent = \";\".join(stack_path.split(\";\")[:-1])\n        parent_children.setdefault(parent, []).append((stack_path, value))\n    for parent, children in parent_children.items():\n        if parent not in positions:\n            continue\n        parent_x, _ = positions[parent]\n        children.sort(key=lambda c: c[1], reverse=True)\n        current_x = parent_x\n        for stack_path, value in children:\n            positions[stack_path] = (current_x, current_x + value)\n            records.append(\n                {\n                    \"x\": current_x,\n                    \"x2\": current_x + value,\n                    \"depth\": depth,\n                    \"function\": stack_path.split(\";\")[-1],\n                    \"samples\": value,\n                    \"stack\": stack_path,\n                    \"width\": value,\n                }\n            )\n            current_x += value\n\ndf = pd.DataFrame(records)\ndf[\"pct\"] = (df[\"samples\"] / total_samples * 100).round(1)\nmax_depth = int(df[\"depth\"].max())\n\n# Trace the dominant call path (widest descendant at each depth) for emphasis.\nhot_path = {\"main\"}\ncurrent = \"main\"\nfor d in range(1, max_depth + 1):\n    children = df[(df[\"depth\"] == d) & (df[\"stack\"].str.startswith(current + \";\"))]\n    if children.empty:\n        break\n    current = children.loc[children[\"samples\"].idxmax(), \"stack\"]\n    hot_path.add(current)\n\ndf[\"is_hot\"] = df[\"stack\"].isin(hot_path)\ndf[\"opacity_val\"] = df[\"is_hot\"].map({True: 1.0, False: 0.55})\n\n# Plot\nTITLE = \"flamegraph-basic · python · altair · anyplot.ai\"\nratio = 67 / len(TITLE) if len(TITLE) > 67 else 1.0\nTITLE_PX = max(11, round(16 * ratio))\n\nalt.data_transformers.disable_max_rows()\n\nhover = alt.selection_point(on=\"pointerover\", fields=[\"stack\"], empty=False, clear=\"pointerout\")\n\nbase = alt.Chart(df).transform_calculate(\n    mid=\"(datum.x + datum.x2) / 2\", label=f\"datum.width / {total_samples} > 0.06 ? datum.function : ''\"\n)\n\nbars = base.mark_rect(stroke=PAGE_BG, strokeWidth=0.6, cornerRadius=2).encode(\n    x=alt.X(\"x:Q\", title=\"Samples (count)\", scale=alt.Scale(domain=[0, total_samples], nice=False)),\n    x2=\"x2:Q\",\n    y=alt.Y(\"depth:O\", title=\"Stack Depth (level)\", sort=\"descending\"),\n    color=alt.Color(\n        \"depth:Q\", scale=alt.Scale(domain=[0, max_depth], range=WARM_STOPS, interpolate=\"hsl\"), legend=None\n    ),\n    opacity=alt.Opacity(\"opacity_val:Q\", legend=None, scale=alt.Scale(domain=[0.55, 1.0], range=[0.55, 1.0])),\n    tooltip=[\n        alt.Tooltip(\"function:N\", title=\"Function\"),\n        alt.Tooltip(\"samples:Q\", title=\"Samples\"),\n        alt.Tooltip(\"pct:Q\", title=\"% Total\", format=\".1f\"),\n        alt.Tooltip(\"stack:N\", title=\"Stack\"),\n    ],\n)\n\n# Hover overlay: thick ink-coloured outline that lights up only the bar under\n# the pointer. Distinctive Altair pattern — declarative selection + condition.\nhighlight = (\n    base.mark_rect(stroke=INK, strokeWidth=2.5, fill=\"transparent\", cornerRadius=2)\n    .encode(\n        x=\"x:Q\",\n        x2=\"x2:Q\",\n        y=alt.Y(\"depth:O\", sort=\"descending\"),\n        opacity=alt.condition(hover, alt.value(1.0), alt.value(0.0)),\n    )\n    .add_params(hover)\n)\n\nlabels = base.mark_text(fontSize=8, color=INK, fontWeight=\"bold\", align=\"center\", baseline=\"middle\").encode(\n    x=\"mid:Q\", y=alt.Y(\"depth:O\", sort=\"descending\"), text=\"label:N\"\n)\n\nchart = (\n    (bars + highlight + labels)\n    .interactive()\n    .properties(\n        width=620,\n        height=320,\n        background=PAGE_BG,\n        padding={\"left\": 16, \"right\": 16, \"top\": 16, \"bottom\": 16},\n        title=alt.Title(\n            TITLE,\n            subtitle=[\n                \"Hot path: main → request_handler → process_request → db_query → execute_sql\",\n                \"Hover bars to highlight · drag to pan · scroll to zoom\",\n            ],\n            fontSize=TITLE_PX,\n            subtitleFontSize=10,\n            color=INK,\n            subtitleColor=INK_SOFT,\n            anchor=\"start\",\n            offset=12,\n            subtitlePadding=4,\n        ),\n    )\n    .configure_view(fill=PAGE_BG, stroke=None)\n    .configure_axis(\n        grid=False,\n        domainColor=INK_SOFT,\n        tickColor=INK_SOFT,\n        labelColor=INK_SOFT,\n        titleColor=INK,\n        labelFontSize=10,\n        titleFontSize=12,\n    )\n    .configure_legend(\n        fillColor=ELEVATED_BG,\n        strokeColor=INK_SOFT,\n        labelColor=INK_SOFT,\n        titleColor=INK,\n        labelFontSize=10,\n        titleFontSize=10,\n    )\n)\n\n# Save PNG, then PAD (never crop) to the canonical 3200×1800 canvas.\nchart.save(f\"plot-{THEME}.png\", scale_factor=4.0)\n\nTW, TH = 3200, 1800\n_img = Image.open(f\"plot-{THEME}.png\").convert(\"RGB\")\n_w, _h = _img.size\nif _w > TW or _h > TH:\n    raise SystemExit(\n        f\"altair vl-convert produced {_w}x{_h}, exceeds target {TW}x{TH}. \"\n        f\"Shrink chart .properties(width=, height=) values and re-render.\"\n    )\nif _w < TW or _h < TH:\n    _canvas = Image.new(\"RGB\", (TW, TH), PAGE_BG)\n    _canvas.paste(_img, ((TW - _w) // 2, (TH - _h) // 2))\n    _canvas.save(f\"plot-{THEME}.png\")\n\nchart.save(f\"plot-{THEME}.html\")\n"}