Last active
July 3, 2024 01:26
-
-
Save innateessence/40df68d4ce12531bea8b2817321611de to your computer and use it in GitHub Desktop.
Quick and Dirty custom python tracer ; trace all function calls for only the functions you wrote ; written in less than 10 minutes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import os | |
import re | |
import sys | |
''' | |
Example of how to dynamically trace all function calls for your python project, and not any other python code. | |
use case: | |
- You wrote a lot of code, there's a bug, you don't know where it is, and you know the bug doesn't exist in how you're using some 3rd party code | |
''' | |
FilePath = str | |
FuncName = str | |
func_names = [] | |
def _resolve_python_files(path: str = ".") -> list[FilePath]: | |
"""Recursively resolve all python files in a directory""" | |
python_files = [] | |
for root, dirs, files in os.walk(path): | |
for file in files: | |
if file.endswith(".py"): | |
python_files.append(os.path.join(root, file)) | |
for dir in dirs: | |
python_files.extend(_resolve_python_files(dir)) | |
return python_files | |
def _resolve_func_names(file_path: str = ".") -> list[FuncName]: | |
with open(file_path, "r") as file: | |
text = file.read() | |
func_names = re.findall(r"def\s+(\w+)\s*\(", text) | |
return func_names | |
def trace(frame, event, arg=None): | |
global func_names | |
code = frame.f_code | |
func_name = code.co_name | |
line_no = frame.f_lineno | |
if func_name in func_names: | |
print(f"A {event} encountered in {func_name}() at line number {line_no}") | |
return trace | |
if __name__ == "__main__": | |
for _file in _resolve_python_files(): | |
func_names.extend(_resolve_func_names(_file)) | |
sys.settrace(trace) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This needs to be included in your projects python entrypoint.
Can wrap this functionality into a single function that you can call from a module for portability purposes.