如何使用装饰函数文档中的真实签名替换*args
和**kwargs
?
假设我有以下装饰器和装饰功能:
import functools
def mywrapper(func):
@functools.wraps(func)
def new_func(*args, **kwargs):
print('Wrapping Ho!')
return func(*args, **kwargs)
return new_func
@mywrapper
def myfunc(foo=42, bar=43):
"""Obscure Addition
:param foo: bar!
:param bar: bla bla
:return: foo + bar
"""
return foo + bar
因此,致电print(myfunc(3, 4))
会给我们:
Wrapping Ho!
7
到目前为止一切顺利。我还希望我的库包含myfunc
与Sphinx一起正确记录。
但是,如果我通过以下方式将我的函数包含在我的sphinx html页面中:
.. automodule:: mymodule
:members: myfunc
它实际上会显示为:
模糊添加
如何摆脱标题中的通用myfunc(*args, **kwargs)
?这应该被 myfunc(foo = 42,bar = 43)取代。如何更改sphinx或我的装饰器mywrapper
,以便在文档中保留默认关键字参数?
修改:
如前所述,这个问题已被提出过,但答案并不那么有用。
然而,我有一个想法,并想知道这是否可行。 Sphinx是否设置了一些环境变量来告诉我的模块它实际上是由Sphinx导入的?如果是这样,我可以简单地修补我自己的包装。如果我的模块是由Sphinx导入的,我的包装器将返回原始函数而不是包装它们。因此,签名得以保留。
答案 0 :(得分:5)
我想出了functools.wraps
的猴子补丁。
因此,我只是将其添加到项目文档的sphinx conf.py
文件夹中的source
脚本中:
# Monkey-patch functools.wraps
import functools
def no_op_wraps(func):
"""Replaces functools.wraps in order to undo wrapping.
Can be used to preserve the decorated function's signature
in the documentation generated by Sphinx.
"""
def wrapper(decorator):
return func
return wrapper
functools.wraps = no_op_wraps
因此,在通过make html
构建html页面时,functools.wraps
将替换为此装饰器no_op_wraps
,它完全不做任何事情,只是简单地返回原始函数。
答案 1 :(得分:2)
你通常不能。 这是因为在包装函数中用作参数的变量名称甚至不存在于包装函数中 - 因此Sphinx不知道它们。
这是Python中一个已知的复杂问题 - 以至于最新版本 - 不仅包括Python 3,还包括Python 2.7在类装饰上包含__wrapped__
属性,可以从functools.wraps
正确使用 -
这样,在检查装饰函数时,可以通过查看__wrapped__
来了解实际的扭曲函数。不幸的是,Sphinxs忽略__wrapped__
,而是在包装函数上显示信息。
所以,要做的一件事当然是将此报告为Sphinx项目本身的一个错误 - 它应该考虑__wrapped__
。
同时解决方法是更改包装函数以实际包含有关包装的更多信息 - 如其签名 - 所以你可以为你的项目编写另一个函数来代替“functools.wraps”,这就是这样:pre-pend the 功能签名到其docstring,如果有的话。 不幸的是,在3.3以上的Python中检索函数签名是棘手的 - (对于3.3和更新版本,检查https://docs.python.org/3/library/inspect.html#inspect-signature-object) - 但无论如何,对于一个天真的形式,你可以编写另一个版本的“wrapps”:
def wraps(original_func):
wrap_decorator = functools.wraps(original_func)
def re_wrapper(func):
wrapper = wrap_decorator(func)
poorman_sig = original_func.__code__.co_varnames[
:original_func.__code__.co_argcount]
wrapper.__doc__ = "{} ({})\n\n{}".format (
original_func.__name__, ", ".join(poorman_sig),
wrapper.__doc__)
return wrapper
return re_wrapper
使用它代替“functools.wraps”。它至少会添加一个带有参数名称的行(但不是defalt值)作为文档中的第一行。
---嗯..可能只是在完成这项工作之前修补Sphinx使用__wrapped__
会更容易。