[英]How to preserve a docstring of a decorated class for sphinx documentation?
[英]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
到现在为止还挺好。 我还希望我的库包含使用 Sphinx 正确记录的myfunc
。 但是,如果我通过以下方式将我的函数包含在我的 sphinx html 页面中:
.. automodule:: mymodule
:members: myfunc
它实际上会显示为:
模糊加成
如何摆脱标题中的通用myfunc(*args, **kwargs)
? 这应该替换为myfunc(foo=42, bar=43) 。 如何更改 sphinx 或我的装饰器mywrapper
以便在文档中保留默认关键字参数?
编辑:
正如所指出的,这个问题以前曾被问过,但答案并不是那么有帮助。
但是,我有一个想法,想知道这是否可能。 Sphinx 是否设置了一些环境变量来告诉我的模块它实际上是由 Sphinx 导入的? 如果是这样,我可以简单地修补我自己的包装。 如果我的模块是由 Sphinx 导入的,我的包装器将返回原始函数而不是包装它们。 因此,签名被保留。
我为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
,它除了简单地返回原始函数之外什么都不做。
你通常不能。 那是因为在包装函数中用作参数的变量名称甚至不存在于包装函数中 - 所以 Sphinx 不知道它们。
这是 Python 中一个已知的复杂问题 - 以至于最近的版本 - 不仅包括 Python 3,还包括 Python 2.7 在装饰类上包含一个__wrapped__
属性,可以从functools.wraps
正确使用 - 这样,在检查装饰函数时可以通过查看__wrapped__
来了解实际的包装函数。 不幸的是,Sphinxs 忽略了__wrapped__
,而是显示包装函数的信息。
所以,要做的一件事当然是将此作为错误报告给 Sphinx 项目本身 - 它应该考虑__wrapped__
。
在此期间的解决方法是更改包装器函数以实际包含有关包装的更多信息(例如其签名),因此您可以编写另一个要调用的函数来代替项目的“functools.wraps”,它就是这样做的: 将函数签名预先添加到其文档字符串中,如果有的话。 不幸的是,在 Python 3.3 之前检索函数签名很棘手 - (对于 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”。 它至少会添加一行带有参数名称的行(但不是默认值)作为文档中的第一行。
---嗯.. __wrapped__
在正确完成此操作之前修补 Sphinx 以使用__wrapped__
会更容易。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.