用Sphinx编写API文档

简介

本文档介绍如何在写代码的同时写下文档内容,并在后期用sphinx+restructuredText生成文档。本章主要介绍docstring和sphinx。

Docstring——将文档写在代码中

文档字符串(docstring)是书写在源代码中的文档,类似于注释。但它和注释及javadoc不一样,会在运行时中保留,这样在程序运行时也可以查看文档信息。

Python,Lisp和Julia等语言都支持这一功能。

在Python的交互式界面下,我们可以通过help()函数显示docstring。

>>> import requests
>>> help(requests.get)
Help on function get in module requests.api:
get(url, params=None, **kwargs)
    Sends a GET request.
    :param url: URL for the new :class:`Request` object.
    :param params: (optional) Dictionary or bytes to be sent in the query string for the :class:`Request`.
    :param \*\*kwargs: Optional arguments that ``request`` takes.
    :return: :class:`Response <Response>` object
    :rtype: requests.Response

Python中,docstring用三个引号引起来。位于文件开头则是对整个文件的说明。位于类或者函数下方则是对这两者的说明。

例如上面的代码显示的requests的get函数的说明在源代码中是这样保存的:

def get(url, params=None, **kwargs):
    r"""Sends a GET request.
    :param url: URL for the new :class:`Request` object.
    :param params: (optional) Dictionary or bytes to be sent in the query string for the :class:`Request`.
    :param \*\*kwargs: Optional arguments that ``request`` takes.
    :return: :class:`Response <Response>` object
    :rtype: requests.Response
    """
    kwargs.setdefault('allow_redirects', True)
    return request('get', url, params=params, **kwargs)

我们可以分析一下docstring都由哪几部分组成。比如一个函数,一般由简介、参数、返回值和返回类型构成docstring的内容。

根据Python的PEP 257标准,python文件一般都要求有docstring。Docstring作为注释,可以使代码更容易被别人读懂。同时它也能方便的在Python终端中被调用。

Sphinx——文档生成工具

_images/sphinx.JPG

Sphinx是用来生成文档的工具,最初专门为Python设计,很多知名的Python项目都采用Sphinx编写的文档。

它使用reStructuredText这一标记语言写文档,可以输出多种格式,包括PDF、HTML和man page等等。Sphinx有apidoc这一扩展,可以自动从python源文件生成API文档。

Sphinx本身用Python编写,可以通过以下命令安装:

pip install -U Sphinx

以下是Sphinx生成的网页版文档的界面:

_images/home.JPG

以下是一些使用Sphinx编写文档的项目链接:

快速上手

这一章教您如何快速构建您的API文档。

创建一个sphinx项目

  1. 首先,我们运行 sphinx-quickstart 来自动创建一个sphinx项目。

你会被问到一些关于项目设置的问题,其中需要注意的是,您需要启用autodoc扩展。

Indicate which of the following Sphinx extensions should be enabled:
> autodoc: automatically insert docstrings from modules (y/n) [n]: y
> doctest: automatically test code snippets in doctest blocks (y/n) [n]:
...

如果您创建时没有选择启用autodoc,您也可以通过修改conf.py文件实现:需要修改extensions那个列表,加入以下内容:

sphinx.ext.autodoc',
  1. 运行如上命令后会生成一个文件夹,结构如下
目录
│  Makefile
│  make.bat
│
├─source
│  │  conf.py
│  │  index.rst
│  │
│  ├─_templates
│  └─_static
└─build

source文件夹是保存rst源文件的,build是保存生成的html和pdf等文件的,根据之前的设定,两个文件夹可以合并。

index.rst是项目的主文档,我们可以在其中的toctree里添加其它rst文件。

.. toctree::
    :maxdepth: 2

    usage/installation
    usage/quickstart
    ...
  1. 生成文档。

在根目录下运行如下命令,可以选择不同的输出格式,比如以下命令会输出网页。

make html

基本的sphinx项目介绍就是这样,我们下一步讲解如何从含有docstring的源代码文件生成API文档。

自动生成API文档

我们需要使用sphinx-apidoc来代码文件生成对应rst文件,使用方法如下:

$ sphinx-apidoc [options] -o outputdir packagedir [pathnames]

比如我们把源代码放在source文件夹的getYiyi文件夹里,要在source文件夹下生成rst文件,则需要运行如下命令:

$ sphinx-apidoc -o ./ getYiyi

运行后会生成modules.rst和对应每个代码文件名rst文件。我们在index.rst里引用生成的modules文件即可以将生成的文档集成进来。

