Hi,
I've written a small program to extract documentation from a DBus
interface file. Currently, it spits out reST text. This works well,
but does not easily integrate into a Sphinx project.
Ideally, I'd like to be able to pack the data in a few data structures
and pass it off to autodoc. I've spent some time looking at the
autodoc source, but it is not clear how to do this. I'd appreciate
any tips.
Alternatively, I would just include the generated files in my Sphinx
master document. Unfortunately, it appears that the only way to do
that is via the toctree directive, but that expects proper reST
documents, not document snippets. Something like the C preprocessor's
#include would be useful for this.
I see a couple of workarounds for including documents. I could use a
preprocessor. Alternatively, I could generate python skeletons that
autodoc groks. I'd rather avoid investing energy in these
workarounds.
Any help would be appreciated.
Thanks,
Neal
P.S. The script follows.
import sys
import xml.parsers.expat
comment = None
interface = None
method = None
method_comment = None
args = None
output_file = None
do_debug = False
def debug(*args):
if do_debug:
print (args)
def fix_whitespace(text, indent):
"""Chop off any leading whitespace. Expand tabs. Ignore the
first line's white space. Make sure there are two \ns at the end
of the text."""
lines = text.expandtabs ().splitlines ()
if not lines:
return "\n"
min_len = None
for l in range (len (lines)):
spaces = 0
while spaces < len (lines[l]) and lines[l][spaces] in (' ', '\t'):
if lines[l][spaces] == ' ':
spaces += 1
lines[l] = "%*s%s" % (spaces, "", lines[l][spaces:])
if spaces == len (lines[l]):
lines[l] = ""
else:
if l == 0:
# Trim any leading spaces from the first line.
lines[0] = lines[0][spaces:]
elif min_len is None or spaces < min_len:
min_len = spaces
if min_len is not None and lines[0] != "":
lines[0] = "%*s%s" % (min_len, "", lines[0])
lines = "".join (["%*s%s\n" % (indent, "", line[min_len:])
for line in lines]) + "\n"
return lines
def start_element(name, attrs):
global last_comment
global interface
global method
global method_comment
global args
global output_file
name = name.lower ()
debug ('Start element:', name, attrs)
if name == 'node':
pass
elif name == 'interface':
if interface is not None:
raise ValueError ("Nested <interface>s not allowed.")
interface = attrs['name']
if output_file is not None:
output_file.close ()
output_file = open (interface + ".rst", "w")
output_file.write (interface + "\n")
output_file.write ('-' * len (interface) + "\n")
output_file.write ("\n")
output_file.write ('.. class:: %s\n\n' % (interface))
if last_comment is not None:
output_file.write (fix_whitespace (last_comment, 4))
elif name == 'method':
if interface is None:
raise ValueError ("<method>s outside of <interface>s not allowed.")
if method is not None:
raise ValueError ("Nest <method>s not allowed.")
method = attrs['name']
method_comment = (fix_whitespace (last_comment, 8)
if last_comment is not None else "")
args = []
elif name == 'arg':
if method is None:
raise ValueError ("<arg>s outside of <method>s not allowed.")
method_comment = method_comment \
+ " :param " \
+ attrs.get ('direction', 'in') + " " \
+ attrs.get ('name', '') + " " \
+ attrs.get ('type', '') + ":\n" \
+ fix_whitespace (last_comment if last_comment is not None else "",
12)
args.append (attrs.get ('name', None))
elif name == 'property':
if method:
raise ValueError ("<property>s not allowed in <method>s.")
if interface is None:
raise ValueError ("<method>s outside of <interface>s not allowed.")
output_file.write (" .. data:: " + attrs['name'] + "\n\n"
+ (fix_whitespace (last_comment, 8)
if last_comment is not None else ""))
else:
raise ValueError ("Unknown tag <" + name + ">");
last_comment = None
# Got an end of tag.
def end_element(name):
debug ('End element:', name)
global last_comment
global interface
global method
global method_comment
global output_file
name = name.lower ()
if name == 'method':
# Create the header (function (args)) and flush the comment.
if method is None:
raise ValueError ("</method>, but no <method>.")
output_file.write (" .. function:: %s (" % (method,))
for i in range (len (args)):
if i != 0:
output_file.write (", ")
if args[i] is None:
output_file.write ("arg%d" % (i,))
else:
output_file.write (args[i])
output_file.write (")\n"
+ "\n"
+ method_comment + "\n")
method = None
method_comment = None
elif name == 'interface':
# End of an interface.
if interface is None:
raise ValueError ("</interface>, but no <interface>.")
if method is not None:
raise ValueError ("</interface>, but no in a <method>.")
interface = None
elif name in ['node', 'arg', 'property']:
# Nothing to do.
pass
else:
raise ValueError ("</%s> unexpected.", name)
last_comment = None
def comment(data):
global last_comment
# If we concatenate multiple comments, they might have different
# spacing. Clean it up now.
fix_whitespace (data, 0)
if last_comment is None:
# First comment.
last_comment = data
else:
# Append it to the previous comment.
last_comment = last_comment + "\n\n" + data
for file in sys.argv[1:]:
contents = open (file, "r").read ()
p = xml.parsers.expat.ParserCreate()
p.StartElementHandler = start_element
p.EndElementHandler = end_element
p.CommentHandler = comment
p.Parse(contents, 1)
if output_file is not None:
output_file.close ()
--
You received this message because you are subscribed to the Google Groups
"sphinx-dev" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to
[email protected].
For more options, visit this group at
http://groups.google.com/group/sphinx-dev?hl=en.