Python: functools, wrapper decorator for methods: make ipython return proper definition when using '?'
Pergunta
I'm using functools to make a decorator, that allows me to log the details of a method call.
I got a lot of help here to write it...it's not my own and I'm still learning how it even works...
I work out of ipython a lot and when I use:
import Tools
imagetools = Tools.Tools()
imagetools.save_new_image ?
Type: instancemethod
String Form:<bound method ImageTools.save_new_image of <ImageTools.ImageTools object at 0x305d1d0>>
File: /fs/corpus6/dpc/workspace/python/Modules/MiscTools.py
Definition: imagetools.save_new_image(self, *args, **kwargs)
Docstring: <no docstring>
This is my problem line...besides that I didn't write doc string yet... :|
Definition: imagetools.save_new_image(self, *args, **kwargs)
I'd it to be:
save_new_image_clone(self, data, header, outfile, coordmap)
Wrapper:
import functools
def log_method(func):
@functools.wraps(func)
def wrapper(self, *args, **kwargs):
self.__doc__ = func.__doc__
# allows me to put the wrapper in helper methods that are sometimes major steps
if check_for_True_in_last_arg(*args):
log.debug('''Last arg is "True" which could indicate the caller
intends the log to work on this method. For that behaviour, use the keyword arg: log_this=True''')
if 'log_this' in kwargs and kwargs['log_this'] is not False:
args_name = inspect.getargspec(func)[0][1:] #drop the 'self' arg
args_dict = dict(list(itertools_izip(args_name, args)) + list(kwargs.iteritems()))
method_name = wrapper.__name__
args_dict_string = '\nMETHOD: ' + method_name + '\n'
for k,v in args_dict.iteritems():
if type(v) is not np.ndarray:
args_dict_string += "%s: %s\n" %(k,v)
elif type(v) is np.ndarray:
args_dict_string += "NUMPY ARRAY:%s \nSHAPE:%s\n" %(k, str(v.shape))
args_dict_string += '\n'
log.info(args_dict_string)
return func(self, *args, **kwargs)
return wrapper
UPDATE: From within ipython I did this to see what's happening, but it didn't help me yet..
help??
Type: _Helper
String Form:Type help() for interactive help, or help(object) for help about object.
File: /usr/lib64/python2.7/site.py
Definition: help(self, *args, **kwds)
Source:
class _Helper(object):
"""Define the builtin 'help'.
This is a wrapper around pydoc.help (with a twist).
"""
def __repr__(self):
return "Type help() for interactive help, " \
"or help(object) for help about object."
def __call__(self, *args, **kwds):
import pydoc
return pydoc.help(*args, **kwds)
Call def: help(self, *args, **kwds)
Solução
What you are trying to do -- make a decorator whose call signature is identical to the function being decorated -- is not a simple thing to do from scratch. There is a third-party module, called decorator, which can do this however:
import decorator
@decorator.decorator
def log_method(func, *args, **kwargs):
# allows me to put the wrapper in helper methods that are sometimes major steps
if check_for_True_in_last_arg(*args):
log.debug('''Last arg is "True" which could indicate the caller
intends the log to work on this method. For that behaviour, use the keyword arg: log_this=True''')
if 'log_this' in kwargs and kwargs['log_this'] is not False:
args_name = inspect.getargspec(func)[0][1:] #drop the 'self' arg
args_dict = dict(list(itertools_izip(args_name, args)) + list(kwargs.iteritems()))
method_name = wrapper.__name__
args_dict_string = '\nMETHOD: ' + method_name + '\n'
for k,v in args_dict.iteritems():
if type(v) is not np.ndarray:
args_dict_string += "%s: %s\n" %(k,v)
elif type(v) is np.ndarray:
args_dict_string += "NUMPY ARRAY:%s \nSHAPE:%s\n" %(k, str(v.shape))
args_dict_string += '\n'
log.info(args_dict_string)
return func(self, *args, **kwargs)
class Tools(object):
@log_method
def save_new_image(self, data, header, outfile, coordmap):
"""
Jazz the snaz output baz
"""
Then from IPython:
In [14]: imagetools = Tools()
In [15]: imagetools.save_new_image?
Type: instancemethod
String Form:<bound method Tools.save_new_image of <__main__.Tools object at 0xae1e20c>>
File: /tmp/python-2973tNk.py
Definition: imagetools.save_new_image(self, data, header, outfile, coordmap)
Docstring: Jazz the snaz output baz
If you want to see the Python code which achieves this, see the decorator.FunctionMaker class. It uses inspect
to figure out the signature of the original function, then uses string formating to write a def-statement
defining the decorated function. Then it calls exec code in evaldict
to execute the string.