【问题标题】:Preserve default arguments of wrapped/decorated Python function in Sphinx documentation在 Sphinx 文档中保留包装/装饰 Python 函数的默认参数
【发布时间】:2015-04-06 15:46:20
【问题描述】:

如何将*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:吧!
    • 酒吧:bla bla
  • 返回: 富+酒吧

我怎样才能摆脱标题中的通用myfunc(*args, **kwargs)?这应该替换为 myfunc(foo=42, bar=43)。如何更改 sphinx 或我的装饰器 mywrapper 以便在文档中保留默认关键字参数?

编辑

正如之前所指出的,这个问题已经被问过,但答案并没有太大帮助。

但是,我有一个想法,想知道这是否可能。 Sphinx 是否设置了一些环境变量来告诉我的模块它实际上是由 Sphinx 导入的?如果是这样,我可以简单地修补我自己的包装器。如果我的模块是由 Sphinx 导入的,我的包装器会返回原始函数而不是包装它们。因此,签名被保留。

【问题讨论】:

  • 抱歉没有找到问题。是的,它是完全相同的。但恐怕答案不是很有帮助。我不想繁琐地手动将所有装饰函数添加到我的 sphinx 文档中。

标签: python decorator python-sphinx python-decorators


【解决方案1】:

我想出了一个 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 不会在自己的代码库中的任何地方调用 functools.wraps()
  • 这在conf.py 中对我不起作用,尽管如果我将它放在我对包装器的定义之前(相当无用)。 conf.py 中是否有一个特殊的地方需要这个?
  • 我把它放在conf.py的末尾。但我想你必须小心在导入任何其他使用functools.wraps 的模块之前将其包含在from functools import wraps 方面。
【解决方案2】:

你通常不能。 这是因为包装函数中用作参数的变量名甚至不存在于包装函数中 - 所以 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__ 会更容易。

【讨论】:

  • 我想我可以使用inspectinspect.getargspec(myfunc) 提取函数的默认参数?!但仍将这些信息预先添加到文档中不会消除错误的标题。
  • 我为这个问题创建了一个新的github问题:github.com/sphinx-doc/sphinx/issues/1711
猜你喜欢
  • 2016-09-27
  • 1970-01-01
  • 1970-01-01
  • 2019-09-02
  • 1970-01-01
  • 2014-09-25
  • 2020-11-02
  • 2015-09-03
  • 2020-06-08
相关资源
最近更新 更多