reStructuredText 专题

本文最后更新于 2022年10月3日 凌晨

引子

对许多开发者来说,Markdown 这种标记语言在写作、特别是编写技术文章时非常好用:无需为字体、字号、行间距等不重要的样式问题分心,而标题、列表、引用、图片、代码段等又可通过语法指明语义。

事实上,Markdown 并非唯一的常用标记语言。在 Python 圈内,常常会见到以 .rst 为扩展名的文档:Python 官方文档Flask 文档django 文档pandas 文档RAY 文档……几乎所有能想到的优秀项目的文档都是由另一种强大的标记语言写成的。它就是 reStructuredText

reStructuredText

注意,reStructuredText 是一个单词,而不是两个!

简介

reStructuredText 是一个易于阅读的、所见即所得的纯文本标记语法与解析器系统。它适用于内联程序文档(例如 Python 文档字符串)、快速创建简单的网页和独立文档。reStructuredText 是为特定应用程序域的可扩性而设计的。reStructuredText 解析器是 Docutils 的一个组件。reStructuredText 是对 StructuredTextSetext 轻量标记系统的修订和重新解释。

reStructuredText 的主要目标是定义和实现一种标记语法,用于 Python 文档字符串和其他文档领域,这种语法可读性强、简单,但又足够强大,可用于非凡的用途。标记的预期目的是将 reStructuredText 文档转换为有用的结构化数据格式。

它具有以下特点:

  • 简洁:解析前的纯文本仍具有良好可读性。
  • 强大:强大的 role、directive 以及扩展系统可以极大地增强表达能力。
  • 统一:不论是任何平台,其解析功能都统一在 docutils 模块下。

常用语法

reStructuredText 语法大致分为两部分:

  1. 基本语法:实现段落、列表、标题等基本功能
  2. 指令(Directives):实现插入图像、从 CSV 生成表格、生成目录、甚至 “运行 Python 代码或借用进程间通信机制调用其他代码,并将结果嵌入输出中” 等复杂功能

除特别声明外,reStructuredText 中使用 3 个空格表示 1 个缩进。

本节将介绍常用语法,建议阅读以下内容以获取更多信息:

项目 描述
入门 reStructuredText — Learn reStructureText 2.0.0 文档 很不错的中文入门教程
Quick reStructuredText 简单清晰的源码与解析效果对比
reStructuredText Markup Specification 详尽语法细节,适合深入研究

段落(Paragraphs)

由空行分隔的文本块为一个段落。段落必须有相同的缩进。段落内即使有换行,也仍会被解析为同一个段落。

1
2
3
4
5
6
7
这是一个段落。它很短。

本段落将产生一个缩进的文本块,通常用于引用其他文本。

这是另一个很长很长很长很长很长很长很长很长
很长的段落。在源代码中,通过换行便于阅读,但
在解析后仍会处理为一个段落。

有些情况下,期望能保留源代码中的换行情况,又不希望被拆分为多个段落。这时可以在行首使用管道符 | 来创建 line block:

1
2
3
| 你好世界,虽然没有空行,
| 但仍然换行了。
| 而且它们在同一个段落中。

内联标记(Inline markup)

在段落和其他块元素内,可以使用内联的文本记号来为某个片段的文本标记语义。不同的语义常被渲染为不同的样式,例如 *着重* 默认被渲染为 斜体**强调** 默认被渲染为 粗体

实际上,这些内联标记都是 [reStructuredText 角色](Creating reStructuredText Interpreted Text Roles (sourceforge.io)) 的简写。

注意标记符号与被包裹的文本内容之间不能存在空格,与外部文本之间必须存在空格。

着重(emphasis)

用一对单星号表示着重,对应 HTML 中的 <em>,一般用斜体表示。

1
*HTML 着重元素 (<em>) 标记出需要用户着重阅读的内容*

强调(strong)

用一对双星号表示强调,对应 HTML 中的 <strong>,一般用粗体表示。

1
**Strong 元素 (<strong>)表示文本十分重要,一般用粗体显示。**

字面量(literal)

用一对双反引号表示字面量,实际上为 literal 角色,对应 HTML 中的 <pre> 预定义格式文本,常用作 “行内代码” 的表示方法。

1
``HTML <pre> 元素表示预定义格式文本``

列表(Lists)

  • 首末项与上下文间需要空行,列表项之间无需空行。
  • 列表中使用两个空格作为一个缩进。
  • 嵌套子列表时,子列表的首末项与父列表的项目间需要用空行分隔。
  • 有序列表与无序列表可以相互嵌套、多层嵌套。

无序列表(Bulleted lists)

使用 -+* 创建无序列表。

1
2
- 列表第一项
- 列表第二项

