Skip to content

Instantly share code, notes, and snippets.

@dutc
Last active December 15, 2020 20:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dutc/838340dc8b67b2eb57fc7849edef612f to your computer and use it in GitHub Desktop.
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)
#!/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