使用Sphinx写项目文档

Posted by Hsz on November 29, 2016

使用sphinx写项目文档

Sphinx是一个开源的python文档生成器,这里有一个中文的使用手册,它采用reStructuredText作为源文件格式,可以输出为html文件或者pdf文件(需要latex).

它需要python作为运行环境,安装只要使用pip就可以.

sphinx项目的基本构建过程是:

  1. 使用快速构建工具搭建项目框架

  2. 修改conf.py做全局设置

  3. (可选)编写文档内容,本文将会讲如何用Markdown来写内容

  4. 构建文档

    构建文档项目可以有两种方法:

    • 使用make命令,make <目标文件类型>命令可以在_build目录下简单快捷地构建文档.

    • 使用sphinx-build [options] sourcedir builddir [filenames]来构建.

      • sphinx-buildmake的底层,用它可以更灵活的构建.
      • sourcedir是源文件目录,
      • builddir是生成文件目录
      • 一般不需要写filenames

      脚本 sphinx-build的选项有:

      • -b 生成器名字

        生成器,决定了生成文档的类型,是最重要的选项. 通用的生成器有:

        • html 生成HTML文档. 默认的生成器.
        • dirhtml 生成HTML文档,但是每个文档都有单一的目录,在用浏览器访问时有漂亮的URLs(没有后缀.html)
        • singlehtml 所有内容生成单一的HTML
        • htmlhelp,qthelp,devhelp,epub 生成HTML文档,建立文档集时包含这些类型之一的额外信息.
        • latex 生成LaTeX源,可使用pdflatex将其编译成PDF文档.
        • man 生成UNIX系统的groff格式手册.
        • texinfo 生成Texinfo文件,可以使用makeinfo产生Info文件.
        • text 生成纯文本文件.
        • gettext 生成gettext-style分类信息(.pot 文件).
        • doctest 运行文档集内所有测试,如果doctest扩展可用.
        • linkcheck 检查所有外部链接的可信度.查看Available builders,列出了Sphinx支持的所有生成器及其可添加的扩展.
      • -a 给出时重写全部文档,默认则仅重新生成有新的源文件或源文件被修改的文档.(不适用于所有生成器)

      • -E 不使用保存的environment(环境,缓存了所有的参考索引),而是完全重建.默认仅读取和解析最近新添加及改动的源文件.

      • -t <tag> 定义标签<tag>.与only指令相关,标签是一个目录集合,仅处理标签目录中的内容.

      • -d <path>目前Sphinx生成输出前会读取和解析所有的源文件,解析过的源文件被缓存成”doctree pickles”.通常,这些文件被放在生成目录的 .doctrees文件夹中;这个选项可以选择不同的缓存目录(doctrees可以被所有的生存器共享).

      • -c <path> 不使用源目录下的conf.py而是使用指定的配置文件.注意在配置文件中提及的路径都是相对配置文件所在目录的相对路径,因此路径必须一致.

      • -C 不查找配置文件,仅使用选项-D的配置.

      • -D setting=<value> 覆盖conf.py里的配置值.value是一个字符串或字典.例如:-D latex_elements.docclass=scrartcl.布尔值使用01代替.

      • -A name=<value> 模板里的name变量使用value值代替.

      • -n 采用nit-picky模式.该模式下所有错误都会产生警告信息.

      • -N 不产生彩色输出.(在Windows下,彩色输出一直是不可用的)

      • -q 不产生标准输出,仅使用标准错误输出输出警告和错误信息.

      • -Q 不产生标准输出,也不产生警告信息,仅使用标准错误输出输出错误信息.

      • <-w file> 除标准错误输出外,将警告(错误)输出到指定文件.

      • -W将警告视为错误.产生第一个警告就停止文档生成活动,sphinx-build 在状态1退出.

      • -P 发生未绑定的异常时运行Python调试器pdb.(仅在调试时使用)

    源目录与目标目录后面,可以给出一个到多个文件名.Sphinx会尝试仅生成这些文件(及其依赖文件).

项目快速构建工具

sphinx提供了两个快速构建工具

sphinx-apidoc用于读取python项目的__doc__来生成api文档

  1. 使用sphinx-apidoc搭建项目框架

     sphinx-apidoc [options] -o <outputdir> <packagedir> [pathnames]
    
    • packagedir 是生成文档的页面的根目录
    • outputdir 是生成源文件的输出目录
    • pathnames 给出的路径在生成时不会被忽略.

    脚本sphinx-apidoc也有一些选项:

    • -o <outputdir> 给出文档页的根目录.

    • -f,--force 通常sphinx-apidoc 不会重写任何文件. 使用该项强制重写所有文件.

    • -n,--dry-run 采用该选项,将不会产生任何文件.

    • -s <suffix> 生成文件的后缀名,默认为 rst.

    • -d <maxdepth> 目录的最大层次.

    • -T,--no-toc 避免生成文件modules.rst. 当有选项--full时不起作用.

    • -F,--full 创建整个Sphinx项目, 与sphinx-quickstart使用一样的机制. 大多数配置值被设置为默认,可通过下面选项去修改.

    • -H project 设置项目名

    • -A author 设置作者名

    • -V version 设置文档版本

    • -R release 设置文档的发布版本

  • 配置conf.py

    虽说是自动生成,但也还是要做配置的.配置文件中,在import sys后面添加

      sys.path.insert(0, os.path.abspath('..'))
    
  • 在sphinx项目下使用make html构建api文档

    api文档好不好更多的是看你在代码中段注释用的怎么样.这种方式构建的api文档只有api文档而已,如果要做些定制,就需要知道更多的sphinx的细节.在后面会有讲解.

sphinx-quickstart sphinx项目快速构建

这个脚本只是起到一个快速构建项目目录结构和基础配置的作用.相较于前面的sphinx-apidoc,它构建的项目更加一般化也更加适合定制.

  1. 使用sphinx-quickstart创建项目目录 一路按照提示选择,创建好后目录结构大约是这样:

     ```
     |-_build
     |-_static
     |-_templates
     |-conf.py
     |-index.rst
     |-Makefile
     ```
    

    其中conf.py前面已经有过接触,就是设置文件,index.rst是主页index.html的源文件,Makefile都懂得,就是make命令的设置文件

    我们还选择了几个插件:

    • autodoc 用来自动生成python文档
    • intersphinx 将外链并入项目
    • todo 一个TODO list插件
    • mathjax 可以显示latex写成的公式
    • viewcode 可以查看到python项目的源码
  2. 修改conf.py做全局设置

    我们获得了一个conf.py,我将它修改成如下:

     #coding: utf-8
     import sys
     import os
     # autodoc需要将python模块引入,因此需要将模块所在位置引入path
     sys.path.insert(0, os.path.abspath('..'))
    
     import recommonmark
     from recommonmark.transform import AutoStructify
     from recommonmark.parser import CommonMarkParser
     # -- General configuration ------------------------------------------------
     extensions = [
         'sphinx.ext.autodoc',
         'sphinx.ext.viewcode',
         'sphinx.ext.todo',
         'sphinx.ext.mathjax',
     ]
     templates_path = ['_templates']  # templates存放位置
     source_parsers = {
         #'.md': CommonMarkParser,
         '.md': CommonMarkParser,
     }
     source_suffix = ['.rst', '.md']  # 可以识别的后缀
     master_doc = 'index'  # 入口文件名
     project = u'testsphinx'  # 项目名
     copyright = u'2016, hsz'  # 版权
     author = u'hsz'  # 作者
     version = u'0.0.1'  # 版本
     release = u'bsd'  # 授权说明
     language = 'zh_CN'  # 语言
     exclude_patterns = ['_build']  # 排除出的文件夹编译
     pygments_style = 'sphinx'  # 代码高亮样式
     # ---html设置------------------------------------------------
     html_theme = 'alabaster'  # 编译为html时用的主题
     html_static_path = ['_static']  # 静态文件夹所在位置
     # Output file base name for HTML help builder.
     htmlhelp_basename = 'testsphinxdoc' # 帮助文档根名字
     # -- latex设置 ---------------------------------------------
     latex_elements = {
         # The paper size ('letterpaper' or 'a4paper').
         #'papersize': 'letterpaper',
    
         # The font size ('10pt', '11pt' or '12pt').
         #'pointsize': '10pt',
    
         # Additional stuff for the LaTeX preamble.
         #'preamble': '',
    
         # Latex figure (float) alignment
         #'figure_align': 'htbp',
     }
     latex_documents = [
         (master_doc, 'testsphinx.tex', u'testsphinx Documentation',
             u'hsz', 'manual'),
     ]
     # -- manual设置---------------------------------------
     man_pages = [
         (master_doc, 'testsphinx', u'testsphinx Documentation',
             [author], 1)
     ]
     # -- Texinfo 设置 -------------------------------------------
     texinfo_documents = [
         (master_doc, 'testsphinx', u'testsphinx Documentation',
             author, 'testsphinx', 'One line description of project.',
             'Miscellaneous'),
     ]
     #---插件-----------------------------------------------------
     #intersphinx_mapping = {'https://docs.python.org/': None}  # intersphinx
     todo_include_todos = True  # 插件todo启用
     url_doc_root = 'xxxxxxxxxxxxxxxxxxxx'
     def setup(app):
         app.add_config_value('recommonmark_config', {
                 'url_resolver': lambda url: url_doc_root + url,
                 'auto_toc_tree_section': 'Contents',
                 }, True)
         app.add_transform(AutoStructify)
    

    其中我配置了对markdown的支持,这需要安装一个语法分析器pip install recommonmark.这个语法分析器对utf-8支持不好,我们需要修改recommonmark/states.py其中一段代码

     rawtext=str(content),
     text=str(content),
    

    改成

     rawtext=str(content.encode("utf-8")).decode("utf-8"),
     text=str(content.encode("utf-8")).decode("utf-8"),
    
  3. 编写项目文档

    从这边开始我们试着给一个超简单的项目写一个说明文档,代码sample.py:

     # coding:utf-8
     u"""牛顿法扩展都n次方根 :math:`\sqrt[k] a` .的表达式为:
     :math:`x_{n+1} = x_n - \\frac {x_n^k-a} {kx_n^{k-1}} = \\frac {k-1}{k} x_n + \\frac {a}{kx_n^{k-1}}`
    
     """
     def sqrt_nt(n, m, round_=5):
         u"""牛顿法开根号.
    
         用法: :math:`sqrt\_nt(n,m,round) = \sqrt[m] n`
    
         Parameters:
             n:<int,float,long> - 被开方的数
             m:<int,float,long>  - 开多少次方
             round:<int> - 精度
    
         Returns:
             <int,float,long,complex> -开方结果
    
         Raises:
             TypeError - 参数类型不对时报错
             ValueError - 被开方参数为复数时报错
         """
         if all(map(lambda x: isinstance(x, (int, float, long,)), [n, m])):
             if n < 0:
                 raise ValueError(u"必须是整数")
             elif n == 0:
                 return 0
             elif n == 1:
                 return 1
             else:
                 deviation = 0.1**(round_+1)
                 seed = n / m
                 counter = 0
                 max_count = 100000
                 now_value = seed
                 last_value = 0
                 while abs(now_value - last_value) > deviation:
                     if counter > max_count:
                         raise ValueError(u"在{count}次循环内未能得到精度为{round}的解".format(
                             count=max_count, round=round_))
                     counter += 1
                     last_value = now_value
                     now_value = (1 - 1.0 / m) * last_value + \
                         n / (m * last_value**(m - 1))
                 return round(now_value, round_)
    
         else:
             raise TypeError(u"必须是数")
    

    我在__doc__中插入了Latex数学公式,公式的插入方式就是:math:+反引号对.

    最终我们写了三个文件:

    • index.md:

        # 欢迎来到Sample的文档
      
        Sample是一个测试用的模块,它实现了一个牛顿法开根的方法
      
        内容:
      
        * [说明](README.md)
        * [API文档](APIdoc.md)
      
        # 补充引用
      
        [sample code](../../../sample.py)
      
        [search](search.html)
      
    • README.md
        # 安装
      
        复制模块到要用的文件位置
      
        # 使用
      
        ## 参数
      
        + n 被开方数
      
        + m 开方次数
      
        + round 精度
      
                from sample import sqrt_nt
                sqrt(2,2)
                sqrt(3,2,5)
      
    • APIdoc.md

        # Sample API文档
      
        ## sample 模块
      
        ```eval_rst
        .. automodule:: sample
            :members:
            :undoc-members:
            :show-inheritance:
        ```
      

4.构建文档 我们修改makefile中