无序列表的项目符号可以混用(虽然不建议这样做),只需保持缩进一致即可。

有序列表(Enumerated lists)

许多符号均可创建有序列表(如 a.A.I.1)(1) 等),但最常用的是数字 + .(手动编号)和# + .(自动编号)两种方式:

1
2
3. 列表第一项(编号可以不从1开始)
4. 列表第二项
1
2
3
#. 列表第一项
#. 列表第二项
#. 它能够自动编号

定义列表(Definition lists)

定义列表将术语与定义相关联。

1
2
3
4
5
6
7
术语(限定在一行文本)
术语的定义,必须使用缩进。

下一个术语
术语是单行短语;定义是一个或多个
段落或正文元素,比术语增加一个缩进
术语和定义之间不允许有空行。

标题/章节(Sections)

reStructuredText 并不像 Markdown、HTML 那样用一种固定的语法表示各级标题,而是将所有 “标题装饰字符” 标记出的文本视为标题。解析器遇到的第一种标题样式将作为一级标题,第二种作为二级标题……依次类推。这种设计的便捷之处在于可以随时插入新的标题级别。

标题层级数没有限制(虽然导出为 HTML 或其他格式时往往最多支持 6 级标题),但建议写作时避免使用 5 级甚至更深的标题。

有效的标题装饰字符很多,其中建议使用以下项作为标题的 “下划线” 或 “上下划线” 以修饰:

1
= - ` : . ' " ~ ^ _ * + #

以下为部分标题样式示例 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
=============
Section Title
=============

-------------
Section Title
-------------

Section Title
=============

Section Title
-------------

Section Title
'''''''''''''

Section Title
.............

Section Title
~~~~~~~~~~~~~

Section Title
*************

Section Title
+++++++++++++

Section Title
^^^^^^^^^^^^^

表格(Tables)

reStructuredText 语法支持网格表、简单表两种表格形式。通过指令还可以创建 CSV 表List 表等。

网格表(Grid tables)

功能较强的表格,必须手动 “画” 出单元格:

1
2
3
4
5
6
7
+------------------------+------------+----------+
| Header row, column 1 | Header 2 | Header 3 |
+========================+============+==========+
| body row 1, column 1 | column 2 | column 3 |
+------------------------+------------+----------+
| body row 2 | Cells may span |
+------------------------+-----------------------+

简单表(Simple tables)

更简单紧凑,但功能有限:必须包含多个行,且第一列单元格不能包含多行。

1
2
3
4
5
6
====================  ==========  ==========
Header row, column 1 Header 2 Header 3
==================== ========== ==========
body row 1, column 1 column 2 column 3
body row 2 Cells may span columns
==================== ======================

CSV 表(CSV tables)

使用 csv-table 指令可以从 CSV 数据创建表格。

1
2
3
4
5
6
7
8
.. 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?"
"Gannet Ripple", 1.99, "On a stick!"

列表表(List tables)

使用 list-table 指令,可以用列表的形式创建表格。列表的顶级项表示一行,次级项表示一行的各元素:

1
2
3
4
5
6
7
8
9
10
11
.. list-table::

* - 表头1
- 表头2
- 表头3
* - 内容11
- 内容12
- 内容13
* - 内容21
- 内容22
- 内容23

图像(Images)

使用 image 指令figure 指令插入图像,还可以通过选项进行调整。二者区别在于:

  • image 直接插入图片。
  • figure 由 image 、一段标题(一个单行段落)和(可选的)图例组成。对于基于页的媒体(如 PDF),在排版时 figure 可能会浮动到合适的地方。

image 简单用法举例:

1
.. image:: gnu.png

image 较详细用法举例:

1
2
3
4
5
6
7
.. image:: gnu.png
:height: 100px (长度)
:width: 200px (长度或当前行宽的百分比)
:scale: integer percentage (the "%" symbol is optional)
:alt: alternate text
:align: "top", "middle", "bottom", "left", "center", or "right"
:target: text (URI or reference name)

各属性含义可以参考此文

在 reStructuredText 中,一个超链接由引用和靶标两个部分组成:

可以使用目标定义的形式,将引用和靶标分离:

1
2
3
4
5
6
7
8
引用部分需要在名称后加下划线:链接_
如果名称中包含了空格,则需要用反引号包括起来:`链 接`_。

靶标部分的下划线在名称前面:

.. _链接: https://docutils.sourceforge.io/docs/user/rst/quickref.html

如果留空,则会将靶标引至下一个块元素

引用和靶标也可以写在同一处(内联网页链接):

1
2
3
`名称 <https://docutils.sourceforge.io/docs/user/rst/quickref.html>`_