除此以外,我们还需要进行一些设置。

  • 修改conf.py文件,将以下内容:
#import os
#import sys
#sys.path.insert(0, os.path.abspath(''))

修改为::

import os
import sys
sys.path.insert(0, os.path.abspath('./getYiyi'))

这是为sphinx指定了保存Python代码的路径。

警告

Sphinx需要导入python模块来提取docstring,所以一定要记着加上 if __name__ == ‘__main__’ 这一条件。

如果你还想添加查看源代码的功能,需要在extension里加入 'sphinx.ext.viewcode',

最后生成的结果如下:

_images/source.JPG

文档内容可以在Indices and tables中进行检索。

_images/index.JPG

ApiDoc使用的指令

打开生成的代码的rst文件,可以看到如下内容:

getYiyi
==============

.. automodule:: getYiyi
    :members:
    :undoc-members:
    :show-inheritance:

automodule指令会自动生成所有的class、function和exception等内容。可以在它下面的members里指定要生成的对象。 使用 :undoc-members: 后,没有docstirng的部分也会得到处理。 :show-inheritance: 用来显示基类的信息。

使用如下的autoclass

.. autoclass:: Noodle
    :members:

则不会对私有的方法(以_开始和结束)做处理。如果要记录私有的方法和属性,可以使用 :private-members:。要记录特殊对象(以__开始和结束),可以使用 :special-members:

更多参见sphinx官网:http://www.sphinx-doc.org/en/master/ext/autodoc.html

用restructuredText自己撰写文档

自动生成的API文档可能比较简单,我们会需要自己补充一些内容,这时就需要我们用restructuredText进行写作了。

restructuredText是Sphinx默认使用的写作语言,可以让我们像写代码一样来进行文档的创作。

段落

段落是rst中的基本单位,每个段落需要有一个空行分割。

行内标记

常见的行内标记有以下几个,用法非常简单。

  • 一个星号: *text* 斜体
  • 两个星号: **text** 粗体
  • 反引号: ``text`` 表示等宽代码。

列表

无序列表用星号开头,有序列表可以用数字或者井号开头。

* This is a bulleted list.
* It has two items, the second
item uses two lines.

1. This is a numbered list.
2. It has two items too.

#. This is a numbered list.
#. It has two items too.

定义列表则使用缩进来实现:

term
    Description.

分级

rst使用以下形式表示标题:

=================
This is a heading
=================

使用不同的符号表示不同的等级,但不限定每级使用什么符号。推荐使用如下标准:

  • # 用于部分
  • * 用于章节
  • = 一级标题
  • - 二级标题
  • ^ 三级标题

代码块

在段落后面加上::,代码需要空一行并缩进后输入在后面。默认支持python代码的高亮显示。我们也可以用code-block指令来引用代码。

代码测试块

代码测试块不需要像代码块一样进行缩进:

>>> 1 + 1
2

代码对象

我们可以通过以下方式来记录一个python函数::

.. py:function:: enumerate(sequence[, start=0])

    Return an iterator that yields tuples of an index and an item of the
    *sequence*. (And so on.)

结果:

enumerate(sequence[, start=0])

Return an iterator that yields tuples of an index and an item of the sequence. (And so on.)

除了 py:function ,还可以使用 py:classpy:method

我们可以用 :py:func: 角色来进行引用::

The :py:func:`enumerate` function can be used for ...

效果:enumerate()

指令

Directives是Roles之外的另一个rst使用的显式标记,用以拓展它的功能。Directive block包含三个部分:参数(arguments),选项(options,一个rst语法表示的列表),内容(content)。

它的结构通常是这样的::

+-------+-------------------------------+
| ".. " | directive type "::" directive |
+-------+ block                         |
        |                               |
        +-------------------------------+

比如插入一个图片::

.. image:: http://www.w3school.com.cn//i/eg_tulip.jpg
http://www.w3school.com.cn//i/eg_tulip.jpg

插入一个危险提示::

.. DANGER::
    Beware killer rabbits!

危险

Beware killer rabbits!

插入一个csv表格::

.. csv-table:: Frozen Delights!
   :header: "Treat", "Quantity", "Description"
   :widths: 15, 10, 30

   "Albatross", 2.99, "On a stick!"
   "Crunchy Frog", 1.49, "If we took the bones out, it wouldn't be crunchy, now would it?"
Frozen Delights!
Treat Quantity Description
Albatross 2.99 On a stick!
Crunchy Frog 1.49 If we took the bones out, it wouldn’t be crunchy, now would it?

