Skip to main content

Execution Intelligence Tool โ€” Bottleneck Engine + Human Report Generator

Project description

AI Cost Tracking

PyPI Version Python License AI Cost Human Time Model

  • ๐Ÿค– LLM usage: $1.7263 (14 commits)
  • ๐Ÿ‘ค Human dev: ~$971 (9.7h @ $100/h, 30min dedup)

Generated on 2026-05-26 using openrouter/qwen/qwen3-coder-next


metrun doesn't just show you data โ€” it tells you what the problem is and how to fix it.

What is metrun?

metrun is a Python performance analysis library that turns raw profiling data into an intelligible execution report: bottleneck scores, dependency graphs, critical path highlighting, and actionable fix suggestions โ€” all in one tool.

โŒ traditional profilers โ†’ "here is your data"
โœ… metrun               โ†’ "here is your problem and why it exists"

Features

Feature Description
๐Ÿง  Bottleneck Engine Builds an execution graph, computes score = time + calls + nested amplification, ranks hotspots
๐Ÿ“Š Human Report Generator Emoji-annotated report with time %, call count, score and diagnosis per function
๐Ÿงจ Critical Path Finds the hottest nested call chain root โ†’ leaf
๐Ÿ’ก Fix Suggestion Engine Library-specific advice per diagnosis: lru_cache, asyncio, numba, viztracer, scalene โ€ฆ
๐Ÿ”ฅ ASCII Flamegraph Terminal-friendly proportional bar chart, zero extra dependencies
๐Ÿ–ผ๏ธ SVG Flamegraph Interactive SVG via flameprof
๐Ÿ”Œ cProfile Bridge Use stdlib cProfile as the profiling backend; feed results into the Bottleneck Engine
๐Ÿ“‹ TOON Metric Tree metrun scan auto-profiles and generates metrun.toon.yaml โ€” compact bottleneck map for the TOON ecosystem
โŒจ๏ธ CLI metrun profile, metrun inspect, metrun scan, metrun flame commands

Installation

pip install metrun            # core (click included)
pip install metrun[flamegraph] # + SVG flamegraph support (flameprof)

Decorator tracing

from metrun import trace, get_records, analyse, print_report

@trace
def slow_query(n):
    return sum(i * i for i in range(n))

@trace
def handler(items):
    return [slow_query(i) for i in items]

handler(list(range(100)))

bottlenecks = analyse(get_records())
print_report(bottlenecks)

Context-manager tracing

from metrun import section, get_records, analyse, print_report

with section("data_load"):
    data = load_from_db()

with section("transform"):
    result = process(data)

print_report(analyse(get_records()))

Full enhanced report

from metrun import analyse, get_records, print_report

records = get_records()
bottlenecks = analyse(records)

print_report(
    bottlenecks,
    show_graph=True,           # dependency graph
    show_critical_path=True,   # hottest call chain
    records=records,
    show_suggestions=True,     # fix advice
)

Example output

๐Ÿ”ฅ METRUN PERFORMANCE REPORT
=============================

๐Ÿ”ด slow_query
   โ†’ time:      0.8200s  (78.2%)
   โ†’ calls:     12,430
   โ†’ score:     12.9
   โ†’ diagnosis: ๐Ÿ”ฅ loop hotspot

โ”€โ”€ Critical Path โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
๐Ÿงจ Critical Path  (depth=2, hottest leaf: 0.8200s)

  handler  [1.0500s, 1 calls]
    โ””โ”€ slow_query  [0.8200s, 12430 calls]   โ† ๐Ÿ”ฅ hottest leaf (0.8200s)

โ”€โ”€ Fix Suggestions โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
  ๐Ÿ’ก Fix suggestions for: slow_query
     1. Cache repeated results with lru_cache [functools]
           from functools import lru_cache

           @lru_cache(maxsize=None)
           def slow_query(x): ...

     2. Vectorise the loop with NumPy [numpy]
           import numpy as np
           result = np.sum(arr ** 2)

Auto-diagnosis labels

Label Trigger
๐Ÿ”ฅ loop hotspot calls โ‰ฅ 1 000
๐ŸŒฒ dependency bottleneck โ‰ฅ 3 direct children in the execution graph
๐Ÿข slow execution โ‰ฅ 30 % of total wall time (time_pct โ‰ฅ 0.30), low calls
โœ… nominal below all thresholds

