Skip to content

Instantly share code, notes, and snippets.

@kezabelle
Created May 7, 2021 14:47
Show Gist options
  • Save kezabelle/caf342ffd8a05570583985849985bf47 to your computer and use it in GitHub Desktop.
Save kezabelle/caf342ffd8a05570583985849985bf47 to your computer and use it in GitHub Desktop.
lets play at tracking calls and the locals at the point of return ...
import sys
import threading
from operator import itemgetter
from pprint import pprint, saferepr
keyonly = itemgetter(0)
class ZMixin:
def mixin_via_z(self) -> int:
varz = 999
return varz
def goes_through_everything(self) -> None:
varz = 'whee'
return None
class YMixin:
def mixin_via_y(self) -> int:
vary = 998
return vary
def goes_through_everything(self) -> None:
vary = super().goes_through_everything()
return vary
class XMixin:
def mixin_via_x(self) -> int:
varx = 997
return varx
def goes_through_everything(self) -> None:
varx = super().goes_through_everything()
return varx
class A(ZMixin):
def __init__(self):
test = 'in a'
super().__init__()
def test_c_to_a(self) -> int:
return 1
def test_b_to_a_via_c(self) -> int:
return 3
def test_c_to_a_through_b(self) -> int:
return 4
def goes_through_everything(self) -> None:
vara = super().goes_through_everything()
return vara
class B(YMixin, A):
def __init__(self):
test = 'in b'
super().__init__()
def test_c_to_b(self) -> int:
return 2
def test_b_to_a_via_c(self) -> int:
return super().test_b_to_a_via_c()
def test_c_to_a_through_b(self) -> int:
return super().test_c_to_a_through_b()
def goes_through_everything(self) -> None:
varb = super().goes_through_everything()
return varb
class C(XMixin, B):
def __init__(self):
test = 'in c'
super().__init__()
def test_c_to_a(self) -> int:
return super().test_c_to_a()
def test_c_to_b(self) -> int:
return super().test_c_to_b()
def test_c_to_a_through_b(self) -> int:
return super().test_c_to_a_through_b()
def goes_through_everything(self) -> None:
varc = super().goes_through_everything()
return varc
localvars = threading.local()
# In case you're a doofus like I am, it's worth pointing out that printing or
# pprinting a dictionary doesn't necessarily output it in the same insertion
# order it is, which caused me to scratch my head a bunch and debug a problem
# that doesn't exist if you just iterate over .items()
localvars.seen = {}
def tracefunc(frame, event, arg):
def update_seen(_theframe_):
name = _theframe_.f_code.co_name
vars = _theframe_.f_locals
if 'self' in vars:
self = vars['self']
ancestors = self.__class__.__mro__
template = "%s.%s.%s"
if '__class__' in vars:
currentcls = vars['__class__']
return template % (currentcls.__module__, currentcls.__qualname__, name)
else:
for currentcls in reversed(ancestors):
if hasattr(currentcls, name):
return template % (currentcls.__module__, currentcls.__qualname__, name)
return ""
if event == 'call':
currentcls = update_seen(frame)
localvars.seen[currentcls] = 1
if event == "return":
currentcls = update_seen(frame)
local_reprs = {
lname: saferepr(lval)
for lname, lval in sorted(frame.f_locals.items(), key=keyonly)
if lname != "self" and lname[0:2] != "__"
}
localvars.seen[currentcls] = local_reprs
sys.setprofile(tracefunc)
thing = C()
thing.mixin_via_z()
thing.mixin_via_y()
thing.mixin_via_x()
thing.test_c_to_a()
thing.test_c_to_b()
thing.test_b_to_a_via_c()
thing.test_c_to_a_through_b()
thing.goes_through_everything()
sys.setprofile(None)
for k, v in localvars.seen.items():
pprint(k)
pprint(v)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment