Skip to content

Instantly share code, notes, and snippets.

@wware
Last active December 15, 2015 22:58
Show Gist options
  • Save wware/5336328 to your computer and use it in GitHub Desktop.
Save wware/5336328 to your computer and use it in GitHub Desktop.
Handy debug thing for logging the entry and exit of functions and methods, showing arguments and return values. I found this useful for seeing how things work in an unfamiliar library (in this case, django-haystack).
import logging
import sys
import types
# It's great to log the entering/leaving of methods and functions, but it would be way cool
# to see who gets called by the function you go into. That probably means hacking ASTs and
# finding the GOSUB bytecode.
def discover(obj, filtfunc=None):
if filtfunc is None:
filtfunc = lambda attr: True
return dict([(attr, getattr(obj, attr)) for attr in dir(obj) if filtfunc(attr)])
def investigate(container, name, output=sys.stderr, indent=[0], debug=False, special=None, include=[]):
obj = getattr(container, name)
if isinstance(obj, types.ClassType):
investigate_in(obj, include=include, output=output, debug=debug, indent=indent)
elif callable(obj):
indent_step = 8 * ' '
file = lineno = None
if isinstance(obj, types.MethodType):
objname = str(obj.im_class) + '.' + obj.im_func.__name__
file = obj.im_func.func_code.co_filename
lineno = obj.im_func.func_code.co_firstlineno
elif isinstance(obj, types.FunctionType):
file = obj.func_code.co_filename
lineno = obj.func_code.co_firstlineno
objname = container.__name__ + '.' + name
else:
objname = str(obj)
def emit(x, output=output):
if isinstance(output, logging.Logger):
output.debug(x)
else:
output.write(x + '\n')
def enter(args, kwargs, output=output, file=file, line=lineno, special=special, objname=objname):
if file is not None:
emit('%s%s:%d' % (indent_step * indent[0], file, line))
emit('%sEnter %s, args=%s, kwargs=%s' % (indent_step * indent[0], objname, args, kwargs))
if special is not None:
special(*args, **kwargs)
indent[0] += 1
def leave(result, output=output, objname=objname):
indent[0] -= 1
emit('%sLeave %s, returns %s' % (indent_step * indent[0], objname, result))
if isinstance(container, types.InstanceType) or isinstance(container, types.ClassType):
if getattr(container, name).im_self is None:
def wrapped_callable(cls, *args, **kwargs):
enter(args, kwargs)
result = obj(cls, *args, **kwargs)
leave(result)
return result
if debug:
emit('Instrumenting %s:%s' % (container, name))
setattr(container, name, wrapped_callable)
else:
def wrapped_callable(cls, *args, **kwargs):
enter(args, kwargs)
result = obj(*args, **kwargs)
leave(result)
return result
if debug:
emit('Instrumenting %s:%s' % (container, name))
setattr(container, name, classmethod(wrapped_callable))
else:
def wrapped_callable(*args, **kwargs):
enter(args, kwargs)
result = obj(*args, **kwargs)
leave(result)
return result
if debug:
emit('Instrumenting %s:%s' % (container, name))
setattr(container, name, wrapped_callable)
def investigate_in(container, include=[], output=sys.stderr, debug=False, indent=[0]):
for attr in dir(container):
if (not attr.startswith('_')) or attr in include:
investigate(container, name=attr, output=output, debug=debug, indent=indent)
# example usage
from haystack.views import SearchView
from haystack.fields import SearchField
investigate(SearchView, 'get_results')
investigate(SearchField, 'prepare_template')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment