Last active
December 15, 2020 20:43
-
-
Save dutc/838340dc8b67b2eb57fc7849edef612f to your computer and use it in GitHub Desktop.
Call Graph Generation (as seen on the Don't Use This Code mailing list)
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 | |
from networkx import DiGraph | |
from networkx.drawing.nx_pydot import to_pydot | |
from contextlib import contextmanager | |
from sys import setprofile | |
from collections import namedtuple | |
class Ident(namedtuple('IdentBase', 'filename lineno name')): | |
def __str__(self): | |
return f'"{self.filename}\n{self.lineno}\n{self.name}"' | |
@contextmanager | |
def callgraph(root=Ident('<context manager>', -1, '')): | |
def cb(frame, event, arg): | |
nonlocal current | |
if current is root and g.out_edges(root): | |
return | |
if event == 'call': | |
code = frame.f_code | |
g.add_edge(current, current := Ident(code.co_filename, code.co_firstlineno, code.co_name)) | |
elif event == 'return': | |
current = next(g.predecessors(current), root) | |
g = DiGraph() | |
g.add_node(current := root) | |
try: | |
setprofile(cb) | |
yield g | |
finally: | |
setprofile(None) | |
# NOTE|dutc: don't uncomment this is if you want to sleep at night | |
# from io import StringIO | |
# data = StringIO(''' | |
# a, b, c | |
# 1, 10, 100 | |
# 2, 20, 200 | |
# ''') | |
# from pandas import read_csv | |
# with callgraph() as cg: | |
# df = read_csv(data) | |
# from matplotlib.pyplot import subplots | |
# with callgraph() as cg: | |
# subplots(2, 2) | |
# from pandas import date_range | |
# with callgraph() as cg: | |
# date_range(start='2020-01-01', end='2020-07-04', tz='America/Los_Angeles') | |
# def f(): g() | |
# def g(): h(); k() | |
# def h(): pass | |
# def k(): pass | |
# with callgraph() as cg: | |
# f() | |
files = {n.filename for n in cg.nodes()} | |
funcs = {n.name for n in cg.nodes()} | |
print(f'{len(files)} files, {len(funcs)} funcs') | |
# # NOTE|dutc: requires python-pydot | |
from matplotlib.pyplot import imshow, show, axis | |
from matplotlib.image import imread | |
from io import BytesIO | |
(png := BytesIO()).write(to_pydot(cg).create_png(prog='dot')); png.seek(0) | |
axis('off'); imshow(imread(png), aspect='equal'); show() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment