开发者

How to keep help strings the same when applying decorators?

开发者 https://www.devze.com 2022-12-10 05:29 出处:网络
How can I keep help strings in functions to be visible after applying a decorator? Right now the doc string is (partially) replaced with that of the inner function of the decorator.

How can I keep help strings in functions to be visible after applying a decorator?

Right now the doc string is (partially) replaced with that of the inner function of the decorator.

def deco(fn):
    def x(*args, **kwargs):
        return fn(*args, **kwargs)
    x.func_doc = fn.func_doc
    x.func_name = fn.func_name
    return x

@deco
def y(a, b):
    """This is Y"""
    pass

def z(c, d):
    """This is Z"""
    pass

help(y) # 1
help(z) # 2

In the Y function, required arguments aren't shown in the help. The user may assume it takes any arguments, while actually it doesn't.开发者_如何学JAVA

y(*args, **kwargs) <= y(a, b) is desired
    This is Y

z(c, d)
    This is Z

I use help() and dir() a lot, since it's faster than pdf manuals, and want to make reliable document strings for my library and tools, but this is an obstacle.


give the decorator module a peek. i believe it does exactly what you want.

In [1]: from decorator import decorator
In [2]: @decorator
   ...: def say_hello(f, *args, **kwargs):
   ...:     print "Hello!"
   ...:     return f(*args, **kwargs)
   ...: 
In [3]: @say_hello
   ...: def double(x):
   ...:     return 2*x
   ...: 

and info says "double(x)" in it.


What you're requesting is very hard to do "properly", because help gets the function signature from inspect.getargspec which in turn gets it from introspection which cannot directly be fooled -- to do it "properly" would mean generating a new function object on the fly (instead of a simple wrapper function) with the right argument names and numbers (and default values). Extremely hard, advanced, black-magic bytecode hacking required, in other words.

I think it may be easier to do it by monkeypatching (never a pleasant prospect, but sometimes the only way to perform customization tasks that are otherwise so difficult as to prove almost impossible, like the one you require) -- replace the real inspect.getargspec with your own lookalike function which uses a look-aside table (mapping the wrapper functions you generate to the wrapped functions' argspecs and otherwise delegating to the real thing).

import functools
import inspect

realgas = inspect.getargspec

lookaside = dict()

def fakegas(f):
    if f in lookaside:
        return lookaside[f]
    return realgas(f)

inspect.getargspec = fakegas

def deco(fn):
    @functools.wraps(fn)
    def x(*args, **kwargs):
        return fn(*args, **kwargs)
    lookaside[x] = realgas(fn)
    return x

@deco
def x(a, b=23):
  """Some doc for x."""
  return a + b

help(x)

This prints, as required:

Help on function x in module __main__:

x(a, b=23)
    Some doc for x.
(END)
0

精彩评论

暂无评论...
验证码 换一张
取 消