过去,本专栏讨论过XML的替代方法-满足您可能会使用XML的许多相同目的的文档格式。 reStructuredText延续了这一传统。 与YAML(适用于数据格式)相反,reStructuredText专为文档而设计; 与智能ASCII相比,reStructuredText更重,更强大且更正式地指定。 与XML相比,所有这些格式都可以使用标准文本编辑器轻松自然地读取和编辑。 使用XML或多或少都需要专门的XML编辑器,例如我之前查看过的那些编辑器(请参阅参考资料 )。
reStructuredText(通常缩写为reST)是Python Docutils项目的一部分。 该项目的目标是创建一套用于处理纯文本文档的工具,包括将其导出为HTML,XML和TeX等结构化格式。 尽管该项目来自Python社区,但它所满足的需求超出了Python。 各种类型的程序员和作家经常创建诸如README,HOWTO,FAQ,应用程序手册之类的文档,而对于Python而言,则创建PEP(Python增强建议)。 对于这些类型的文档,要求用户处理冗长而困难的格式(例如XML或LaTeX)通常是不合理的,即使这些用户是程序员。 但是,仍然经常希望将这些类型的文档用于简单查看之外的目的(例如索引,编译,漂亮打印,过滤等)。
Docutils工具可以满足Python程序员的需求,就像JavaDoc帮助Java程序员或POD帮助Perl程序员一样。 可以将Python模块中的文档转换为Docutils文档树,然后转换为各种输出格式(通常在单个脚本中)。 但是对于本文而言,更有趣的用途是用于常规文档。 对于像这样的文章,甚至对于我即将出版的书,我都是使用智能ASCII编写的。 但是我开始觉得reStructuredText的形式会更好(而且我可能会开发工具来转换现有文档)。
在撰写本文时,Docutils项目正在开发中,尚未发布稳定版本。 现有的工具是好的,但是整个项目是诺言,良好意图,部分文档和一些实际工作工具的结合。 但是,进度是稳定的,此时您可以执行的操作非常有用。
reStructuredText的示例
通过一个简短的示例,您可以更好地了解reStructuredText的含义。 以下文本是PEP 287(假设的PEP的一部分)中的一个示例:
清单1. PEP的纯文本版本
Abstract
This PEP proposes adding frungible doodads [1] to the
core. It extends PEP 9876 [2] via the BCA [3] mechanism.
...
References and Footnotes
[1] http://www.example.org/
[2] PEP 9876, Let's Hope We Never Get Here
http://www.python.org/peps/pep-9876.html
[3] "Bogus Complexity Addition"
清单1中的格式正是PEP在287之前的格式。如果使用reStructuredText标记相同的PEP,则它看起来可能像这样:
清单2. PST的reST版本
Abstract
========
This PEP proposes adding `frungible doodads`_ to the core.
It *extends* PEP 9876 [#pep9876]_ via the BCA [#]_ mechanism.
...
References & Footnotes
======================
.. _frungible doodads: http://www.example.org/
.. [#pep9876] PEP 9876, Let's Hope We Never Get Here
.. [#] "Bogus Complexity Addition"
有一些细节与明文不同。 但是,少量字符的散布确实不会损害可读性。 如果您在文本编辑器或打印页面中看到它,则无需翻阅即可。
清单2中的reST格式的文档可以自动转换成XML方言,例如Docutils Generic DTD定义的:
清单3. PEP的Docutils XML版本
<?xml version="1.0" encoding="UTF-8"?>
<document source="test">
<section id="abstract" name="abstract">
<title>Abstract</title>
<paragraph>This PEP proposes adding <reference
refname="frungible doodads">Frungible doodads</reference>
to the core. It<emphasis>extends</emphasis><reference
refuri="http://www.python.org/peps/pep-9876.html">
PEP 9876</reference><footnote_reference auto="1" id="id1"
refname="pep9876"/> via the BCA <footnote_reference
auto="1" id="id2"/> mechanism.</paragraph>
<paragraph>...</paragraph>
</section>
<section id="references-footnotes"
name="references & footnotes">
<title>References & Footnotes</title>
<target id="frungible-doodads" name="frungible doodads"
refuri="http://www.example.org/"/>
<footnote auto="1" id="pep9876" name="pep9876">
<paragraph><reference
refuri="http://www.python.org/peps/pep-9876.html">PEP
9876</reference>, Let's Hope We Never Get Here
</paragraph>
</footnote>
<footnote auto="1" id="id3">
<paragraph>"Bogus Complexity Addition"
</paragraph>
</footnote>
</section>
</document>
您可以对比这三种格式看到几件事。 最显着的区别是浏览XML版本要困难得多。 但是,值得注意的是reStructuredText工具在reST文档中找到了多少信息。 正确匹配了几种类型的引用,标识了文档部分,并添加了字符级印刷标记。 在其他示例中,可以在处理过程中生成链接的TOC以及其他特殊指令。
docutils项目结构
docutils
程序包由许多子程序包组成,它们之间的关系相当复杂。 PEP 258(Docutils设计规范)包含一个图表,可用于了解整体模式:
图1. Docutils项目模型
该PEP中包含有关组件子包的更完整的说明,但是在这里需要重复简短的说明。
将reST文本转换为节点树的繁重工作是由docutils.parsers.rst
子包完成的。 reStructuredText解析器以面向行的方式处理源,在每行上寻找状态转换; 如果未找到其他过渡模式,则text
过渡会抓住这一行。 过渡包括缩进更改,特殊的前导符号等功能。 默认值仅包括下一行作为当前节点内的更多文本。
此结构类似于智能ASCII解析器txt2dw和txt2html中使用的结构 。 其他解析器将位于docutils.parsers
层次结构下,但目前未提供任何解析器。 但是,有一个实验性的Python源代码解析器将Python源文件视为文档树。
一旦docutils.transforms
子程序包为文档生成了节点树,您就可以通过多种方式来操纵该树。 例如,如果您指定了包含目录的指令,则文档树将遍历以标识列出的项目。 同样,转换在此阶段执行对引用和链接的一些清理。 在初始遍历期间,提示转换的占位符会填充树中未解析元素将要到达的位置。
面向事件的输出
各种docutils.writers
模块可能是本文大多数读者感兴趣的主要方面。 一些更有趣的作家在写这篇文章的时候(请在docutils的网站仍处于实验性“沙箱”区域相关信息 ),但原则是在任何情况下是相同的。 writer模块应定义一个继承自docutils.writers.Writer
的Writer
类。 这个Writer
类定义了一些设置,但主要定义了.translate()
方法,可能类似于:
清单4.典型的自定义Writer.translate()方法
def translate(self):
visitor = DocBookTranslator(self.document)
self.document.walkabout(visitor)
self.output = visitor.astext()
正如您所看到的,作者依赖于一个知道如何处理每种类型节点的访问者 。 访问者通常将从docutils.nodes.NodeVisitor
继承。 对访问者进行编程非常类似于对SAX , expat , REXML或其他面向事件的XML解析器进行编程。 但是,访问者甚至更接近Python的xmllib模块的编程风格。 也就是说,访问者对于每种类型的节点都将具有.visit_FOO()
和.depart_FOO()
方法,而不是在大型.startElement()
和endElement()
方法中打开类型。 OOP的纯粹主义者可能更喜欢这种风格。 Docbook / XML编写器的一个简单示例是:
class DocBookTranslator(nodes.NodeVisitor):
[...lots of methods...]
def visit_block_quote(self, node):
self.body.append(self.starttag(node, 'blockquote'))
def depart_block_quote(self, node):
self.body.append('</blockquote>\n')
[...lots more methods...]
对自定义编写器/访问者进行编程非常简单,并且有用于Docutils / XML,HTML,PEP-HTML,PseudoXML(一种将起始标签与缩进但不包含结束标签的轻型XML),LaTeX,DocBook编写器。 / XML,PDF,OpenOffice / XML和Wiki-HTML。
面向树的处理
您可以将reStructuredText文档转换为可以以类似于DOM的方式进行操作的节点树。 以下是使用清单2中所示的reST PEP的示例。
清单5.创建一个reST节点树
>>> txt = open('pep.txt').read()
>>> def rst2tree(txt):
... import docutils.parsers.rst
... parser = docutils.parsers.rst.Parser()
... document = docutils.utils.new_document("test")
... document.settings.tab_width = 4
... document.settings.pep_references = 1
... document.settings.rfc_references = 1
... parser.parse(txt, document)
... return document
...
>>> doc = rst2tree(txt)
>>> doc.children
[<section "abstract": <title...><paragraph...><paragraph...>>,
<section "references & footnotes": <title...>
<target "frungible doodads"...><footnote "pep9 ...>]
>>> print doc.autofootnotes
[<footnote "pep9876": <paragraph...>>, <footnote: <paragraph...>>]
>>> print doc.autofootnotes[0].rawsource
PEP 9876, Let's Hope We Never Get Here
与DOM相反,需要注意的一件事是reStructuredText已经是固定的文档方言。 因此,您可以使用以其含义命名的属性来搜索节点,而不是使用通用方法来搜索匹配的节点。 .children
属性通常是分层的,但是大多数属性收集给定类型的节点。
reST节点的一种便捷方法是.pformat()
,它生成用于漂亮打印的文档树的伪XML表示,如清单6所示:
清单6. reST节点的伪XML表示形式
>>> print doc.autofootnotes[0].pformat(' ')
<footnote auto="1" id="pep9876" name="pep9876">
<paragraph>
<reference refuri="http://www.python.org/peps/pep-9876.html">
PEP 9876,
Let's Hope We Never Get Here
节点的方法,如.remove()
.copy()
.append()
和.insert()
是用于修剪和操作树很有用。
对于XML程序员而言,更理想的API可能是DOM本身。 幸运的是,此API仅需一个方法即可调用:
清单7.将reST树转换为DOM树
>>> dom = doc.asdom()
>>> foot0 = dom.getElementsByTagName('footnote')[0]
>>> print foot0.toprettyxml(' ')
<footnote auto="1" id="pep9876" name="pep9876">
<paragraph>
<reference refuri="http://www.python.org/peps/pep-9876.html">
PEP 9876
</reference>
, Let's Hope We Never Get Here
</paragraph>
</footnote>
不幸的是,在撰写本文时,还没有工具或函数将DOM树或XML文档转换回 reStructuredText。 拥有Docutils通用DTD的阅读器会特别好; 这将使您为相应的XML生成一个reST文档树。 您可以使用.astext()
节点方法将其写回为reST。 编写这样的阅读器并不难,我相信这会及时发生的(也许是我本人或我的一位读者)。
翻译自: https://www.ibm.com/developerworks/xml/library/x-matters24/index.html