当前位置:   article > 正文

python技术文档_Python技术文档最佳实践

python项目交付的技术文档

所有好的产品都应该有一份简洁易读的使用说明书,除了苹果的产品。苹果认为他们的产品应该设计成为无须说明,用户天生就应该知道如何使用的那种。

但是很显然,对于软件来说,其复杂性之高,往往要求有与之配套的详尽的帮助文档,使用者才好上手。即使是开源产品,人们通常也是首先借助产品的帮助文档快速上手。在一个速食时代,如果不是逼不得已,谁有时间去一行一行地看代码呢?

那么,什么是一个好的文档?除了要求技术作者本身有较好的文笔之外,一个好的帮助文档常常还包括以下技术要求:规范的文档结构

能够提供必要的导航和交叉引用,帮助读者进一步阅读,并且无死链

内容准确无误,包括文档版本与代码实现始终保持一致(多版本)

文档在线托管,随时可阅读和可搜索

在必要时能够生成各种格式,比如html, PDF, epub等。

这篇文章将探索常见的文档构建技术栈。我们的重点不在于提供一份大而全的cookbook,而在于探索各种可能的方案,并对它们进行比较,从而帮助您选择自己最适合的方案。至于如何一步步地应用这些方案,文章也提供了丰富的链接供参考。

通过阅读这篇文章,您将了解到:文档结构的最佳实践

文档构建的两大门派

如何自动生成API文档

如何使用readthedocs进行在线文档托管

1. 核心概念

1.1. 文档的组成

一份技术文档通常有两个来源:一是我们在写代码的过程中按照一定风格提供的注释,通过工具将其提取出来形成的所谓API文档,这部分文档深入到细节之中;二是在此之外,我们特别撰写的帮助文档,相比API文档,它们更加宏观概要,涵盖了API文档中不适合提及的部分,比如整个软件的安装指南、License信息、版本历史等。

下面的清单列出了相关的文档及其布局(Layout):

README.rst

LICENSE

HISTORY.rst

AUTHORS.rst

CONTRIBUTING.rst

setup.py

requirements.txt

sample/

docs/conf.py

docs/index.rst

tests/

这个布局是《Python最佳实践指南》一书中推荐的,它的最初出处是Knnedth Reitz在2013年推荐的一个Python项目布局的最佳实践,为适应开源项目的需要,我在这里增加了CONTRIBUTING.rst和AUTHORS.rst两个文件。

如果你使用Cookiecutter-pypackage来生成项目的框架,你会发现它生成的项目正好就包括了这些文件。

Tip:

cookiecutter在Github上有13k stars, cookiecutter-pypackage则有2.8k stars,它们已成为生成项目框架的某种事实标准。

API文档将在构建的过程中动态生成;我们撰写的各种文档,比如deployment, usage, faq, tutorial等,一般都会放在docs目录下。对于超大型的项目,或者包含子项目的工程,还会在docs目录下创建多个子目录。此外,根据构建工具的需要,在docs目录下还会放置配置文件等等。

1.2. 文档格式

