Lightweight Python tracing utility — trace function calls, variables, expressions, and code blocks via console and webhook
Project description
pyfollow
Lightweight Python tracing utility. Trace function calls, variables, expressions, and code blocks — output to console, webhook, or both.
Zero dependencies. Pure stdlib.
- Installation
- Quick Start
- API Reference
- Configuration
- Webhook
- Project Structure
- Tests
- Contributing
- License
Installation
pip install pyfollow
Or copy the pyfollow/ directory into your project.
from pyfollow import follow
Quick Start
Console tracing
from pyfollow import follow
t = follow.init()
@t.function
def add(a, b):
return a + b
x = 1
t.var(x)
x = x + 5
r = add(3, 7)
t.flush()
Output:
[pyfollow.function] add:
[pyfollow.function] call add(a=3, b=7)
[pyfollow.var] x = 1
[pyfollow.var] x change: 6 [changed from 1]
[pyfollow.function] add -> 10
Webhook tracing
from pyfollow import follow
t = follow.init({"webhook_url": "http://localhost:8000/webhook"})
@t.function(usecode="batch1")
def compute(n):
return n * 2
compute(5)
t.sync("batch1")
t.flush()
Variable watching
from pyfollow import follow
t = follow.init()
x = 10
t.var(x) # trace init: x = 10
x = 20 # auto-detect: x changed from 10 to 20
print(repr(x)) # auto-detect: x read
t.unvar("x") # stop watching
Block tracing
from pyfollow import follow
t = follow.init()
with t:
a = 42
b = a + 1
print(b)
Expression tracing
from pyfollow import follow
t = follow.init()
t(1 + 2) # [pyfollow] t(1 + 2) -> 3
t(sorted([3,1,2])) # [pyfollow] t(sorted([3,1,2])) -> [1, 2, 3]
API Reference
follow.init(config=None)
Create a new Follow instance.
t = follow.init() # default
t = follow.init({"webhook_url": "http://...", "uselogs": False})
t.config(**kwargs)
Update configuration at runtime.
t.config(uselogs=False)
t.config(webhook_url="http://localhost:8000/webhook")
@t.function
Decorator that traces function calls and returns.
@t.function
def my_func(x):
return x + 1
@t.function(usecode="tag")
Decorator with deferred sync — events are buffered under a tag until t.sync("tag") is called.
@t.function(usecode="batch1")
def compute(n):
return n * 2
compute(5) # buffered, not sent
t.sync("batch1") # flush only batch1 events
t.var(value, name=None)
Watch a variable. Detects init, changes, and reads (via repr()/str()).
x = 1
t.var(x) # log: x = 1
x = 2 # log: x changed from 1 to 2
t.unvar(name)
Stop watching a variable.
t.unvar("x")
t(expr)
Trace an expression — logs the source code line and its result.
t(1 + 2) # log: t(1 + 2) -> 3
with t:
Context manager that traces every line inside the block.
with t:
x = 1
y = x + 1
t.sync(code=None)
Flush buffered events to webhook.
t.sync()— flush all untagged eventst.sync("tag")— flush only events with matching tag
t.flush()
Block until all pending webhook sends complete.
Configuration
| Key | Type | Default | Description |
|---|---|---|---|
webhook_url |
str |
None |
URL for HTTP POST event delivery |
uselogs |
bool |
True |
Print trace events to console |
# Console only (default)
t = follow.init()
# Webhook only
t = follow.init({"webhook_url": "http://localhost:8000/webhook", "uselogs": False})
# Both
t = follow.init({"webhook_url": "http://localhost:8000/webhook"})
Webhook
Event types
type |
Trigger |
|---|---|
var_init |
t.var(x) called |
var (change) |
Variable reassigned |
var (read) |
Variable accessed via repr()/str() |
function_call |
Traced function called |
function_return |
Traced function returned |
block_start |
with t: entered |
block_line |
Line executed inside block |
block_end |
with t: exited |
expression |
t(expr) evaluated |
Event payload example
{
"type": "function_call",
"name": "add",
"args": "a=2, b=3",
"source": "def add(a, b):\n return a + b"
}
Server example (FastAPI)
cd examples/server
uvicorn main:app --reload
# examples/server/main.py
from fastapi import FastAPI, Request
app = FastAPI()
@app.post("/webhook")
async def webhook(request: Request):
events = await request.json()
for event in events:
print(event)
return {"status": "ok"}
Client example
python examples/webhook_client.py
Project Structure
trace-module-python/
├── pyproject.toml # Build config
├── README.md # Documentation
├── LICENSE # MIT License
├── TODO.md # Task tracking
├── pyfollow/ # Package
│ ├── __init__.py # Public API + metadata
│ ├── follow.py # Follow class (core orchestrator)
│ ├── watcher.py # _FollowedVar proxy + VarWatcher (bytecode)
│ └── sender.py # WebhookSender (async queue + retry)
├── tests/
│ └── test_trace.py # 17 pytest tests
└── examples/
├── simple.py # Console demo
├── webhook_client.py # Webhook demo
└── server/
└── main.py # FastAPI webhook receiver
Tests
pip install pytest
python -m pytest tests/ -v
How It Works
pyfollow hooks into Python's sys.settrace machinery to intercept line execution events. For variable watching, it uses dis (bytecode disassembly) to detect LOAD_FAST/LOAD_GLOBAL instructions that read watched variables.
The _FollowedVar proxy wraps values transparently — all dunder methods are forwarded to the original object, making the wrapper invisible to user code. Reads are detected when repr() or str() is called on the proxy.
Webhook delivery runs on a background daemon thread with a queue, retrying failed requests up to 2 times with 1-second backoff.
Contributing
Contributions welcome. Open an issue or submit a PR on GitHub.
Author
License
MIT © 2026 Rayan Rav
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pyfollow-0.1.2.tar.gz.
File metadata
- Download URL: pyfollow-0.1.2.tar.gz
- Upload date:
- Size: 13.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
efcecc5b37df77855de0763fab847f283aa67b260628596f6b5708a8bc864825
|
|
| MD5 |
247fc52ec9d011026e72a9aabbf349dd
|
|
| BLAKE2b-256 |
c5457eb0ff64f41a50508f640f02a696eed46e277665ce181a6f0e614f8e7f9d
|
Provenance
The following attestation bundles were made for pyfollow-0.1.2.tar.gz:
Publisher:
release.yml on RayanBO/trace-module-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyfollow-0.1.2.tar.gz -
Subject digest:
efcecc5b37df77855de0763fab847f283aa67b260628596f6b5708a8bc864825 - Sigstore transparency entry: 2174456559
- Sigstore integration time:
-
Permalink:
RayanBO/trace-module-python@42412591a37cee02b46089e1f95b3753a045d3d4 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/RayanBO
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@42412591a37cee02b46089e1f95b3753a045d3d4 -
Trigger Event:
push
-
Statement type:
File details
Details for the file pyfollow-0.1.2-py3-none-any.whl.
File metadata
- Download URL: pyfollow-0.1.2-py3-none-any.whl
- Upload date:
- Size: 9.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0b7ea6d8374318093debceb6c0d71bd2cdddba1404ba33c0bb3bffe6706a8bde
|
|
| MD5 |
67ea048f50c6ab9e5f840012f35bb8f1
|
|
| BLAKE2b-256 |
7b4f0cdaac18065068b8c673dd70ceeada496488ba0a71f39eec1246924605f2
|
Provenance
The following attestation bundles were made for pyfollow-0.1.2-py3-none-any.whl:
Publisher:
release.yml on RayanBO/trace-module-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyfollow-0.1.2-py3-none-any.whl -
Subject digest:
0b7ea6d8374318093debceb6c0d71bd2cdddba1404ba33c0bb3bffe6706a8bde - Sigstore transparency entry: 2174456649
- Sigstore integration time:
-
Permalink:
RayanBO/trace-module-python@42412591a37cee02b46089e1f95b3753a045d3d4 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/RayanBO
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@42412591a37cee02b46089e1f95b3753a045d3d4 -
Trigger Event:
push
-
Statement type: