使用Sphinx Autodoc生成fabfile文档

6

是否可以使用Sphinx autodoc从函数docstrings生成我的fabfile的文档?

例如,对于包含setup_development任务的fabfile,我尝试了以下方法:

.. automodule::fabfile
   :members:
   .. autofunction:: setup_development

但是没有任何生成。

fabfile片段:

@task
def setup_development(remote='origin', branch='development'):
    """Setup your development environment.

    * Checkout development branch & pull updates from remote
    * Install required python packages
    * Symlink development settings
    * Sync and migrate database
    * Build HTML Documentation and open in web browser

    Args:
        remote: Name of remote git repository. Default: 'origin'.
        branch: Name of your development branch. Default: 'development'.
    """
    <code>

1
一个问题是 .. automodule::fabfile 之间缺少空格。如果你想使用 .. autofunction:: (我认为不需要),它应该在前面加上一个空行。 - mzjn
谢谢,这个方法很有效!看起来 :members: 不能捕获包装函数(根据 shahjapan 给出的答案),所以我可以使用 wraps 或者使用 .. autofunction::,它可以与 @task 装饰器一起使用。 - Matt Austin
2个回答

3

这是因为你在函数 setup_development 上应用了装饰器。

你需要使用 functools.wraps 更新你的 task 函数,如下所示:

from functools import wraps

def task(calling_func):
    @wraps(calling_func)
    def wrapper_func(self, *args, **kw):
        return calling_func(*args, **kw)
    return wrapper_func

如果要记录已装饰的函数或方法,请记住,自动文档将通过导入模块并检查给定函数或方法的__doc__属性来检索其文档字符串。这意味着,如果一个装饰器用另一个函数替换了已装饰的函数,它必须将原始的__doc__复制到新函数中。
从Python 2.5开始,可以使用functools.wraps()创建行为良好的修饰函数。
参考资料:

我真的无法使用现有的“fabric.api.task”装饰器使其正常工作。你能否提供特定的代码来装饰这个现有的装饰器以传递“__doc__”?谢谢。 - Matt Austin
@Matt Austin:所以autofunction@task不能一起使用吗? - mzjn
@mzjn 的 autofunction 只能部分工作,虽然生成了文档字符串信息,但函数的 args/kwargs 却缺失了。 - Matt Austin
1
@shahjapan,你能解释一下为什么你的装饰器函数看起来是这样的吗?calling_wrapper是什么?perm_context从哪里来? - mzjn
1
@shahjapan,我仍然觉得有些东西缺失。你对Matt说:“你需要按照下面的方式使用functools.wraps更新你的task函数。”那应该如何具体操作呢?原始的task函数来自Fabric库 - mzjn

1

我使用decorator模块中的文档中找到的decorator_apply配方,成功生成了完整的文档并保留了函数签名。

""" myfabfile.py """

from fabric.api import task as origtask
from decorator import FunctionMaker

def decorator_apply(dec, func):
    return FunctionMaker.create(
        func, 'return decorated(%(signature)s)',
        dict(decorated=dec(func)), __wrapped__=func)

def task(func):
    return decorator_apply(origtask, func)

@task
def setup_development(remote='origin', branch='development'):
    """Setup your development environment.

    * Checkout development branch & pull updates from remote
    * Install required python packages
    * Symlink development settings
    * Sync and migrate database
    * Build HTML Documentation and open in web browser

    :param remote: Name of remote git repository.
    :param branch: Name of your development branch.
    """
    pass

这是我使用的简单ReST源代码:

.. automodule:: myfabfile
   :members:

一些注释:

shahjapan提交的答案解释了如何在一般情况下保留文档字符串,但它没有解决@task装饰器定义在外部库中的事实。

无论如何,结果证明使用@task修饰的函数会自动保留文档字符串。以下是Fabric的tasks.WrappedCallableTask类的__init__方法中的内容:

if hasattr(callable, '__doc__'):
    self.__doc__ = callable.__doc__

因此,它已经按原样工作(需要显式的 .. autofunction::)。为了确保函数签名也得到保留,可以使用decorator模块如上所示。


更新

使用decorator模块会破坏Fabric的工作方式(请参见评论)。所以这可能并不可行。作为替代方案,我建议使用以下修改后的reST标记:

.. automodule:: myfabfile2
   :members: 

   .. autofunction:: setup_development(remote='origin', branch='development')

也就是说,您必须包括完整的函数签名。这也是Sphinx文档建议的做法(参见"如果方法的签名被修饰符隐藏,则此举很有用。")。


这对于自动文档来说有效,但使用这种方法fabric no longer recognises其decorated tasks。“fab --list”列出所有函数(这是向后兼容的后备行为),在这种情况下,完全省略“@task”会更容易。它也可以防止在将fabfile作为模块使用时,在其他文件中找到导入的tasks。 - Matt Austin
我想那么做的唯一方法就是在两个文件中都复制函数名称和签名。有点麻烦,但感谢您的调查。 - Matt Austin
由于我们还没有找到一种自动生成fabfile文档的方法,因此我在Github上为Fabric创建了一个问题:https://github.com/fabric/fabric/issues/569 - Matt Austin
在问题报告中,Matt将两个解决方法都标记为“失败”。但我认为,在.rst文件中手动指定函数签名是一个合理的妥协。是的,这有点违反“不要重复自己”的原则。但是我参考了Sphinx文档和Fabric维护者的评论,表明现在没有更好的解决方案。@task特别复杂,因为它“涉及包装/委托类,而不仅仅是嵌套或装饰函数对象”。 - mzjn
是的,不幸的是,我将不得不依靠记住在添加/修改任务时更新sphinx签名。至少文档仍然是这样生成的,比起这个问题开始时要好得多。感谢您的帮助。 - Matt Austin
谢谢!调查这个问题很有趣(在此过程中我也学到了一两件事情)。 - mzjn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接