即 `name <target>`_ 的形式。前者将会渲染为显示名称,后者将会作为靶标。

符合规则的 URI 也会自动被解析为超链接,文本与链接本身相同。例如:

1
See https://www.python.org for info.

将被解析为如下 HTML:

1
2
See <a href="https://www.python.org">https://www.python.org</a> for
info.

代码段(Code)

使用 code 指令创建。

1
2
3
4
5
.. code:: python

def my_function():
"just a test"
print 8/2

如果指定了编程语言,则会由 Pygments 语法高亮器处理解析。

TODO

脚注(Footnotes)

TODO

更多信息,请参阅文档

目录(Contents)

使用 contents 指令即可生成本文的目录,类似于某些 Markdown 解析器提供的 TOC 扩展语法。

1
.. contents::

可选参数及更多信息,请参阅文档

警告(Admonition)

可以使用 admonition 下的具体某一个指令来创建一条警告/提示消息。

编辑器

虽然使用任何纯文本编辑器均可完成 reStructuredText 源代码的编写,但期望在编辑时能有语法高亮、自动补全、预览等功能。

安装插件后的 VS Code 可以很好地实现上述功能。

reStructuredText Extension for Visual Studio Code on Strikingly

TODO

reStructuredText vs Markdown

优缺点

相比流传广泛的 Markdown,reStructuredText 具有更好的表达能力、扩展能力以及稳定性。 Markdown 根据其扩展标准、具体实现、使用环境的不同,有多个社区提供了不同的 Markdown 支持。 在具有丰富生态的同时,也陷入了生态分裂的窘境。

由于使用 Markdown 不可避免的表现力不足的缺点,各个实现往不同的方向寻找扩展方法。 有的 Markdown 使用 {% parameters %} 形式的 shortcode 来提供扩展,如 markdown-it; 有的 Markdown 将额外的功能写入解析器,如最为强大的 pandoc; 有的 Markdown 试图添加新的语法,例如 latex 风格的指令。

但它们的生态是分裂的,在前者提供的扩展,后者可能未提供,或者以另一种形式提供, 导致文档难以迁移。

reStructuredText 在这一方面拥有巨大的优势:它以及提供了一个完善的扩展开发环境。 任何表达都可以通过统一的 role 或 directive API 来实现!

——Learn reStructureText 2.0.0 文档

相互转换

可以使用工具 Pandoc 实现这两种语言间的相互转换。虽然不可避免地会丢失一些非共有的特性和细节,但完全可以胜任日常简单文档的格式转换。

查看 Pandoc 文档的安装页面,最简单的安装方式为:前往 GitHub Releases 页面下载适合自己操作系统的最新版安装包即可。完成安装后,pandoc 命令已被添加至系统环境变量,可以在任意位置使用。

最简单的使用方式如下:

1
pandoc -f markdown -t rst from.md  -s -o to.rst

更多信息,请参阅 Pandoc 用户手册

Sphinx

实际上,本文正是为介绍 Sphinx 做铺垫。

参考资料

reStructuredText

reStructuredText Markup Specification

reStructuredText Directives

Learn reStructureText 2.0.0 文档

Sphinx reStructuredText 语法入门 — MegEngine 文档

碎碎念

精巧的标记语言

reStructuredText 这篇博文的写作难度远高于预期。本以为简单复制粘贴整理一些 “基本语法”,加上简单介绍即可。然而真正开始阅读 Markup Specification 等详尽文档说明时,渐惊讶于 “角色”、“指令” 等精巧的设计和非常丰富的功能。这次学习之旅不仅仅掌握了一门标记语言的基本用法,更初步领略到了标记语言背后复杂精妙的设计、权衡。

Pyject 首次预告

reStructuredText 正是对 SphinxRead the Docs 这一组 Python 项目文档编写托管工作流的铺垫。而文档工作流,以及之前所写的代码格式化工具 Black虚拟环境与依赖管理工具 Poetry静态类型检查工具 Mypy 等等,其实都是一个大型项目 Pyject 的组成部分。

PyjectPython Project 的缩写,旨在为进行大型多人合作的 Python 项目中可能遇到的非代码编程问题(文档编写、代码风格、版本控制、依赖管理、静态类型检查……)提供指引,介绍对应的工具以解决这些问题。

显然,Pyject 的内容非常之多,目前已完成的部分也较为独立,相互间缺乏整理联系,还有很长的路要走。计划在 “文档编写与托管” 这一部分初稿完成后,创建 GitHub 仓库,并开始将已完成的文章进行整合与查漏补缺。


reStructuredText 专题
https://muzing.top/posts/4ca11f0b/
作者
Muzing
发布于
2022年5月12日
更新于
2022年10月3日
许可协议