{"spec_id":"flamegraph-basic","library":"plotnine","language":"python","code":"\"\"\" anyplot.ai\nflamegraph-basic: Flame Graph for Performance Profiling\nLibrary: plotnine 0.15.5 | Python 3.13.13\nQuality: 92/100 | Updated: 2026-06-08\n\"\"\"\n\nimport os\nimport sys\n\nimport pandas as pd\n\n\n# Work around the naming conflict between this script (plotnine.py) and the plotnine package\nscript_dir = os.path.dirname(os.path.abspath(__file__))\nfor entry in (script_dir, \"\", \".\"):\n    if entry in sys.path:\n        sys.path.remove(entry)\n\nfrom plotnine import (\n    aes,\n    annotate,\n    coord_cartesian,\n    element_blank,\n    element_line,\n    element_rect,\n    element_text,\n    geom_rect,\n    geom_text,\n    ggplot,\n    labs,\n    scale_color_identity,\n    scale_fill_gradientn,\n    scale_x_continuous,\n    scale_y_continuous,\n    theme,\n    theme_void,\n)\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\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\n\n# Warm flame gradient from the Imprint palette (semantic association: flame → fire).\n# amber → ochre → matte red — single-polarity continuous, no external cmap.\nFLAME_LOW = \"#DDCC77\"  # Imprint amber (cool flame, low samples)\nFLAME_MID = \"#BD8233\"  # Imprint ochre (medium heat)\nFLAME_HIGH = \"#AE3030\"  # Imprint matte red (hottest path)\nBAR_EDGE = PAGE_BG  # subtle gap between siblings, theme-aware\n# Dark-mode amber bars are too light for cream INK text — fall back to dark INK\n# on those leaves so the function name stays high-contrast.\nAMBER_LABEL_INK = \"#1A1A17\"\n\n# Data — simulated CPU profiling stacks (web request handling, 59 unique stacks)\nstacks = {\n    \"main\": 1000,\n    \"main;request_handler\": 800,\n    \"main;request_handler;parse_headers\": 150,\n    \"main;request_handler;parse_headers;parse_cookies\": 60,\n    \"main;request_handler;parse_headers;read_content_type\": 40,\n    \"main;request_handler;parse_headers;read_user_agent\": 30,\n    \"main;request_handler;parse_body\": 120,\n    \"main;request_handler;parse_body;decode_json\": 90,\n    \"main;request_handler;parse_body;decode_json;tokenize\": 35,\n    \"main;request_handler;parse_body;decode_json;parse_tree\": 45,\n    \"main;request_handler;parse_body;validate_schema\": 25,\n    \"main;request_handler;parse_body;validate_schema;check_required\": 15,\n    \"main;request_handler;route_dispatch\": 480,\n    \"main;request_handler;route_dispatch;auth_middleware\": 100,\n    \"main;request_handler;route_dispatch;auth_middleware;verify_token\": 70,\n    \"main;request_handler;route_dispatch;auth_middleware;verify_token;decode_jwt\": 25,\n    \"main;request_handler;route_dispatch;auth_middleware;verify_token;check_signature\": 30,\n    \"main;request_handler;route_dispatch;auth_middleware;verify_token;check_expiry\": 10,\n    \"main;request_handler;route_dispatch;auth_middleware;check_permissions\": 25,\n    \"main;request_handler;route_dispatch;auth_middleware;check_permissions;load_roles\": 15,\n    \"main;request_handler;route_dispatch;query_database\": 250,\n    \"main;request_handler;route_dispatch;query_database;build_query\": 40,\n    \"main;request_handler;route_dispatch;query_database;build_query;bind_params\": 20,\n    \"main;request_handler;route_dispatch;query_database;execute_sql\": 160,\n    \"main;request_handler;route_dispatch;query_database;execute_sql;fetch_rows\": 100,\n    \"main;request_handler;route_dispatch;query_database;execute_sql;fetch_rows;read_socket\": 40,\n    \"main;request_handler;route_dispatch;query_database;execute_sql;fetch_rows;parse_rows\": 45,\n    \"main;request_handler;route_dispatch;query_database;execute_sql;deserialize\": 45,\n    \"main;request_handler;route_dispatch;query_database;execute_sql;deserialize;parse_json_blob\": 30,\n    \"main;request_handler;route_dispatch;query_database;cache_result\": 35,\n    \"main;request_handler;route_dispatch;query_database;cache_result;key_hash\": 10,\n    \"main;request_handler;route_dispatch;query_database;cache_result;serialize_value\": 20,\n    \"main;request_handler;route_dispatch;render_template\": 110,\n    \"main;request_handler;route_dispatch;render_template;compile_template\": 40,\n    \"main;request_handler;route_dispatch;render_template;compile_template;parse_ast\": 20,\n    \"main;request_handler;route_dispatch;render_template;compile_template;bytecode_gen\": 15,\n    \"main;request_handler;route_dispatch;render_template;apply_filters\": 55,\n    \"main;request_handler;route_dispatch;render_template;apply_filters;escape_html\": 20,\n    \"main;request_handler;route_dispatch;render_template;apply_filters;format_date\": 15,\n    \"main;request_handler;route_dispatch;render_template;apply_filters;truncate_text\": 10,\n    \"main;request_handler;send_response\": 40,\n    \"main;request_handler;send_response;serialize_body\": 8,\n    \"main;request_handler;send_response;compress_gzip\": 30,\n    \"main;request_handler;send_response;compress_gzip;deflate\": 25,\n    \"main;gc_collect\": 80,\n    \"main;gc_collect;mark_sweep\": 60,\n    \"main;gc_collect;mark_sweep;traverse_refs\": 30,\n    \"main;gc_collect;mark_sweep;mark_objects\": 25,\n    \"main;gc_collect;compact_heap\": 15,\n    \"main;gc_collect;compact_heap;move_objects\": 10,\n    \"main;logging\": 100,\n    \"main;logging;format_message\": 40,\n    \"main;logging;format_message;render_format_str\": 20,\n    \"main;logging;format_message;serialize_args\": 15,\n    \"main;logging;write_file\": 50,\n    \"main;logging;write_file;buffer_write\": 30,\n    \"main;logging;write_file;fsync\": 15,\n    \"main;heartbeat\": 15,\n    \"main;heartbeat;ping_monitoring\": 10,\n}\n\n# Compute x positions level by level, children laid out within their parent's extent\ntotal_samples = stacks[\"main\"]\ndepth_children: dict[tuple[int, str | None], list[tuple[str, str, int]]] = {}\nfor stack_path, samples in stacks.items():\n    parts = stack_path.split(\";\")\n    depth = len(parts) - 1\n    parent = \";\".join(parts[:-1]) if depth > 0 else None\n    depth_children.setdefault((depth, parent), []).append((parts[-1], stack_path, samples))\n\npositions: dict[str, tuple[int, int]] = {\"main\": (0, total_samples)}\nmax_stack_depth = max(len(p.split(\";\")) - 1 for p in stacks)\nfor current_depth in range(1, max_stack_depth + 1):\n    for (d, parent), children in sorted(depth_children.items()):\n        if d != current_depth or parent not in positions:\n            continue\n        x_cursor = positions[parent][0]\n        for _func_name, stack_path, samples in children:\n            positions[stack_path] = (x_cursor, x_cursor + samples)\n            x_cursor += samples\n\n# Identify the hottest call path (greedy descend by max-sample child) for emphasis\nhottest_path: set[str] = set()\ncursor_stack = \"main\"\nwhile True:\n    hottest_path.add(cursor_stack)\n    cursor_depth = cursor_stack.count(\";\")\n    kids = depth_children.get((cursor_depth + 1, cursor_stack), [])\n    if not kids:\n        break\n    cursor_stack = max(kids, key=lambda c: c[2])[1]\n\n# Build dataframe. Label color flips to dark INK on amber bars in dark mode so\n# the function name stays readable on the lightest fills (~samples < 200 sit in\n# the amber stop of the warm gradient).\nrecords = []\nfor stack_path, (xmin, xmax) in positions.items():\n    parts = stack_path.split(\";\")\n    depth = len(parts) - 1\n    func_name = parts[-1]\n    samples = xmax - xmin\n    width_frac = samples / total_samples\n    is_amber = samples < 200\n    label_color = AMBER_LABEL_INK if (THEME == \"dark\" and is_amber) else INK\n    # Suppress labels that won't fit within the bar (~0.5% canvas per glyph at size 6 mm)\n    fits = width_frac >= max(0.04, 0.005 * len(func_name))\n    records.append(\n        {\n            \"xmin\": xmin,\n            \"xmax\": xmax,\n            \"ymin\": depth + 0.05,\n            \"ymax\": depth + 0.95,\n            \"depth\": depth,\n            \"func\": func_name,\n            \"samples\": samples,\n            \"label\": func_name if fits else \"\",\n            \"label_x\": (xmin + xmax) / 2,\n            \"label_y\": depth + 0.5,\n            \"label_color\": label_color,\n            \"on_hot_path\": stack_path in hottest_path,\n        }\n    )\n\ndf = pd.DataFrame(records)\nhot_df = df[df[\"on_hot_path\"]].copy()\nmax_depth = int(df[\"depth\"].max())\n\n# Plot — geom_rect grammar with sample-count fill mapping (data storytelling: hotspots glow)\nplot = (\n    ggplot(df)\n    + geom_rect(aes(xmin=\"xmin\", xmax=\"xmax\", ymin=\"ymin\", ymax=\"ymax\", fill=\"samples\"), color=BAR_EDGE, size=0.4)\n    # Outline the hottest call path so the eye lands on the bottleneck immediately\n    + geom_rect(hot_df, aes(xmin=\"xmin\", xmax=\"xmax\", ymin=\"ymin\", ymax=\"ymax\"), fill=None, color=INK, size=0.9)\n    + geom_text(\n        aes(x=\"label_x\", y=\"label_y\", label=\"label\", color=\"label_color\"),\n        size=6,\n        fontweight=\"bold\",\n        family=\"DejaVu Sans\",\n        show_legend=False,\n    )\n    + scale_color_identity()\n    + scale_fill_gradientn(\n        colors=[FLAME_LOW, FLAME_MID, FLAME_HIGH], name=\"Samples\", breaks=[100, 500, 1000], limits=(0, total_samples)\n    )\n    + scale_x_continuous(expand=(0, 0))\n    + scale_y_continuous(expand=(0, 0.1))\n    + coord_cartesian(ylim=(0, max_depth + 1.3))\n    + labs(title=\"flamegraph-basic · python · plotnine · anyplot.ai\")\n    + annotate(\n        \"text\",\n        x=total_samples,\n        y=max_depth + 1.1,\n        label=f\"Total: {total_samples:,} samples  ·  outlined = hottest call path\",\n        ha=\"right\",\n        va=\"top\",\n        size=7,\n        color=INK_SOFT,\n        family=\"DejaVu Sans\",\n    )\n    + theme_void()\n    + theme(\n        figure_size=(8, 4.5),\n        plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n        panel_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n        panel_grid=element_blank(),\n        plot_title=element_text(size=12, color=INK, weight=\"bold\", ha=\"center\", margin={\"b\": 8, \"t\": 4}),\n        plot_margin=0.02,\n        legend_position=\"bottom\",\n        legend_direction=\"horizontal\",\n        legend_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n        legend_title=element_text(size=10, color=INK, weight=\"bold\"),\n        legend_text=element_text(size=8, color=INK_SOFT),\n        legend_key_width=70,\n        legend_key_height=8,\n        legend_box_margin=4,\n        legend_ticks=element_line(color=PAGE_BG, size=0.6),\n    )\n)\n\n# Save — figsize=(8, 4.5) @ dpi=400 → 3200×1800\nplot.save(f\"plot-{THEME}.png\", dpi=400, width=8, height=4.5, units=\"in\", verbose=False)\n"}