在 Sphinx 文档中保留 wrapped/decorated Python 函数的默认参数
Preserve default arguments of wrapped/decorated Python function in Sphinx documentation
如何在修饰函数的文档中用真正的签名替换*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)
晦涩的加法
- 参数:
- foo: 酒吧!
- bar: bla bla
- Returns:
foo + bar
如何去掉标题中的通用 myfunc(*args, **kwargs)
?这应该被替换为 myfunc(foo=42, bar=43)。我如何更改 sphinx 或我的装饰器 mywrapper
以便在文档中保留默认关键字参数?
编辑:
正如所指出的,这个问题之前已经被问过,但答案并不是很有帮助。
但是,我有一个想法,想知道这是否可能。 Sphinx 是否设置了一些环境变量来告诉我的模块它实际上是由 Sphinx 导入的?如果是这样,我可以简单地 monkey-patch 我自己的包装器。如果我的模块是由 Sphinx 导入的,我的包装器 return 原始函数而不是包装它们。因此,签名得以保留。
你通常不能。
那是因为在包装函数中用作参数的变量名甚至不存在于包装函数中 - 所以 Sphinx 不知道它们。
这是 Python 中的一个已知复杂问题 - 如此之多以至于最近的版本 - 不仅包括 Python 3,而且 Python 2.7 都包含一个 __wrapped__
属性在 class 上正确使用 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 )——但是无论如何,对于一个天真的形式,你可以编写另一个版本的 "wraps"沿:
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__
会更容易。
我想出了一个 functools.wraps
的猴子补丁。
因此,我只是将其添加到项目文档的 sphinx source
文件夹中的 conf.py
脚本中:
# 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
替换,它只做 return原始功能。
如何在修饰函数的文档中用真正的签名替换*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)
晦涩的加法
- 参数:
- foo: 酒吧!
- bar: bla bla
- Returns: foo + bar
如何去掉标题中的通用 myfunc(*args, **kwargs)
?这应该被替换为 myfunc(foo=42, bar=43)。我如何更改 sphinx 或我的装饰器 mywrapper
以便在文档中保留默认关键字参数?
编辑:
正如所指出的,这个问题之前已经被问过,但答案并不是很有帮助。
但是,我有一个想法,想知道这是否可能。 Sphinx 是否设置了一些环境变量来告诉我的模块它实际上是由 Sphinx 导入的?如果是这样,我可以简单地 monkey-patch 我自己的包装器。如果我的模块是由 Sphinx 导入的,我的包装器 return 原始函数而不是包装它们。因此,签名得以保留。
你通常不能。 那是因为在包装函数中用作参数的变量名甚至不存在于包装函数中 - 所以 Sphinx 不知道它们。
这是 Python 中的一个已知复杂问题 - 如此之多以至于最近的版本 - 不仅包括 Python 3,而且 Python 2.7 都包含一个 __wrapped__
属性在 class 上正确使用 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 )——但是无论如何,对于一个天真的形式,你可以编写另一个版本的 "wraps"沿:
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__
会更容易。
我想出了一个 functools.wraps
的猴子补丁。
因此,我只是将其添加到项目文档的 sphinx source
文件夹中的 conf.py
脚本中:
# 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
替换,它只做 return原始功能。