18.7 用Sphinx生成文档

    Sphinx工具能够生成外观非常好看的多格式文档。通过它,我们可以很容易地将来自于源代码的文档和来自外部的带有设计笔记、需求或者背景信息的文件进行合并。

    Sphinx可以在http://sphinx-doc.org 上找到。下载过程可能会比较复杂,因为Sphinx依赖于另外几个项目。更容易的做法可能是首先安装setuptools,这里面包含了easy _ install脚本,然后用这个脚本来安装Sphinx。它可以帮助追踪那些必须先安装的、项目额外的细节。

    关于setuptools的帮助信息,查看https://pypi.python.org/pypi/setuptools

    一些开发作者倾向于使用pip来完成这种类型的安装。关于pip的信息,可参见https:// pypi.python.org/pypi/pip

    Sphinx的教程写得非常好。可以从这些教程开始学习,并且确保你可以使用sphinx- quickstart和sphinx-build命令。通常,make程序会负责运行sphinx-build,这样的方式简化了一些Sphinx命令行的使用。

    18.7.1 使用Sphinx的快速启动

    sphinx-quickstart的一个很方便的功能是能够通过一个交互式问答会话填充非常复杂的config.py。 下面是这种会话的一部分,用于展示这个会话看起来是怎样的,我们标出了一些不是最佳的默认响应。 对于更复杂的项目,从长期来看,将文档从工作代码中分离出来更简单。在全局的项目树中创建一个doc目录通常是一个好主意。
    Enter the root path for documentation.
    > Root path for the documentation [.]: doc
    对于非常小的文档,与代码和HTML交叉没有问题。对于更大的文档,尤其是对于可能用于生成LaTex和PDF的文档,保持这些文件与文档的HTML版本的独立性会为我们带来方便。
    You have two options for placing the build directory for Sphinx
    output.
    Either, you use a directory "build" within the root path, or you
    separate
    "source" and "build" directories within the root path.
    > Separate source and build directories (y/N) [n]: y
    下一批问题指出了一些特定的加载项,它以下面的备注开头。
    Please indicate if you want to use one of the following Sphinx extensions:
    我们会建议使用一些对常见的Python开发最有用的加载项。对于第1次使用Sphinx的用户,这些加载项足以让我们能够开始使用Sphinx并且生成优秀的文档。当然,对于特定的项目需求和目标会需要其他方案。 我们几乎总是希望包含autodoc功能来从文档注释中生成文档。如果我们用Sphinx在Python程序外部生成文档,我们可能需要关闭autodoc
    > autodoc: automatically insert docstrings from modules (y/N) [n]: y
    如果我们有doctest的示例,可以让Sphinx运行这些doctest示例。对于小项目,其中的大部分测试都是通过doctest来完成的,这种做法非常方便。对于更大的项目,我们通常会有包含doctest的单元测试脚本。通过Sphinx执行doctest和通过正式的单元测试执行都是很好的选择。
    > doctest: automatically test code snippets in doctest blocks (y/N)
    [n]: y
    一个成熟的开发过程中可能有许多紧密关联的项目,这可能包括多个相关的Sphinx文档目录。
    > intersphinx: link between Sphinx documentation of different projects
    (y/N) [n]:
    todo扩展允许我们在文档注释中包含一个..todo::指令。然后,可以在文档中官方的to-do列表中添加一个特殊的..todolist::指令。
    > todo: write "todo" entries that can be shown or hidden on build
    (y/N) [n]:
    覆盖率报告可以作为质量保证指标。
    > coverage: checks for documentation coverage (y/N) [n]:
    对于包含很多数学函数的项目,使用LaTex工具集让我们可以将数学函数排版为美观的图片并且包含在HTML中。它也会在LaTex的输出中保留原始的数学函数。MathJax是一个基于网页的JavaScript库,我们也可以用下面的方式来使用它。
    > pngmath: include math, rendered as PNG images (y/N) [n]: y
    > mathjax: include math, rendered in the browser by MathJax (y/N) [n]:
    对于非常复杂的项目,我们可能需要生成不同的文档。
    > ifconfig: conditional inclusion of content based on config values
    (y/N) [n]:
    大部分应用程序的文档都会描述API。我们应该包含autodoc混合viewcode功能。viewcode选项允许读者查看代码,这样他们就可以了解详细的实现细节。
    > viewcode: include links to the source code of documented Python
    objects (y/N) [n]: y
    autodocdoctest功能意味着我们可以专注于在代码中编写文档注释,只需要编写非常少的Sphinx文档文件来提取文档注释信息。对于一些开发者而言,能够专注代码减少了与编写文档相关的恐惧成分。 18.7.2 编写Sphinx文档 软件开发的项目中有两个通用的起点。 - 创建一些起始文档,并且这些文档要保留下来。 - 无,从零开始。 当项目以一些遗留的文档开始时,这些文档可能包含需求、用户故事或者架构笔记。它可能也包括组织政治的笔记、过期的预算和进度表,还有其他一些和技术无关的材料。 在理想状况下,这些起始文档已经是文本文件了。如果不是的话,它们可能是基于可以被保存为文本的某些文字处理格式。当我们有面向文本的起始文档后,添加RST标记来向我们展示大纲结构并且将这些文本文件组织成一个简单的目录结构就变得相对简单了。 没有理由将内容保留为文字处理文档。一旦它作为软件开发项目的技术文档的一部分,RST就可以允许更灵活地使用初始信息。 一种会有困难的情况是,初始文档是用Keynote、PowerPoint或者一个类似的工具创建的一套幻灯片。将这些文件转换成以文本为中心的RST很困难,因为图表和图片是幻灯片中最主要的内容。在这些情况下,我们有时候最好能够将演示文本导出为一个HTML文档,然后将它保存在Sphinx的doc/source/
    static目录下。这允许我们可以通过简单的RST链接将原始的材料集成进Sphinx中,这种链接通常以下面的形式呈现:'Inception < static/inception doc/index.html>'。 当使用交互式的、基于网页的工具来管理项目或者用户故事时,起始和背景文档需要通过简单的URL引用来处理,这种引用的形式是:'Background &lt;http://someservice/ path/to/page.html&gt;'。 通常以一个包含一些占位符的轮廓作为书写文档的开始是最容易的,因为文档的内容会随着软件开发的进行逐渐增加。一种基于4+1视图架构的结构可能会对我们有帮助。起始文档通常作为场景或者4+1视图中用户故事的一部分。有时候,起始文档会作为开发或者物理部署的一部分。 更多关于4+1架构的信息,可以参考下面的页面。 http://en.wikipedia.org/wiki/4%2B1 architectural view _ model

    我们可以在index.html根下创建5个顶层的文档:user _ storieslogicalprocessimplementationphysical。每个都必须包含一个RST标题,但是在文件中不需要任何其他文本。

    然后我们可以更新Sphinx的index.rst文件默认生成的.. toctree::目录。

    .. Mastering OO Python documentation master file, created by
      sphinx-quickstart on Fri Jan 31 09:21:55 2014.
      You can adapt this file completely to your liking, but it should at least
      contain the root 'toctree' directive.

    Welcome to Mastering OO Python's documentation!
    ===============================================

    Contents:
    .. toctree::
      :maxdepth: 2

      user_stories
      logical
      process
      implementation
      physical

    Indices and tables
    ==================
    :ref:'genindex'
    :ref:'modindex'
    * :ref:'search'

    一旦我们有了顶层的结构,就可以用make命令创建文档。

    make doctest html

    这行命令会运行我们的测试用例,如果所有的测试通过,它会创建HTML文档。

    18.7.3 在文档中加入4+1视图

    随着开发的进行,4+1视图可以被用于组织不断增加的细节。这种技术通常用在文档注释外部的信息中。

    我们用user _ stories.rst文档来收集用户故事、需求和其他高层的背景注释。如果用户故事变得越来越复杂,这个文档会逐渐演变为一个目录树。

    我们用logical.rst文档来收集初始面向对象设计的类、模块和包。这应该作为我们设计思路的起源。它可能包含替代方案、笔记、数学背景、正确性证明和逻辑软件设计的图表。对于设计清晰的相对简单的项目,这个文件可能会保持为空。对于复杂的项目,这个文件可能会描述一些复杂的分析和设计,这些信息会作为实现的背景或理由。

    最后的面向对象设计是implementation.rst文件中的Python模块和类。我们会介绍多一些关于这个文件的细节,因为这会成为我们的API文档。这部分将直接基于Python代码和RST标记的文档字符串。

    process.rst文档可以收集关于动态性、运行时行为的信息。这些信息包括了一些主题,例如并发性、分布式和集成。它也有可能包括关于性能和可扩展性。网络设计和协议的使用也有可能在该文档中描述。

    对于更小的应用程序,哪些材料应该保存在process文档中不是非常清晰。这个文档可能会与逻辑设计和全局的架构信息重合。当有疑问时,我们必须力求基于受众对于信息的需求来明确哪些信息是必要的。对于一些用户而言,许多的小文档是很有帮助的。对于其他的一些用户,他们更倾向于一个大型文档。

    physical.rst文件是我们记录部署细节的地方。我们会将配置小细节的描述保存在这个文件中:环境变量、配置文件格式细节、可用的日志记录器名称以及其他管理与技术支持所需要的信息。这可能也包含配置信息,例如服务器名称、IP地址、账号名、目录名和相关的注释。在一些公司里,管理员可能会觉得其中有些细节不适合作为通用的软件文档。

    18.7.4 编写实现文档

    implementation.rst文档可以用automodule创建文档。下面是如何开始编写一份implementation.rst文档。

    Implementation
    ================

    Here's a reference to the 'inception document <static/inception_doc/
    index.html>'


    The p3_c18 module
    ———————————-
    .. automodule:: p3_c18
      :members:
      :undoc-members:
      :special-members:

    The simulation_model module
    ————————————————

    .. automodule:: simulation_model
      :members:
      :undoc-members:
      :special-members:

    我们用了两种RST标题:有一个单独的顶层标题和两个副标题。RST推断出了父标题和子标题间的关系。在本例中,我们在父标题上用了"==="作为下划线,在子标题上用了" —- "作为下划线。

    我们为你提供了一个复制到static目录下的文档的显式引用——inception doc。我们在单词inception document和实例文档的index.html文件间创建了一个复杂的RST链接。

    在两个副标题中,我们用Sphinx的.. automodule::指令从两个模块中提取文档字符串。我们为automodule指令提供了3个参数。

    • :members::这个指令包含模块的所有成员。我们可以列出显式的成员类和函数,而不是列出所有的成员。
    • :undoc-members::这个指令包含缺少适当文档注释的成员。在开发的开始阶段,使用这个指令很方便,我们会获得一些最基本的API信息。
    • :special-members::这个指令包含了默认情况下没有包含在Sphinx文档中的特殊方法名成员。

    这个文档会提供相对完整的视图,但有时过于完整,如果我们又不使用undoc _ members::special-members:指令,我们会获得一个更小、信息更集中的文档。

    我们的implementation.rst文档可以随着项目的演变而变化。我们会添加automodule引用作为已完成的模块。

    .. automodule::指令的组织方式可以为我们提供一份包含复杂的模块或包的集合的蓝图或者概述。我们花了一些时间组织文档呈现的方式,这样它就可以向我们展示软件的组件之间是如何协作的,这比大量的空话更有价值。关键在于不要编写大量记叙性的文本,并为其他的开发者提供引导。

    18.7.5 用Sphinx创建交叉引用

    Sphinx扩展了RST中可以使用的交叉引用技术。最重要的交叉应用能力是可以直接引用特定的Python代码的功能。这些交叉引用会用:role: 'text'语法调用内联的RST标记。在本例中,大量额外的角色都是来自于Sphinx。

    我们有下面几种可用的交叉引用角色。

    • :py:mod:'some _ module'语法会生成指向这个模块或者包的定义的链接。
    • :py:func:'some _ function'语法会生成指向函数定义的链接,可以使用带有module.function或package.module.function的全名。
    • :py:data:'variable'和:py:const:'variable'语法会生成指向定义在.. py:data:: variable指令中的模块变量的链接。常量只是一个不应该被修改的变量。
    • :py:class:'some _ class'语法会链接到类定义。可以使用类似module.class这样的全名。
    • :py:meth:'class.method'语法会链接到一个方法的定义。
    • :py:attr:'class.attribute'语法会链接到定义在一个.. py:attribute:: name指令中的属性。
    • :py:exc:'exception'语法会链接到一个定义好的异常。
    • :py:obj:'some _ object'语法可以创建一个指向对象的通用链接。

    如果我在文档注释中使用"SomeClass",我们会得到用等宽字体显示的类名。如果我们使用:py:class:'SomeClass',会获得一个指向类定义的正确引用,这个引用通常对我们更有帮助。

    每个角色都带有:py:前缀,因为Sphinx可以用来编写关于Python之外的其他语言的文档。通过在每个角色上使用:py:前缀,Sphinx可以合理地提供语法补充和高亮显示。

    下面的文档注释包含指向其他类和异常的显式的交叉引用。

    def card( rank, suit ):
      """Create a :py:class:'Card' instance from rank and suit.
      :param rank: Numeric rank in the range 1-13.
      :param suit: Suit object (often a character from '♣♥◇♠')
      :returns: :py:class:'Card' instance
      :raises :py:exc:'TypeError': rank out of range.
      Etc.
      """

    通过使用:py:class:'Card'而不是"Card",我们可以在这个注释块和Card类的定义间创建显式的链接。类似地,我们使用:py:exc:'TypeError'来创建一个指向这个异常定义的显式链接。

    另外,我们可以通过.. _ some-name::定义一个链接目标,并且在Sphinx文档树的任意文档中,可以通过:ref:'some-name'来引用这个标签。some-name这个名称必须是唯一的。为了确保这种唯一性,通常定义某种层次结构会是一个好方法,这样一来名称就是从文档到节到主题的某种路径。

    18.7.6 将Sphinx文件重构为目录

    对于更大的项目,我们需要使用目录而不是简单的文件。在本例中,我们将使用下面的步骤把一个文件重构为目录。

    1.添加目录:implementataion

    2.将原始的implementation.rst文件移动到implementation/index.rst

    3.修改原始的index rst文件。转换..toctree::指令引用implementation/index而不是implementation

    然后,我们可以开始在implementation目录下工作,在implementation/index.rst文件中使用.. toctree::指令包含本目录中的其他文件。

    当我们的文档被分为包含简单文本文件的简单目录时,我们可以修改小的、集中的文件。每个开发者都可以做出一些重要贡献而不会在试图修改一个大型的文字处理文档时遇到文件共享冲突的情况。