Last active
December 15, 2015 22:58
-
-
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).
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
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