TASKPM/doc/tools/doctool
Adrian Perez 179f430435 Fix doctool to work with docutils 0.6
New versions of docutils starting from 0.6 have introduced some changes in
the LaTeX translator which made doctool fail. This patch adds a workaround
for those new versions; for older docutils versions things should keep
working normally.
2010-07-19 17:35:47 +02:00

295 lines
8.2 KiB
Python
Executable file

#! /usr/bin/env python
import sys, shlex, subprocess
from optparse import OptionParser, OptionGroup
from docutils.writers import latex2e
try:
import locale
locale.setlocale(locale.LC_ALL, '')
except:
pass
class AttributedDict(object):
def __init__(self):
object.__setattr__(self, "_items", dict())
def __getattr__(self, key):
return self._items.get(key)
def __delattr__(self, key):
if key in self._items:
del self._items[key]
else:
object.__delattr__(self, key)
def __setattr__(self, key, val):
if hasattr(self, key):
object.__setattr__(self, key, val)
else:
self._items[key] = val
parser = OptionParser(usage="Usage: %prog <command> [options]")
OPT = AttributedDict()
OPT.common = OptionGroup(parser, "Common options")
OPT.docinfo = OptionGroup(parser, "Documentation information")
OPT.docinfo.add_option("-i", "--info", dest="docinfo",
default=None, metavar="FILE",
help="Read project information from FILE")
def optparser(*groups):
parser.add_option_group(OPT.common)
[parser.add_option_group(g) for g in groups]
return parser
class DupRefsRemover(object):
def __init__(self, out):
self.out = out
self.ref = dict()
def handle_line(self, line):
if line.startswith(".. _") and ":" in line:
r = map(lambda x: x.strip(), line[4:].split(":", 1))
if r[0] in self.ref:
# Do some sanity check
if self.ref[r[0]] != r[1]:
raise ValueError("Reference '%s' mismatch (%s - %s)"
% (r[0], self.ref[r[0]], r[1]))
else:
# Add reference and print it out
self.ref[r[0]] = r[1]
self.out.write(line)
else:
self.out.write(line)
class ShellEscapeRunner(object):
def __init__(self, out):
self.out = out
def _expand(self, cmdline):
cmd = shlex.split(cmdline, False)
pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE)
return "\n ".join(pipe.communicate()[0].rstrip().splitlines())
def handle_line(self, line):
index = line.find("!!")
if index < 0:
self.out.write(line)
return
self.out.write(line[:index].replace("\\!", "!"))
cmd = line[index+2:]
end = cmd.find("!!")
if end > index:
self.out.write(self._expand(cmd[:end].replace("\\!", "!")))
self.out.write(cmd[end+2:].replace("\\!", "!"))
else:
self.out.write(self._expand(cmd.replace("\\!", "!")))
self.out.write("\n")
LATEX_SETTINGS = {
"documentclass" : "scrbook",
"documentoptions" : "11pt,oneside,a4paper",
"table_style" : "booktabs",
"use_latex_footnotes": 1,
"use_latex_citations": 1,
"use_latex_toc" : 1,
"use_latex_docinfo" : 0,
"use_latex_abstract" : 1,
}
EBOOK_SETTINGS = dict(LATEX_SETTINGS)
EBOOK_SETTINGS["documentoptions"] = "14pt,oneside,b5paper"
HTML_SETTINGS = {
"field_name_limit" : 20,
"cloak_email_addresses": 1,
}
# Those are two mocks needed to properly reuse the LaTeX code generator, but
# avoiding generation of all the cruft from the preamble and the postamble,
# and only the middle part.
#
class LaTeXStandaloneTranslator(latex2e.LaTeXTranslator):
def __init__(self, document):
self._class = document.settings.documentclass
self._typearea = ""
latex2e.LaTeXTranslator.__init__(self, document)
# XXX We rely on DocumentClass having a document_class attribute and
# on it not changing the attribute once initialization was done
#
if self.d_class.document_class == "igaliabk":
self.d_class.document_class = "book"
try:
del self.head_prefix[self.head_prefix.index(self.typearea)]
except AttributeError:
pass
def __get_typearea(self):
if self._class == "igaliabk" or self._class.startswith("scr"):
value = ""
else:
value = self._typearea
return value
def __set_typearea(self, value):
self._typearea = value
typarea = property(__get_typearea, __set_typearea)
class LaTeXInsertTranslator(LaTeXStandaloneTranslator):
def astext(self):
return "".join(self.body)
class LaTeXCustomWriter(latex2e.Writer):
def __init__(self, translator_class):
latex2e.Writer.__init__(self)
self.translator_class = translator_class
class DocTool(object):
def main(self, argv=None):
if argv is None:
argv = sys.argv
if len(argv) < 2:
raise SystemExit("Insufficient number of arguments")
method = getattr(self, "cmd_" + argv[1], self.no_command)
method(*argv[1:])
def cmd_rstinsert(self, cmd, *args):
from docutils.core import publish_cmdline, default_description
description = (
"Generates LaTeX portions to be included in documents " +
"from standalone reStructuredText sourtces " +
default_description)
publish_cmdline(writer=LaTeXCustomWriter(LaTeXInsertTranslator),
description=description,
argv=list(args)
)
def cmd_rst2latex(self, cmd, *args):
from docutils.core import publish_cmdline, default_description
description = (
'Generates LaTeX documents from RST sources. ' +
default_description)
publish_cmdline(writer=LaTeXCustomWriter(LaTeXStandaloneTranslator),
settings_overrides=LATEX_SETTINGS,
description=description,
argv=list(args),
)
def cmd_rst2ebook(self, cmd, *args):
from docutils.core import publish_cmdline, default_description
description = (
'Generates LaTeX documents from RST sources. ' +
default_description)
publish_cmdline(writer=LaTeXCustomWriter(LaTeXStandaloneTranslator),
settings_overrides=EBOOK_SETTINGS,
description=description,
argv=list(args),
)
def cmd_rst2html(self, cmd, *args):
from docutils.core import publish_cmdline, default_description
description = (
'Generates HTML documents from RST sources. ' +
default_description)
publish_cmdline(writer_name="html",
settings_overrides=HTML_SETTINGS,
description=description,
argv=list(args),
)
def cmd_toplevel(self, cmd, *args):
opts, args = optparser(OPT.docinfo).parse_args(list(args))
if opts.docinfo:
f = file(opts.docinfo, "rU")
e = ShellEscapeRunner(sys.stdout)
map(e.handle_line, f.readlines())
f.close()
print
print ".. contents::"
print
out = DupRefsRemover(sys.stdout)
for name in args:
if not name.endswith(".rst"):
continue
f = file(name, "rU")
map(out.handle_line,
filter(lambda l: ".. contents::" not in l,
f.readlines())
)
f.close()
print
def cmd_htmlindex(self, cmd, *args):
opts, args = optparser(OPT.docinfo).parse_args(list(args))
if opts.docinfo:
f = file(opts.docinfo, "rU")
e = ShellEscapeRunner(sys.stdout)
map(e.handle_line, f.readlines())
f.close()
print
for name in args:
# Skip non-RST inputs
if not name.endswith(".rst"):
continue
f = file(name, "rU")
line = f.readline().strip()
f.close()
print "#. `%s <%s.html>`__" % (line, name[:-4])
def cmd_help(self, cmd, *args):
if args:
self.main([args[0], args[0], "--help"])
else:
print "Available commands:"
for k in dir(self):
if k.startswith("cmd_"):
print " ", k[4:].replace("_", "-")
cmd_commands = cmd_help
def no_command(self, cmd, *args):
raise SystemExit("No such command '%s'" % cmd)
if __name__ == "__main__":
DocTool().main()