Python: functools, wrapper decorator for methods: make ipython return proper definition when using '?'

StackOverflow https://stackoverflow.com//questions/25085601

  •  02-01-2020
  •  | 
  •  

Question

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)
Était-ce utile?

La solution

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.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top