简体   繁体   English

在 Sphinx 文档中保留包装/装饰的 Python 函数的默认参数

[英]Preserve default arguments of wrapped/decorated Python function in Sphinx documentation

How can I replace *args and **kwargs with the real signature in the documentation of decorated functions?如何用装饰函数文档中的真实签名替换*args**kwargs

Let's say I have the following decorator and decorated function:假设我有以下装饰器和装饰功能:

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

Accordingly, calling print(myfunc(3, 4)) gives us:因此,调用print(myfunc(3, 4))给我们:

Wrapping Ho!
7

So far so good.到现在为止还挺好。 I also want my library containing myfunc properly documented with Sphinx.我还希望我的库包含使用 Sphinx 正确记录的myfunc However, if I include my function in my sphinx html page via:但是,如果我通过以下方式将我的函数包含在我的 sphinx html 页面中:

.. automodule:: mymodule
    :members: myfunc

It will actually show up as:它实际上会显示为:

myfunc(*args, **kwargs) myfunc(*args, **kwargs)

Obscure Addition模糊加成

  • Parameters:参数:
    • foo : bar!:酒吧!
    • bar : bla bla酒吧:bla bla
  • Returns: foo + bar返回: foo + bar

How can I get rid of the generic myfunc(*args, **kwargs) in the title?如何摆脱标题中的通用myfunc(*args, **kwargs) This should be replaced by myfunc(foo=42, bar=43) .这应该替换为myfunc(foo=42, bar=43) How can I change sphinx or my decorator mywrapper such that the default keyword arguments are preserved in the documentation?如何更改 sphinx 或我的装饰器mywrapper以便在文档中保留默认关键字参数?

EDIT :编辑

As pointed out this question has been asked before, but the answers are not so helpful.正如所指出的,这个问题以前曾被问过,但答案并不是那么有帮助。

However, I had an idea and wonder if this is possible.但是,我有一个想法,想知道这是否可能。 Does Sphinx set some environment variable that tells my module that it is actually imported by Sphinx? Sphinx 是否设置了一些环境变量来告诉我的模块它实际上是由 Sphinx 导入的? If so, I could simply monkey-patch my own wrappers.如果是这样,我可以简单地修补我自己的包装。 If my module is imported by Sphinx my wrappers return the original functions instead of wrapping them.如果我的模块是由 Sphinx 导入的,我的包装器将返回原始函数而不是包装它们。 Thus, the signature is preserved.因此,签名被保留。

I came up with a monkey-patch for functools.wraps .我为functools.wraps想出了一个猴子补丁。 Accordingly, I simply added this to the conf.py script in my project documentation's sphinx source folder:因此,我只是将其添加到我的项目文档的 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

Hence, when building the html page via make html , functools.wraps is replaced with this decorator no_op_wraps that does absolutely nothing but simply return the original function.因此,当通过make html构建 html 页面时, functools.wraps被替换为这个装饰器no_op_wraps ,它除了简单地返回原始函数之外什么都不做。

You ordinarily can't.你通常不能。 That is because the variable names used as parameters in the wrapped function are not even present on the wrapped function - so Sphinx do not know about them.那是因为在包装函数中用作参数的变量名称甚至不存在于包装函数中 - 所以 Sphinx 不知道它们。

That is a known complicated issue in Python - so much that recent versions - including not only Python 3, but also Python 2.7 included a __wrapped__ attribute on class decorated that make the proper use from functools.wraps - that way, upon inspecting the decorated function one is able to know about the actual wrrapped function by looking at __wrapped__ .这是 Python 中一个已知的复杂问题 - 以至于最近的版本 - 不仅包括 Python 3,还包括 Python 2.7 在装饰类上包含一个__wrapped__属性,可以从functools.wraps正确使用 - 这样,在检查装饰函数时可以通过查看__wrapped__来了解实际的包装函数。 Unfortunatelly, Sphinxs ignores the __wrapped__ , and show the info on the wrapper function instead.不幸的是,Sphinxs 忽略了__wrapped__ ,而是显示包装函数的信息。

SO, one thing to do is certainly to report this as a bug to the Sphinx project itself - it should take __wrapped__ in account.所以,要做的一件事当然是将此作为错误报告给 Sphinx 项目本身 - 它应该考虑__wrapped__

A meantime workaround for that would be to change the wrapper function to actually include more information about the wrapped - like its signature - so you could write another function to be called in place of "functools.wraps" for your project, which does just that: pre-pend the function signature to its docstring, if any.在此期间的解决方法是更改​​包装器函数以实际包含有关包装的更多信息(例如其签名),因此您可以编写另一个要调用的函数来代替项目的“functools.wraps”,它就是这样做的: 将函数签名预先添加到其文档字符串中,如果有的话。 Unfortunatelly, retrieving the function signatures in Python older than 3.3 is tricky - (for 3.3 and newer, check https://docs.python.org/3/library/inspect.html#inspect-signature-object ) - but anyway, for a naive form, you could write another version of "wraps" along:不幸的是,在 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

And use that instead of "functools.wraps".并使用它而不是“functools.wraps”。 It would at least add a line with the parameter names, (but not th e defalt values) as first line in the docs.它至少会添加一行带有参数名称的行(但不是默认值)作为文档中的第一行。

---Hmm..maybe it would be easier just to patch Sphinx to use __wrapped__ before getting this done right. ---嗯.. __wrapped__在正确完成此操作之前修补 Sphinx 以使用__wrapped__会更容易。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM