Metadata-Version: 2.4
Name: tracefunc
Version: 0.0.6
Summary: AST-level execution tracing via sys.monitoring
Home-page: https://github.com/AnswerDotAI/tracefunc
Author: Jeremy Howard
Author-email: github@jhoward.fastmail.fm
License: Apache Software License 2.0
Keywords: nbdev jupyter notebook python
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Natural Language :: English
Classifier: Programming Language :: Python :: 3.12
Classifier: License :: OSI Approved :: Apache Software License
Requires-Python: >=3.12
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: dev
Dynamic: author
Dynamic: author-email
Dynamic: classifier
Dynamic: description
Dynamic: description-content-type
Dynamic: home-page
Dynamic: keywords
Dynamic: license
Dynamic: license-file
Dynamic: provides-extra
Dynamic: requires-python
Dynamic: summary

# tracefunc


<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->

`tracefunc` takes a function and its arguments, executes it, and returns
a list of per-call traces. Each entry is `(stack_str, trace_dict)` where
`stack_str` is the call stack (filtered so `fn` is the shallowest frame
shown; empty when `target_func` is `None`) and `trace_dict` maps
AST-level snippets to `(hit_count, vars_map)` with per-hit samples.
Comprehensions show up as their own lines with per-iteration values.

## Install

``` sh
pip install tracefunc
```

Requires Python 3.12+ (uses sys.monitoring instruction events).

## How to use

``` python
from tracefunc import tracefunc
from pprint import pprint
```

### Simple function

Here’s a simple example tracing a loop:

``` python
def demo(n):
    total = 0
    for i in range(n): total += i
    return total
```

``` python
def show_res(x):
    for snippet, (hits, vars_map) in x.items():
        print('-', repr(snippet), hits)
        pprint(vars_map)
```

``` python
stack, result = tracefunc(demo, 3, target_func=demo)[0]
print(stack)
show_res(result)
```

    demo (2643322203.py:1)
    - 'total = 0' 1
    {'total': [('int', '0')]}
    - 'for i in range(n):' 4
    {'i': [('int', '0'), ('int', '1'), ('int', '2'), ('int', '2')],
     'n': [('int', '3'), ('int', '3'), ('int', '3'), ('int', '3')],
     'range': [('type', "<class 'range'>"),
               ('type', "<class 'range'>"),
               ('type', "<class 'range'>"),
               ('type', "<class 'range'>")]}
    - 'total += i' 3
    {'i': [('int', '0'), ('int', '1'), ('int', '2')],
     'total': [('int', '0'), ('int', '1'), ('int', '3')]}
    - 'return total' 1
    {'total': [('int', '3')]}

### Multiple statements on one physical line

Semicolon-separated statements are tracked separately.

``` python
def one_liner(): x = 1; y = 2; return x + y

_, res = tracefunc(one_liner)[0]
show_res(res)
```

    - 'x = 1' 1
    {'x': [('int', '1')]}
    - 'y = 2' 1
    {'y': [('int', '2')]}
    - 'return x + y' 1
    {'x': [('int', '1')], 'y': [('int', '2')]}

### Targeted tracing and call stacks

You can trace a specific target function and see the call stack for each
call. Stack paths are shown relative to `fn`’s directory when possible.

``` python
def target(x):
    return x + 1

def another(x): return target(x)

def wrapper(n):
    out = []
    for i in range(n): out.append(target(i))
    out.append(another(10))
    return out

for stack, res in tracefunc(wrapper, 2, target_func=target):
    print(stack)
    show_res(res)
```

    wrapper (1041865549.py:8)
    target (1041865549.py:1)
    - 'return x + 1' 1
    {'x': [('int', '0')]}
    wrapper (1041865549.py:8)
    target (1041865549.py:1)
    - 'return x + 1' 1
    {'x': [('int', '1')]}
    wrapper (1041865549.py:9)
    another (1041865549.py:4)
    target (1041865549.py:1)
    - 'return x + 1' 1
    {'x': [('int', '10')]}

### Nested function

Nested definitions appear as statements, and their bodies are traced
when called.

``` python
def outer(x):
    def inner(y):
        return x + y
    return inner(5)

_, res = tracefunc(outer, 10)[0]
show_res(res)
```

    - 'def inner(y):' 1
    {'inner': [('function', '<function outer.<locals>.inner>')]}
    - 'return x + y' 1
    {'x': [('int', '10')], 'y': [('int', '5')]}
    - 'return inner(5)' 1
    {'inner': [('function', '<function outer.<locals>.inner>')]}