技术文档一般使用纯文本格式的超集来书写。常见的格式有[reStructuredText](https://docutils.sourceforge.io/rst.html和Markdown(以下称rst)。前者历史更为久远,比较复杂,但功能也更为强大;后者比较新颖,语法十分简洁,在一些第三方插件的支持下,功能上也在逐渐追上来。

我们推荐使用Markdown来作为主要的文档格式,原因是使用Markdown可以节省较多的时间;在Markdown标准语法无法涵盖的一些特殊情况上,可以使用插件或者其它workaround方法来补救。

这里我们通过几个例子来比较一下两种格式上的区别。比如要生成一到六级的标题,reStructured的语法如下所示:

一级标题

========

二级标题

--------

三级标题

^^^^^^^^^

四级标题

~~~~~~~~

五级标题

^^^^^^^^^

六级标题

.........

这种语法的繁琐和难用之外在于,首先标题字符数与下面的标点符号数必须匹配。当使用中文时,还必须使用两倍的标点符号数来匹配。除了在输入上不够简洁,易出错外(主要是指数量匹配),使用者还必须记住每个符号与标题级别的对应关系,否则生成的文档就会出现标题级别错误。

在Markdown中,标题使用 #来引起,有几个#,就意味着是几级标题,非常简明,完全没有上述烦恼。

当然rst也有Markdown力有不逮的地方。它强大的指令(directive)语法使之很容易扩展。Sphinx利用这一特性扩展出很多高效的指令,比如csv-table:

.. csv-table:: 物理内存需求表

:header: "行情数据","记录数(每品种)","时长(年)","物理内存(GB)"

:widths: 12, 15, 10, 15

日线,1000,4,0.75

上面的语法将生成下面的表格:

如果你使用过Markdown的表格(实际上Markdown标准语法并不支持表格,表格已经是扩展的语法,但已成为事实上的标准,几乎所有的Markdown viewer都会支持),你就会了解在Markdown中画表格是多么繁琐的一件事!不过,如果你使用vscode,那么也可以用扩展可以将csv数据转换成为Markdown支持的表格格式。下面这个表格,就是先输入csv数据,再转换成Markdown表格的:

下表列出了两种语法各自的能力特征。对rst来说,一些通过Sphinx支持的功能基本上已成事实标准,多数能渲染rst的工具也支持。

1.3. Sphinx vs Mkdocs, 两种主要的构建工具

rst和Markdown为我们提供了可靠的(即无须任何工具也可阅读)、但是降了级的阅读体验。所以,我们需要文档构建工具,将以这些文档格式写成的文件,转换成富文本格式的文件,比如html,pdf等。此外,在一个较大的工程中,我们的文档也必然是分成多个文档来组织,而决不会是一个单一文档。如何将多个文档统合起来,使之呈现一定的结构,文档各部分能够相互链接,也需要构建工具来实现。

Sphinx和Mkdocs就是两种比较重要的文档构建工具。

Sphinx是始于2008年5月的一种文档构建工具,当前版本3.3。其主要功能是通过主控文档来统合各个子文档,生成文档结构(toctree),自动生成API文档,实现文档内及跨文、跨项目的引用,以及界面主题功能。

在早期的版本中,Sphinx并没有生成API文档的功能,我们通过第三方工具,sphinx-apidoc来实现这一功能。大约是从2018年起,Sphinx通过autodoc这一扩展来实现了生成API文档的功能。现在的项目中,已经没有必要再使用sphinx-apidoc这一工具了(注:如果你使用cookiecutter-pypackage来生成项目,它仍然在使用这一工具)。

intersphinx是其特色功能,它允许你在两个不同的文档中相互链接。比如,你在自己的项目中重载了Python标准库中的某个实现,自己提供了新增实现的这部分文档,但对于未做改变的那部分功能,你并不希望将它的帮助文档重写一遍,这样就有了链接到Python标准库文档的需求。比如,通过intersphinx,你可以使用 :py:class:`zipfile.ZipFile` 来跳转到Python标准库的ZipFile类的文档上。虽然也可以直接使用一个外部链接来实现这样的跳转,但毫无疑问,intersphinx的语法更为简洁。

Mkdocs出现于2014年,当前版本1.1。其主要功能除了构建项目文档外,还可以用来构建静态站点。在构建项目文档方面,它主要提供文档统合功能(包括 toctree)和界面主题,其它功能(比如API文档)要依靠插件来实现。与Sphinx相比,它提供了更好的实时预览能力。Sphinx自身没有提供这一能力,有一些第三方工具(比如vscode中的rst插件,提供了单篇文章的预览功能。很显然Mkdocs无法提供intersphinx的功能,但在项目内的相互引用是完全满足要求的。

这两种文档构建工具都得到了readthedocs的支持。在多数情况下,我们更推荐使用mkdocs及Markdown语法。

3. 使用Sphinx构建文档

3.1. 初始化文档结构

您可以使用前面提到的cookiecutter-pypackage来生成项目的框架。它生成的项目框架就包含了Sphinx构建工具及相关配置。

如果您没有使用框架代码生成工具,也可以在安装sphinx之后,运行下面的代码来初始化文档:

pip install sphinx

# exec this in your project root folder!!!

shpinx-quickstart

Sphinx会提示你输入项目名称、作者、版本等信息,最终生成docs目录及以下文件:

docs/

docs/conf.py

docs/index.rst

docs/Makefile

docs/make.bat

docs/_build

docs/_static

docs/_templates

如果文档中使用了图像文件,应该放在_static目录下。

现在运行 make html就可以生成一份文档。你可以通过浏览器打开_build/index.html来阅读,也可以通过python -m http.server -d _build/index,然后再通过浏览器来访问阅读。

按照Python的最佳实践,我们一般把README.rst, AUTHOR.rst, HISTORY.rst放在项目的根目录下,即与Sphinx的文档根目录同级。而按Sphinx的要求,文档又必须放置在docs目录下。我们当然不想同样的文件,在两个目录下各放置一份拷贝。为解决这个问题,我们一般使用include语法,来将父目录中的同名文件包含进来。比如上述index.rst中的history文件:

# content of docs/history.rst

.. include:: ../HISTORY.rst

这样就避免了同一份文件,出现多个拷贝的情况。

3.2. 主控文档和工具链

如果您是通过Sphinx-quickstart来进行初始化的,它的向导会引您进行一些工具链的配置,比如象autodoc(用于生成API文档)。为了完备起见,我们还是再提一下这个话题。

Sphinx在构建文档时,需要一个主控文档,一般是index.rst:

文档Title

==========

.. toctree::

:maxdepth: 2

deployment

usage

api

contributing

authors

history

Indices and tables

==================

* :ref:`genindex`

* :ref:`modindex`

* :ref:`search`

Sphinx通过主控文档,把单个文档串联起来。 上面的toctree中的每一个入口(比如deployment),都对应到一篇文档(比如deployment.rst)。此外,还包含了索引和搜索入口。

3.2.1. 生成API文档

要自动生成API文档,我们需要配置autodoc扩展。Sphinx的配置文档是docs/conf.py:

# from conf.py

# 要实现autodoc的功能,你的模块必须能够导入,因此先声明导入路径

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

# 声明autodoc扩展

extensions = [

'sphinx.ext.intersphinx',

'sphinx.ext.autodoc',

'sphinx.ext.doctest'

]

注意到在index.rst中我们声明了对api文档的引用。这个文档用作autodoc的文档入口,其语法入下:

Crawler Python API

==================

Getting started with Crawler is easy.

The main class you need to care about is :class:`~crawler.main.Crawler`

crawler.main

------------

.. automodule:: crawler.main

:members:

crawler.utils

-------------

.. testsetup:: *

from crawler.utils import should_ignore, log

.. automethod:: crawler.utils.should_ignore

.. doctest::

>>> should_ignore(['blog/$'], 'http://ericholscher.com/blog/')

True

这里假设了一个名为Crawler的程序,它共有main和util两个模块。我们通过.. automodule:: crawler.main将main模块引入,并使用..doctest::来进行测试。

在Sphinx进行文档构建时,就会生成这两个模块对应的API文档,并将上述入口绑定到正确的链接上。

Sphinx的功能比较强大,因而其学习曲线也比较陡峭。在学习时,可以将其渲染好的教程与教程的源码对照起来看,这样更容易理解。

使用Autodoc生成的API文档,需要我们逐个手动添加入口,就象上面的.. automodules:: cralwer.main那样。对比较大的工程,这样无疑会引入一定的工作量。Sphinx的官方推荐使用sphinx.ext.autosummary扩展来自动化这一任务。前面已经提到,在较早的时候,Sphinx还有一个cli工具,叫sphinx-apidoc可以用来完成这一任务。但根据这篇文章,我们应该转而使用sphinx-ext.autosummary这个扩展。

除此之外,readthedocs官方还开发了一个名为sphinx-autoapi的扩展。与autosummary不同,它在构建API文档时,并不需要导入我们的项目。目前看,除了不需要导入项目之外,没有人特别提到这个扩展与autosummary相比有何优势,这里也就简单提一下,大家可以持续跟踪这个项目的进展。

3.2.2. docstring的样式

如果不做任何配置,Sphinx会使用rst的docstring样式。为简洁起见,我们一般使用google style(最简),或者numpy style(适用于较长的docstring)。

要在文档中使用这两种样式的docstring,你需要启用Napolen扩展。关于这两种样式的示例,我觉得最好的例子来自于MkApi的文档,这里不再赘述。

注意在Sphinx 3.0以后,如果你使用了Type Hint,则在书写docstring时,不必在参数和返回值上声明类型。扩展将自动为你加上类型声明。

3.2.3. 混合使用Markdown

多数人会觉得rst的语法过于繁琐,因此很自然地,我们希望部分文档使用Markdown来书写(如果不能全部使用Markdown的话)。大约从2018年起,readthedocs开发了一个名为recommonmark的扩展,以支持在Sphinx构建过程中部分使用Markdown。

在这种场景下要注意的一个问题是,Markdown文件必须都在docs目录及其下级目录中,而不能出现在项目的根目录下。这样一来,象README,HISTORY这样的文档,就必须仍然使用rst来写(以利用include语法来包含来自上一级的README)。如果要使用Markdown的话,就必须使用符号连接将父目录中的README.md连接到docs目录下(recommenmark自己的文档采用这种方式);或者通过Makefile等第三方工具,在sphinx build之前,将这些文档拷贝到docs目录。

在github上还有一个m2r的项目,及其fork m2r2,可以解决这些问题,不过开发者怠于维护,随着Sphinx版本升级,基本上不可用了。

如果您的项目必须使用rst,那么可以在项目中启用recommonmark,实现两种方式的混用。通过在recommonmark中启用一个名为autostructify的子组件,可以将Markdown文件事前编译成rst文件,再传给Sphinx处理;更妙的是,autostructify组件支持在Markdown中嵌入rst语法,所以即使一些功能Markdown不支持,也可以通过局部使用rst来补救。

如果您对使用Markdown来撰写文档更感兴趣的话,请接着往下看。

4. 使用Mkdocs构建文档

Mkdocs支持完全使用Markdown来撰写文档,并且通过社区提供的插件来支持将生成的API文档与手工文档融合。

mkdocs自身提供的功能非常简单,粗粗看一眼的话,你会觉得它只能用来构建静态网站,而无法用来撰写项目文档。但社区提供了很多插件,加上本身提供的扩展一起,使得简单快捷地构建项目文档成为可能。

安装mkdocs之后,可以看一下它的基本命令:

Mkdocs提供了两种theme,readthedocs和mkdocs。你也可以在社区里寻找更 多的Theme。有些主题很适合构建静态网站。这篇文章给出了一个不错的教程。

我们来简单地看一下使用:

pip install --upgrade pip

pip install mkdocs

pip install mkdocs-material # 安装material主题,可忽略。技术文档一般使用自带的readthedocs主题

# 创建文档结构,在项目根目录下执行

mkdocs new PROJECT_NAME

cd PROJECT_NAME

现在,在项目根目录下应该多了一个docs目录,和一个名为mkdocs.yaml的文件。docs目录下还有一个名为index.md的文件。如果此时运行mkdocs serve -a 0.0.0.0:8000,在浏览器中打开,你会看到如下图所示界面:

Note: 请注意,Mkdocs提供的是实时预览文档,而且有很快的响应速度。

现在来看一看mkdocs.yaml的内容:

site_name: An amazing site!

nav:

- Home: index.md

- 安装: installation.md

- History: history.md

theme: readthedocs

这里mkdocs.yaml充当了主控文档。nav下面的每一项列表,都成为一级菜单。列表项可以用":"来分割,左边的是显示的文字,右边则是连接的文档。

接下来, 我们主要对照文章最初提出的那些要求,看看如何在Mkdocs中通过插件和扩展来实现。

Note 在Mkdocs中既有扩展,又有插件。

4.1. 链接到父目录中的文件

前面提到,我们应该把READEME, HISTORY, AUTHORS, LICENSE等几个文件放在项目根目录下,但又不希望在docs目录中重复它们的拷贝。mkdocs也不能支持这种结构,不过好在有一个好用的插件,mkdocs-include-mkdown-plugin,在安装好之后,修改index.md文件,使之指向父目录的README:

{%

include-markdown "../README.md"

%}

修改mkdocs.yaml,加载include-markdown插件:

site_name: Omicron

nav:

- Home: index.md

- 安装: installation.md

- History: history.md

theme: readthedocs

plugins:

- include-markdown

4.2. API文档

前面已经提到过这个插件, MkApi。但在我们试用中,可能Mkdocstrings的稳定性更好,社区活跃度也更高一些。

这两个插件的配置都不复杂。Mkdocs只支持google style的docstring, 在样式上支持了Material样式。对readthedocs的支持还在测试中,但也将在下一版发布。

4.3. 警示标注

在rst中你可以用这样的警示语:

.. Note:: title of notice

PLEASE DONT

这样的警示语还有tip, important, warnings等好几种。Markdown不支持警示语语法,不过你可以使用Admonitions扩展来增强。

这个扩展支持了info, todo, tip,hint,important, suceess, check, done, warnings, error, example等语法:mkdocs显示的Tip

4.4. 其它

链接到文档、文档中的节标题都很容易。在使用了Mkdocstrings这一类的API文档插件之后,也能够直接链接到模块中的函数。如何链接到其它项目(比如Python标准库)中的对象,没有看到文档说明。

5. 使用Readthedocs托管文档

最好的文档分发方式是使用在线托管,并且一旦有新版本发布,文档能立即得到更新;并且,旧的版本对应的文档也能得到保留。这两个功能,都在read the docs网站上得到完美支持。此外,您也可以使用gitpages。如果您使用Mkdocs来构建的话,似乎也支持多个版本同时在线。

Readethedocs(以下称RTD)是Python文档最重要的托管网站,也是事实上的标准。关于如何使用RTD,请参考它的帮助文档。

这里要注意的几个核心概念:目前RTD支持Sphinx和MkDocs两种构建工具。

RTD上面文档来源于在线代码托管库,比如github。RTD上面的文档与你本地构建的文档没有任何关系。你在本地构建的文档,都不要上传到github上。如果你将github账号与RTD账号进行了绑定,此后每次更新代码,RTD都会自动为你编译文档并发布(也可以登录到RTD上手动触发build)。

如果设置了RTD自动同步代码并build,那么每次往github上push代码时,都会触发一次build,并导致文档更新。所以正确的做法是将RTD绑定到特定的分支上(比如release),只有重要的版本发布时,才往这个分支上push代码,从而触发文档编译。RTD目前并不支持tags。

RTD编译文档时,可能会遇到各种依赖问题。首先应该绑定构建工具(Sphinx和Mkdocs)的版本。RTD提供了readthedocs.yml以供配置(放在项目根目录下)。根据你使用的API文档生成工具,可能还需要导入你的package,这种情况下,可能还需要为你的构建工具指定依赖。在这里有这个配置文件的模板。

在文档构建中可能出现各种问题,为了帮助调试,RTD发布了官方docker image,供大家在本地使用。

6. 结论

Sphinx + RST是构建技术文档的事实标准,整个技术栈比较成熟稳定,但学习曲线比较陡峭,RST的一些语法过于繁琐。随着Markdown的应用越来越成熟,Mkdocs正在成为构建静态站点和技术文档的新工具,并得到的Read the docs的支持。在使用Mkdocs进行技术文档构建时,要注意选用的插件在支持的Python版本、docstring样式及主题方面的限制。下面是两种方式的一个比较:

两种方式一般配置的插件(扩展)见以下清单:

Sphinx:

autodoc

autosummary

recommonmark

mkdocs:

mkdocs-include-markdown-plugin

Admonitions

mkdocstrings

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Guff_9hys/article/detail/993115
推荐阅读
相关标签
  

闽ICP备14008679号