Score formula:

score = (total_time / max_time) ร— 10  +  log10(calls + 1)  +  n_children ร— 0.5

ASCII Flamegraph

from metrun import render_ascii, print_ascii

print_ascii(bottlenecks, title="My App Flamegraph")
๐Ÿ”ฅ My App Flamegraph
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
  slow_query    โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ  78.2%  score=12.9
  handler       โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ 100.0%  score=9.4
  serialize     โ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘   5.1%  score=2.1
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

SVG Flamegraph (via flameprof)

from metrun.cprofile_bridge import CProfileBridge
from metrun import render_svg

bridge = CProfileBridge()
with bridge.profile_block():
    my_function()

render_svg(bridge.get_stats(), "flame.svg")
## cProfile Bridge

Integrate with stdlib `cProfile` or any existing `.prof` dump:

```python
from metrun.cprofile_bridge import CProfileBridge
from metrun import analyse, print_report

bridge = CProfileBridge()

@bridge.profile_func
def my_function():
    ...

my_function()

# Analyse with the Bottleneck Engine
bottlenecks = analyse(bridge.to_records())
print_report(bottlenecks)

# Save for snakeviz / flameprof CLI
bridge.save("profile.prof")

Compatible with these popular tools (no code changes needed):

Tool Command
snakeviz โ€” interactive web viewer snakeviz profile.prof
flameprof โ€” SVG flamegraph flameprof profile.prof > flame.svg
py-spy โ€” sampling profiler py-spy record -o flame.svg -- python script.py
viztracer โ€” full trace + HTML flamegraph see below
scalene โ€” line-level CPU+memory python -m scalene script.py

Language-neutral records interchange

metrun can export and import normalised profiling data as JSON.

  • metrun profile my_script.py --export-records profile.json
    • saves the collected records as language-neutral JSON.
  • metrun inspect --records profile.json
    • loads a JSON or JSONL records file produced by metrun or another runtime.
  • metrun inspect --records profile.json --export-records normalized.json
    • loads records, normalises them, and writes them back out as language-neutral JSON.

The importer accepts top-level records, functions, nodes, or items collections, plus single-record objects and mapping-of-records payloads. The language field is preserved when present.

Example payload:

{
  "schema_version": 1,
  "language": "javascript",
  "records": [
    {
      "name": "root",
      "total_time": 1.0,
      "calls": 1,
      "children": ["child"],
      "parents": [],
      "language": "javascript"
    },
    {
      "name": "child",
      "total_time": 0.25,
      "calls": 4,
      "children": [],
      "parents": ["root"],
      "language": "javascript"
    }
  ]
}

For JSONL, write one record per line:

{"name":"root","total_time":1.0,"calls":1,"children":["child"],"language":"javascript"}
{"name":"child","total_time":0.25,"calls":4,"parents":["root"],"language":"javascript"}

pip install viztracer

from viztracer import VizTracer

with VizTracer(output_file="trace.json"): my_function()

Critical Path

from metrun import find_critical_path, print_critical_path, get_records

path = find_critical_path(get_records())
print_critical_path(path)
๐Ÿงจ Critical Path  (depth=3, hottest leaf: 0.4200s)

  handler  [0.9100s, 1 calls]
    โ””โ”€ db_query  [0.6300s, 50 calls]
      โ””โ”€ serialize  [0.4200s, 50 calls]   โ† ๐Ÿ”ฅ hottest leaf (0.4200s)

Fix Suggestion Engine

from metrun import analyse, get_records, suggest, format_suggestions

for b in analyse(get_records()):
    tips = suggest(b)
    print(format_suggestions(b.name, tips))

Suggestion catalogue per diagnosis:

Diagnosis Suggestions
๐Ÿ”ฅ loop hotspot functools.lru_cache, numpy vectorisation, numba @jit
๐ŸŒฒ dependency bottleneck concurrent.futures, asyncio.gather, batching
๐Ÿข slow execution cProfile + snakeviz, algorithmic review, joblib.Memory
Score โ‰ฅ 8 (any) scalene, viztracer

Profile a script โ€” bottleneck report (user code only, stdlib filtered)

metrun profile my_script.py

Profile + ASCII flamegraph in terminal

metrun profile my_script.py --ascii-flame

Profile + save SVG flamegraph

metrun profile my_script.py --flame flame.svg

Full enhanced report: bottlenecks + critical path + suggestions

metrun inspect my_script.py

Export normalised records for another runtime or later analysis

metrun profile my_script.py --export-records profile.json

Analyse language-neutral JSON or JSONL records

metrun inspect --records profile.json metrun inspect --records profile.jsonl

Load, normalise, and re-export language-neutral records

metrun inspect --records profile.json --export-records normalized.json

Include Python stdlib / C-builtins in the report

metrun profile my_script.py --include-stdlib metrun inspect my_script.py --include-stdlib

Auto-scan and generate metrun.toon.yaml metric tree

metrun scan my_script.py --output project/

Scan from pre-collected records

metrun scan --records profile.json --output project/

Convert existing .prof dump to SVG

metrun flame profile.prof -o flame.svg


---

## Automatic project scanning & TOON output

`metrun scan` profiles a Python script (or loads pre-collected records) and
generates a `metrun.toon.yaml` file containing a compact metric tree that
describes the project's performance bottlenecks.

### How it works

1. **Endpoint recognition** โ€” metrun identifies *root* functions (entry points)
   as any function with no recorded callers.  In decorator mode these are the
   top-level `@trace`-d functions; in cProfile mode they are the call-tree
   roots after stdlib filtering.
2. **Profiling** โ€” the script is executed under `cProfile` (via
   `CProfileBridge`) and the resulting call tree is converted to
   `FunctionRecord` entries.
3. **Bottleneck analysis** โ€” the `BottleneckEngine` scores every function and
   assigns a diagnosis label.
4. **Critical path** โ€” a DFS walk finds the hottest rootโ†’leaf chain.
5. **TOON rendering** โ€” all results are formatted into a compact
   `.toon.yaml` file with sections: `SUMMARY`, `BOTTLENECKS`, `CRITICAL-PATH`,
   `SUGGESTIONS`, `ENDPOINTS`, and `TREE`.

# metrun | 2b | top: handler ๐ŸŒฒ | python | 2026-04-07

SUMMARY:
  bottlenecks: 2
  top_score: 11.3
  top_name: handler
  top_diagnosis: ๐ŸŒฒ dependency bottleneck
  total_time: 1.5500s
  total_calls: 101

BOTTLENECKS[2]:
  ๐ŸŒฒ handler                        score=11.3   time=0.8000s (51.6%)  calls=1       dependency bottleneck
  ๐Ÿข slow_query                     score=10.3   time=0.7500s (48.4%)  calls=100     slow execution

CRITICAL-PATH (depth=2, leaf=0.7500s):
  handler โ†’ slow_query โ† ๐Ÿ”ฅ

SUGGESTIONS[2]:
  handler: Run independent child calls concurrently [concurrent.futures]
  slow_query: Profile deeper with cProfile + snakeviz [cProfile / snakeviz]

ENDPOINTS[1]:
  handler  calls=1  time=0.8000s  children=1

TREE:
  ๐ŸŒฒ handler  0.8000s  ร—1
  โ”‚ โ”œโ”€ ๐Ÿข slow_query  0.7500s  ร—100

Python API

from metrun import analyse, get_records, generate_toon, save_toon

bottlenecks = analyse(get_records())
toon = generate_toon(bottlenecks, get_records())
save_toon(toon, "project/metrun.toon.yaml")

Integration with project.sh

metrun scan demo.py --output project/

The generated metrun.toon.yaml sits alongside other TOON files (analysis.toon.yaml, duplication.toon.yaml, validation.toon.yaml, etc.) and gives a performance perspective on the project.


Architecture

  @trace / section()          cProfile.Profile
       โ”‚                            โ”‚
       โ–ผ                            โ–ผ
 ExecutionTracer              CProfileBridge
  (FunctionRecord)             .to_records()
       โ”‚                            โ”‚
       โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                  โ–ผ
         BottleneckEngine.analyse()
          score + diagnosis + rank
                  โ”‚
       โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
       โ–ผ          โ–ผ              โ–ผ
  print_report  find_critical  suggest()
  (report.py)    _path()      (suggestions.py)
                            
  ASCII/SVG flamegraph โ† flamegraph.py

The two tracing backends (ExecutionTracer for decorator/section API and CProfileBridge for cProfile API) both produce the same Dict[str, FunctionRecord] structure consumed by the engine.

Module overview

metrun/
โ”œโ”€โ”€ profiler.py        # ExecutionTracer โ€” decorator + context-manager tracing
โ”œโ”€โ”€ bottleneck.py      # BottleneckEngine โ€” score, diagnosis, ranking
โ”œโ”€โ”€ report.py          # Human Report Generator
โ”œโ”€โ”€ critical_path.py   # Critical path analysis (DFS on call graph)
โ”œโ”€โ”€ suggestions.py     # Fix Suggestion Engine
โ”œโ”€โ”€ flamegraph.py      # ASCII + SVG (flameprof) flamegraphs
โ”œโ”€โ”€ cprofile_bridge.py # cProfile โ†” metrun bridge
โ”œโ”€โ”€ toon.py            # TOON metric-tree generator (metrun.toon.yaml)
โ””โ”€โ”€ cli.py             # Click CLI entry-point

Known limitations

Limitation Detail
Name collisions in cProfile mode CProfileBridge.to_records() uses function name only as key (no file:lineno) โ€” functions with the same name in different modules are merged
Decorator tracing is opt-in Only functions decorated with @trace or wrapped in section() appear in get_records() โ€” not the full call tree
Thread-local call stack Each thread has an independent call stack; cross-thread parentโ†’child links are not recorded
No async support asyncio coroutines are not automatically traced by the decorator backend

cProfile filtering

By default CProfileBridge.to_records() and the CLI commands strip Python stdlib, C-builtins, anonymous entries (<module>, <genexpr>, etc.) and metrun's own internals โ€” so the report focuses on user code only. Call graph connectivity is maintained through bridging: filtered intermediate nodes (e.g. decorator wrappers) are transparently traversed when rebuilding parentโ†’child links.

records = bridge.to_records()                    # user code only (default)
records = bridge.to_records(exclude_stdlib=False) # full call tree

License

Licensed under Apache-2.0.

Status

Last updated by taskill at 2026-04-25 13:40 UTC

Metric Value
HEAD f5ac1b7
Coverage โ€”
Failing tests โ€”
Commits in last cycle 18

Added documentation and examples for a configuration management system, expanded the code analysis engine and code quality metrics, and introduced profiling utilities (flamegraph, critical path, cProfile bridge) plus CLI improvements. Several docs/examples/tests were refactored and a bottleneck engine/report was merged.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

metrun-0.1.14.tar.gz (50.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

metrun-0.1.14-py3-none-any.whl (41.0 kB view details)

Uploaded Python 3

File details

Details for the file metrun-0.1.14.tar.gz.

File metadata

  • Download URL: metrun-0.1.14.tar.gz
  • Upload date:
  • Size: 50.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.7

File hashes

Hashes for metrun-0.1.14.tar.gz
Algorithm Hash digest
SHA256 3365713412e2c6c0407c4eb88fac4cb97135660a73ad471bb0f375dda74d1bd6
MD5 716d5509b3d3eea0313eddc4989f8de8
BLAKE2b-256 279c14d26c658c21d36de6de760e4d55a5f18f3bd18fdaa867c5981a472161a8

See more details on using hashes here.

File details

Details for the file metrun-0.1.14-py3-none-any.whl.

File metadata

  • Download URL: metrun-0.1.14-py3-none-any.whl
  • Upload date:
  • Size: 41.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.7

File hashes

Hashes for metrun-0.1.14-py3-none-any.whl
Algorithm Hash digest
SHA256 88e736850543116edc646ba7f3d7b7cb48af3a7ec27cc0fee5f37c8aaca60230
MD5 3ba0cbe6cc71cd8b1c44b9f71fd277bb
BLAKE2b-256 21b930d512642d0b922286d861765214820b20ab5900dd1683f026a178680594

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page