如何用修饰功能文档中的真实签名替换*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
到目前为止,一切都很好。我还希望我的包含[Sphinx]正确记录的myfunc
的库。但是,如果我通过以下方式在我的狮身人面像html页面中包含函数:
.. automodule:: mymodule
:members: myfunc
它实际上将显示为:
晦涩加法
如何去除标题中的通用myfunc(*args, **kwargs)
?应该用myfunc(foo = 42,bar = 43)代替。如何更改狮身人面像或装饰器mywrapper
,以使默认关键字参数保留在文档中?
编辑:
如已指出,此问题以前曾被问过,但答案并没有那么大的帮助。
但是,我有一个主意,想知道这是否可能。 Sphinx是否设置一些环境变量来告诉我的模块它实际上是由Sphinx导入的?如果是这样,我可以简单地用猴子包装自己的包装纸。如果我的模块是由Sphinx导入的,我的包装器将返回原始函数,而不是包装它们。因此,签名被保留。
我想出了一个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
取代,该装饰器绝对不执行任何操作,只是返回原始功能。
您通常不能。那是因为在包装函数中甚至没有出现在包装函数中用作参数的变量名-因此Sphinx不知道它们。
[这是Python中一个已知的复杂问题-如此之多,以至于最近的版本-不仅包括Python 3,而且Python 2.7在修饰的类上都包含__wrapped__
属性,可以从functools.wraps
中正确使用-这样,检查装饰功能后,便可以通过查看__wrapped__
来了解实际的包装功能。不幸的是,Sphinxs忽略了__wrapped__
,而是在包装函数上显示了信息。
因此,要做的一件事当然是将其作为错误报告给Sphinx项目本身-应该考虑__wrapped__
。
同时解决此问题的方法是更改包装器函数,使其实际上包含有关被包装的更多信息(例如其签名-因此您可以为您的项目编写另一个函数来代替“ functools.wraps”,该函数就是这样:预先添加函数签名为其文档字符串(如果有)。不幸的是,在3.3之前的Python中检索函数签名很棘手-(对于3.3和更高版本,请检查https://docs.python.org/3/library/inspect.html#inspect-signature-object)-但无论如何,对于幼稚的形式,您可以编写以下版本的“包装”:
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”。至少会在文档的第一行添加带有参数名称的行(但不是默认值)。
----嗯..也许在完成正确的操作之前先打补丁Sphinx以使用__wrapped__
会更容易。