0

I've written a custom ReST directive that takes input like this:

.. foo::
    abcdef
    ghijkl
    mnopqr

    a = apple; apple.png
    b = banana; banana.png
    (etc.)

And outputs a table. The table is shaped like the block of letters above, each letter represents an image. The next few lines associate an image to each letter.

This is the section of the directive that does the output:

tbody = nodes.tbody()
for line in lines[:3]:
    row = []
    for c in line:
        if c not in ingredients:
            continue
        name, image = ingredients[c]
        row.append(nodes.image(uri=IMAGE_URL.format(image)))
    row_entries = [nodes.entry([n]) for n in row]
    row = nodes.row()
    row.extend(row_entries)
    tbody.append(row)

table = nodes.table()
tgroup = nodes.tgroup()
table.append(tgroup)
tgroup.append(tbody)
return [nodes.literal_block(text='\n'.join(self.content))] + table

This "works", in that it returns okay. But later, when the writer tries to output what my directive has returned, it causes a traceback:

Traceback (most recent call last):
  File "./rst2html.py", line 24, in <module>
    publish_cmdline(writer_name='html', description=description)
  File "/usr/lib64/python3.2/site-packages/docutils/core.py", line 352, in publish_cmdline
    config_section=config_section, enable_exit_status=enable_exit_status)
  File "/usr/lib64/python3.2/site-packages/docutils/core.py", line 219, in publish
    output = self.writer.write(self.document, self.destination)
  File "/usr/lib64/python3.2/site-packages/docutils/writers/__init__.py", line 80, in write
    self.translate()
  File "/usr/lib64/python3.2/site-packages/docutils/writers/html4css1/__init__.py", line 173, in translate
    self.document.walkabout(visitor)
  File "/usr/lib64/python3.2/site-packages/docutils/nodes.py", line 174, in walkabout
    if child.walkabout(visitor):
  File "/usr/lib64/python3.2/site-packages/docutils/nodes.py", line 174, in walkabout
    if child.walkabout(visitor):
  File "/usr/lib64/python3.2/site-packages/docutils/nodes.py", line 174, in walkabout
    if child.walkabout(visitor):
  File "/usr/lib64/python3.2/site-packages/docutils/nodes.py", line 174, in walkabout
    if child.walkabout(visitor):
  File "/usr/lib64/python3.2/site-packages/docutils/nodes.py", line 174, in walkabout
    if child.walkabout(visitor):
  File "/usr/lib64/python3.2/site-packages/docutils/nodes.py", line 174, in walkabout
    if child.walkabout(visitor):
  File "/usr/lib64/python3.2/site-packages/docutils/nodes.py", line 166, in walkabout
    visitor.dispatch_visit(self)
  File "/usr/lib64/python3.2/site-packages/docutils/nodes.py", line 1627, in dispatch_visit
    return method(node)
  File "/usr/lib64/python3.2/site-packages/docutils/writers/html4css1/__init__.py", line 792, in visit_entry
    if node.parent.parent.parent.stubs[node.parent.column]:
IndexError: list index out of range

This would appear to be a bug in docutils¹; I'm guess that the table elements I'm returning are not quite "well-formed", at least to the writer. However, there doesn't seem to be any documentation on this. (If there is, please, point me to it!)

Does anyone know what about the table node I'm returning the HTML writer dislikes, and what I should change to fix it?

¹Even if my output isn't good, a better error than "List index out of range" should get sent back to me.

Thanatos
  • 42,585
  • 14
  • 91
  • 146

1 Answers1

1

I made two mistakes:

  1. It seems that tables really must have colgroups.

    for _ in range(3):
        colspec = nodes.colspec(colwidth=1)
        tgroup.append(colspec)
    

    That fixed the immediate problem of the traceback. The final structure looked something like this:

     * table
       * tgroup
         * one colspec for each column.
         * tbody
           * one row for each row
             * one entry for each column. (These are the cells.)
               * The optional content for the cell. You can pass in an empty
                 entry, the HTML writer will output an &nbsp;'d cell.
    

    There's also a thead, for headers; works just like tbody. I had been using (and ended up figuring this out from) docutils.parsers.rst.directives.tables, the function build_table_from_list was quite helpful.

  2. I needed to tweak the return a bit:

    return [nodes.literal_block(text='\n'.join(self.content))] + table
    

    That should be [table], not table:

    return [nodes.literal_block(text='\n'.join(self.content))] + [table]
    

    Amusingly, the writer accepts that error. You'll get a table, minus the surrounding <table> tags.

Note that colgroup's colwidth is a ratio: Three columns of colwidth "1" get output as three columns of width "33%".

Thanatos
  • 42,585
  • 14
  • 91
  • 146