sphinx给rst添加了一个toctree指令,用来安排目录。它可以连接不同的文件,使得我们可以把内容写在不同的文件里。:

.. toctree::
    :maxdepth: 2

    usage/installation
    usage/quickstart
    ...

链接

使用以下代码可以在行内使用链接::

`Link text <https://domain.invalid/>`_

更多

生成中文PDF

Sphinx是将rst文件转为latex文件以生成pdf的,装好latex后,在项目目录下运行make latexpdf就可以生成了。

以下是一些注意事项,主要是conf.py的修改:

  • 修改latex_elements里的preamble,添加ctex或者cjk宏包可以支持中文。
  • 修改language可以更改默认的语言,在生成的tex文件中会使用renewcommand修改原来的日期等信息为对应语言的。但默认中文的章节名没有得到更改,可以用以下代码不显示英文的Chapter。注意latex里有很多斜杠,在python中需要在引号前加r以避免转义。
'preamble': r'''
\addto\captionsenglish{\renewcommand{\chaptername}{}}
\usepackage[UTF8, scheme = plain]{ctex}
''',

更换网页主题

修改conf.py文件的 html_theme = 'alabaster' 可以换主题。更多设置可以在此找到:http://www.sphinx-doc.org/en/stable/theming.html

使用readthedocs托管文档

readthedocs是一个文档托管平台,很多著名的python项目都将文档托管在这上面。

我们将项目传到github上,再在readthedocs中绑定该项目。这样就有了一个持续集成环境。每次修改被提交到github上,readthedocs就会基于最新的项目构建一次网页和PDF。

使用Markdown

Markdown是比restructuredText更加轻量的标记语言。Sphinx支持用Markdown进行写作。

启用Markdown需要如下步骤:

  1. 安装recommonmark::

    pip install recommonmark
    
  2. 添加如下内容到conf.py中::

    source_parsers = {
    '.md': 'recommonmark.parser.CommonMarkParser',
    }
    
  3. 添加Markdown的文件扩展名到配置文件的source_suffix变量::

    source_suffix = ['.rst', '.md']
    

给其它语言生成API文档

Sphinx默认支持生成Python的API文档,但对于其它语言的支持不佳。有一款Sphinx的扩展,叫做Breathe,便可以使Sphinx支持其它语言。

Breathe是Sphinx和Doxygen这两个文档工具的桥梁,这样Doxygen支持的语言也可以用到Sphinx当中。而Doxygen支持的语言就比较广泛了:

  • C, C++
  • Objective-C
  • C#
  • PHP
  • Java
  • Python
  • IDL (Corba, Microsoft, and UNO/OpenOffice flavors)
  • Fortran
  • VHDL
  • Tcl
  • D

更多见Breathe的主页:http://breathe.readthedocs.io/en/latest/

本地化文档

Sphinx支持使用gettext以支持项目的本地化。gettext是本地化常用的一个标准。

下图是一个Sphinx本地化的大致流程:

_images/translation.png

以下是具体操作方法:

  1. pip install sphinx-intl 命令安装sphinx-intl。

  2. 在conf.py文件中添加以下配置::

    locale_dirs = ['locale/']   # path is example but recommended.
    gettext_compact = False     # optional.
    
  3. 生成gettext的pot文件::

    make gettext
    
  4. 创建或者更新locale文件夹,这里我们可以指定目标语言::

    sphinx-intl update -p _build/gettext -l en
    
  5. 翻译上一步在locale/en/LC_MESSAGES文件夹下生成的po文件。

  6. 生成翻译的文档::

    make -e SPHINXOPTS="-D language='en'" html
    

或者直接使用 sphinx-build 来生成在指定文件夹::

sphinx-build -M html source en -D language=en

REST API

你可以用Sphinx来手写API文档,但更加推荐使用支持OpenAPI规范的专门的REST API工具,比如国内阿里的rap和开源的Swagger。

Swagger有以下几点好处:

  • Swagger 可以生成一个具有互动性的API控制台,开发者可以用来快速学习和尝试API。
  • Swagger 可以生成客户端SDK代码用于各种不同的平台上的实现。
  • Swagger 文件可以在许多不同的平台上从代码注释中自动生成。
  • Swagger 有一个强大的社区,里面有许多强悍的贡献者。

这里是一个详细的REST API文档编写教程:http://idratherbewriting.com/learnapidoc/

参考文献

本教程的编写参考了网络上的资源,在此进行记录。