2012-05-26 26 views
6

Decir que tengo la siguiente entrada de ocio:Extracto lista de campos de reStructuredText

Some text ... 

:foo: bar 

Some text ... 

Lo que me gustaría terminar con es un diccionario de esta manera:

{"foo": "bar"} 

Traté de usar esto:

tree = docutils.core.publish_parts(text) 

lo hace analizar la lista de campos, pero terminan con un poco de pseudo XML en tree["whole"]?:

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

Desde el tree dict no contiene ninguna otra información útil y que es sólo una cadena, no estoy seguro de cómo analizar la lista de campos del documento de descanso. ¿Como podría hacerlo?

Respuesta

7

Puede intentar utilizar algo como el siguiente código. En lugar de usar el método publish_parts, he usado publish_doctree para obtener la representación pseudo-XML de su documento. Luego, he convertido a un DOM XML para extraer todos los elementos field. Luego obtengo los primeros elementos field_name y field_body de cada elemento field.

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'} 

El módulo xml.dom no es el más fácil de trabajar (¿por qué tengo que usar .firstChild.nodeValue en lugar de sólo .nodeValue por ejemplo), por lo que es posible que desee utilizar el módulo xml.etree.ElementTree, que me parece mucho más fácil trabajar con. Si usa lxml, también puede usar la notación XPATH para buscar todos los elementos field, field_name y field_body.

+0

¡Gracias, eso se parece a lo que estoy buscando! –

0

Tengo una solución alternativa que me parece menos pesada, pero tal vez más frágil. Después de revisar la implementación de la clase de nodo https://sourceforge.net/p/docutils/code/HEAD/tree/trunk/docutils/docutils/nodes.py, verá que es compatible con un método de paseo que puede utilizarse para extraer los datos deseados sin tener que crear dos representaciones xml diferentes de sus datos. Esto es lo que estoy usando ahora, en mi código prototipo muy:

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

y luego

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) 
0

aquí es mi ElementTree aplicación:

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())} 

Uso

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