Skip to content

Instantly share code, notes, and snippets.

@kezabelle
Created June 15, 2016 10:07
Show Gist options
  • Save kezabelle/d24d529a6f47eb515201bfa84ee6550e to your computer and use it in GitHub Desktop.
Save kezabelle/d24d529a6f47eb515201bfa84ee6550e to your computer and use it in GitHub Desktop.
Peeking into a class' API usage by smashing over __getattribute__
from collections import namedtuple, defaultdict
from operator import itemgetter
ClassUsage = namedtuple('ClassUsage', 'cls attrs')
AttributeUsage = namedtuple('AttributeUsage', 'name count')
attrname = itemgetter(0)
attrcount = itemgetter(1)
def print_handler(data):
print(data)
def peek(handler=None):
if handler is None:
handler = print_handler
def class_decorator(cls):
def printer(self, item):
if not item.startswith('_'):
if not hasattr(cls, "_attributes_used"):
cls._attributes_used = defaultdict(int)
cls._attributes_used[item] += 1
out = tuple(
AttributeUsage(name=attrname(x), count=attrcount(x))
for x in sorted(cls._attributes_used.items(), key=attrname)
)
usage = ClassUsage(cls=cls, attrs=out)
handler(usage)
return super(cls, self).__getattribute__(item)
setattr(cls, '__getattribute__', printer)
return cls
return class_decorator
@peek() # I can't figure out how to get rid of the parens :(
class Lol(...):
pass
@kezabelle
Copy link
Author

In an ideal world, one could do:

@peek
class Lol(...):

and it'd Just Work, defaulting to the print handler.
In reality, without the parens, handler is Lol, not None so one has to provide () to ensure immediate evaluation

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment