首页 文章

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

提问于
浏览
8

如何将 *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)

模糊的加法

  • Parameters:

  • foo :吧!

  • bar :bla bla

  • Returns: foo吧

如何摆脱 Headers 中的泛型 myfunc(*args, **kwargs) ?这应该被 myfunc(foo=42, bar=43) 取代 . 如何更改sphinx或我的装饰器 mywrapper ,以便在文档中保留默认关键字参数?

EDIT

正如之前所指出的那样,这个问题已被提出,但答案并没有那么有用 .

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

2 回答

  • 2

    我为 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 ,除了简单地返回原始函数之外什么都不做 .

  • 4

    你通常不能 . 这是因为在包装函数中用作参数的变量名称甚至不存在于包装函数中 - 因此Sphinx不知道它们 .

    这是Python中一个已知的复杂问题 - 最近的版本 - 不仅包括Python 3,还包括Python 2.7在类装饰上包含 __wrapped__ 属性,从 functools.wraps 开始正确使用 - 这样,在检查装饰函数时,一个是通过查看 __wrapped__ 能够了解实际的扭曲功能 . 不幸的是,Sphinxs忽略了 __wrapped__ ,而是在包装函数上显示信息 .

    因此,要做的一件事当然是将此报告为Sphinx项目本身的一个错误 - 它应该考虑 __wrapped__ .

    同时解决这个问题的方法是更改包装函数以实际包含有关包装的更多信息 - 比如它的签名 - 这样你就可以为你的项目编写另一个函数来代替"functools.wraps"来调用它:pre-pend其文档字符串的函数签名(如果有) . 不幸的是,在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” . 它至少会添加一个带有参数名称的行(但不是defalt值)作为文档中的第一行 .

    ---嗯......可能只是在完成这项工作之前修补Sphinx以使用 __wrapped__ 会更容易 .

相关问题