从reStructuredText中提取字段列表

时间:2012-05-26 13:18:55

标签: python restructuredtext docutils

说我有以下reST输入:

Some text ...

:foo: bar

Some text ...

我最终想要的是这样一个字典:

{"foo": "bar"}

我试着用这个:

tree = docutils.core.publish_parts(text)

它确实解析了字段列表,但我在tree["whole"]?中得到了一些伪XML:

<document source="<string>">
    <docinfo>
        <field>
            <field_name>
                foo
            <field_body>
                <paragraph>
                    bar

由于tree dict不包含任何其他有用信息而且只是一个字符串,因此我不确定如何从reST文档中解析字段列表。我该怎么做?

3 个答案:

答案 0 :(得分:7)

您可以尝试使用类似以下代码的内容。而不是使用我使用publish_doctreepublish_parts方法来获取文档的伪XML表示。然后我转换为XML DOM以提取所有field元素。然后,我获得每个field_name元素的第一个field_bodyfield元素。

from docutils.core import publish_doctree

source = """Some text ...

:foo: bar

Some text ...
"""

# Parse reStructuredText input, returning the Docutils doctree as
# an `xml.dom.minidom.Document` instance.
doctree = publish_doctree(source).asdom()

# Get all field lists in the document.
fields = doctree.getElementsByTagName('field')

d = {}

for field in fields:
    # I am assuming that `getElementsByTagName` only returns one element.
    field_name = field.getElementsByTagName('field_name')[0]
    field_body = field.getElementsByTagName('field_body')[0]

    d[field_name.firstChild.nodeValue] = \
        " ".join(c.firstChild.nodeValue for c in field_body.childNodes)

print d # Prints {u'foo': u'bar'}

xml.dom模块不是最容易使用的模块(为什么我需要使用.firstChild.nodeValue而不仅仅是.nodeValue),所以您可能希望使用{ {3}}模块,我发现它更容易使用。如果您使用lxml,您还可以使用XPATH表示法查找所有fieldfield_namefield_body元素。

答案 1 :(得分:0)

我有一个替代解决方案,我觉得不那么负担,但可能更脆弱。在查看节点类https://sourceforge.net/p/docutils/code/HEAD/tree/trunk/docutils/docutils/nodes.py的实现之后,您将看到它支持walk方法,该方法可用于提取所需数据,而无需创建数据的两个不同xml表示。以下是我现在使用的原型代码:

https://github.com/h4ck3rm1k3/gcc-introspector/blob/master/peewee_adaptor.py#L33

from docutils.core import publish_doctree
import docutils.nodes

然后

def walk_docstring(prop):
    doc = prop.__doc__
    doctree = publish_doctree(doc)
    class Walker:
        def __init__(self, doc):
            self.document = doc
            self.fields = {}
        def dispatch_visit(self,x):
            if isinstance(x, docutils.nodes.field):
                field_name = x.children[0].rawsource
                field_value = x.children[1].rawsource
                self.fields[field_name]=field_value
    w = Walker(doctree)
    doctree.walk(w)
    # the collected fields I wanted
    pprint.pprint(w.fields)

答案 2 :(得分:0)

这是我的ElementTree实施:

from docutils.core import publish_doctree
from xml.etree.ElementTree import fromstring

source = """Some text ...

:foo: bar

Some text ...
"""


def gen_fields(source):
    dom = publish_doctree(source).asdom()
    tree = fromstring(dom.toxml())

    for field in tree.iter(tag='field'):
        name = next(field.iter(tag='field_name'))
        body = next(field.iter(tag='field_body'))
        yield {name.text: ''.join(body.itertext())}

用法

>>> next(gen_fields(source))
{'foo': 'bar'}