.PHONY: html
html:
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
if [ -d ../docs ];then rm -r ../docs; fi
cp -r $(BUILDDIR)/html ../docs
touch .nojekyll
mv .nojekyll ../docs
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html"

添加把html结果copy出来到docs的命令,这样方便给githubpage用 运行make html即可

使用插件

sphinx有很强的扩展性,他的插件可以在pip里搜sphinxcontrib得到,非常多每个用法也会不相同.

使用插件必须要将插件放入conf.pyextensions变量中 我们在前面已经看到怎么用autodoc了,这边继续讲几个比较有价值的

  • todo

    一个todolist插件,conf.py中需要额外设置一个参数todo_include_todos = Truetodo_link_only=True 有两个rst标识:

    • 定义一个todo

        .. todo::
      
            A
      
            B
      
            C
      
      
    • 收集所有项目下的todo

        .. todolist::
      
  • sphinxcontrib-autojs

    这个插件就是js版本的doctest+autodoc,不过有个小bug,237行需要替换为

      self.source = ("".join(f.readlines())).decode("utf-8")
    

    加上decode("utf-8")才能解析非英文字符

    之后只要在api文档中使用

      .. autojs:: <module path>
          :members: <member name>
    
    

就可以了.它还支持jsdoctest.

  • sphinxcontrib-httpdomain

    这个插件是用来写网络RESTapi的,它的配置项有:

    • http_headers_ignore_prefixes 严格模式下屏蔽的http头前缀

      http_headers_ignore_prefixes = ['X-']

    • http_index_ignore_prefixes 屏蔽的目录前缀

      http_index_ignore_prefixes = ['/internal', '/_proxy']`

    • http_index_shortname 目录短名

      http_index_shortname = 'api'

    • http_index_localname 目录全名

      http_index_localname = "My Project HTTP API"

    • http_strict_mode 严格模式

      http_strict_mode = True

    它的用法比较复杂,可以看https://pythonhosted.org/sphinxcontrib-httpdomain/,

    本文的例子可以在https://github.com/hsz1273327/sphinx_markdown_example看到, 例子的效果可以在http://blog.hszofficial.site/sphinx_markdown_example/看到

  • sphinxcontrib-napoleon

    这个插件可以支持google和numpy风格的api注释

本文的例子可以在我的github上看到

补充

recommonmark对sphinx的markdown扩展支持

  • 写目录 我们可以在平时markdown引用链接语法的基础上在前面加上*引入下一级文件

      * [说明](README.md)
      * [API文档](APIdoc.md)
    
  • 项目内链引用 项目内链引用可以直接像一般的url链接一样使用
      [search](search.html)
    
  • url解析 sample code 不过需要在conf.py中先把url设定好,通过这种方式可以方便的将静态文件作为下载放在页面中

  • latex公式的解析需要在公式两边加上反引号才能被解析

  • 嵌入 reStructuredText, 可以使用类似
      .. autoclass:: recommonmark.transform.AutoStructify
          :show-inheritance:
    

    的代码嵌入reStructuredText格式的内容

  • 目前不支持表格

使用sphinx嫁接doxygen

doxygen是一套多语言的注释文档转接口文档工具,常用在C语言,java等语言上,其作用基本和pydoc一样,只是针对的语言不同. 如果是C语言的项目,或者项目中使用了C语言,那么就会用到它.

sphinx可以通过插件breathe将doxygen生成的xml文件导入生成的网站

其步骤为:

  • (单独项目的话)使用sphinx-quickstart创建sphinx项目

  • 配置conf.py,以支持breathe扩展.前提是这一扩展已经安装.

    加入扩展:

      extensions = ['breathe']
    
  • conf.py中配置breathe:

      breathe_projects = { "myproject": "./my_xml" }
      breathe_default_project = "myproject"
      breathe_domain_by_extension = {"h" : "c"}
    
  • 在需要的.rst文件中加入以下代码引入接口

按breathe扩展语法编写rst文件,比如:

My API
==========

sp.h
----

.. doxygenfile:: sp.h

  • 运行 make html 生成html即可.

在这些步骤中,需要先调用doxygen生成xml,再调用sphinx-build(make html时自动调用)生成最终文档,如果想一步完成,可以在Makefile中加入doxygen调用:

html:
    doxygen Doxyfile
    $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
    @echo
    @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."