How can I programmatically change the argspec of the * not * function in a python decorator?

Very closely related to: How can I programmatically change the argspec argument in a python decorator?

The decorator module provides tools for creating a decorator function that stores the argument of the decorated function.

If I define a function that is not used as a decorator, is there a way to copy another argspec function?

Usage example:

class Blah(object): def foo(self, *args, **kwargs): """ a docstr """ result = bar(*args, **kwargs) result = result**2 # just so it clear we're doing something extra here... return result def bar(x, y, z=1, q=2): """ a more useful docstr, saying what x,y,z,q do """ return x+y*z+q 

I would like foo argspec to look like bar , but the source remains unchanged (i.e. inspect.getsource(foo) will still show garbage result ). The main purpose of this is to get sphinx sprites and ipython online help to display the corresponding arguments.

As an answer to another question, the decorator package shows a way to do this, but I got lost in the meat is the code. It seems that the decorator package is recompiling the source or something like that. I was hoping for a simpler approach, for example. something like foo.argspec = bar.argspec would be possible.

+6
source share
1 answer

A decorator is just a function that does something with another function. So, technically, you can put the required code directly under the foo method, and then, technically, you would change foo without using a decorator, but that would be a terrible mess.

The easiest way to do what you want is to make a decorator that takes a second function ( bar in this case) as an argument so that it knows which signature should copy. The class code would then look something like this:

 class Blah(object): @copy_argspec(bar) def foo(self, *args, **kwargs): """ a docstr """ result = bar(*args, **kwargs) result = result**2 # just so it clear we're doing something extra here... return result 

You must have the bar specified earlier, and not after the class.

.
.
.
,, Time passes.,.
.
.

Well, fortunately, I found an old decorator that I could fit.

help(Blah.foo) looks like this:

 Help on method foo in module __main__: foo(self, *args, **kwargs) unbound __main__.Blah method a docstr 

and after registration looks like this:

 Help on method foo in module __main__: foo(self, x, y, z=1, q=2) unbound __main__.Blah method a more useful docstr, saying what x,y,z,q do 

Here is the decorator I used:

 import inspect class copy_argspec(object): """ copy_argspec is a signature modifying decorator. Specifically, it copies the signature from `source_func` to the wrapper, and the wrapper will call the original function (which should be using *args, **kwds). The argspec, docstring, and default values are copied from src_func, and __module__ and __dict__ from tgt_func. """ def __init__(self, src_func): self.argspec = inspect.getargspec(src_func) self.src_doc = src_func.__doc__ self.src_defaults = src_func.func_defaults def __call__(self, tgt_func): tgt_argspec = inspect.getargspec(tgt_func) need_self = False if tgt_argspec[0][0] == 'self': need_self = True name = tgt_func.__name__ argspec = self.argspec if argspec[0][0] == 'self': need_self = False if need_self: newargspec = (['self'] + argspec[0],) + argspec[1:] else: newargspec = argspec signature = inspect.formatargspec( formatvalue=lambda val: "", *newargspec )[1:-1] new_func = ( 'def _wrapper_(%(signature)s):\n' ' return %(tgt_func)s(%(signature)s)' % {'signature':signature, 'tgt_func':'tgt_func'} ) evaldict = {'tgt_func' : tgt_func} exec new_func in evaldict wrapped = evaldict['_wrapper_'] wrapped.__name__ = name wrapped.__doc__ = self.src_doc wrapped.func_defaults = self.src_defaults wrapped.__module__ = tgt_func.__module__ wrapped.__dict__ = tgt_func.__dict__ return wrapped 
+4
source

Source: https://habr.com/ru/post/953205/


All Articles