#!/usr/bin/python # Convert the reStructuredText docs to LaTeX for use in Python docs # This script is a hacked version taken from the Optik SVN repository. import sys, os import re import rfc822 from distutils.dep_util import newer_group, newer from docutils.core import Publisher from docutils.readers.standalone import Reader as StandaloneReader from docutils.transforms import Transform from docutils.writers.latex2e import Writer as LaTeXWriter, LaTeXTranslator from docutils import nodes class OptikReader(StandaloneReader): #default_transforms = (StandaloneReader.default_transforms + # (ReplacementTransform,)) pass # python 2.3 if not hasattr(__builtins__,"set"): import sets set = sets.Set if not hasattr(__builtins__,"sorted"): def sorted(list): if hasattr(list,"sort"): return list.sort() # maybe it is sorted return list from markup import codemarkup missing = set() class PyLaTeXWriter(LaTeXWriter): def __init__(self): LaTeXWriter.__init__(self) self.translator_class = PyLaTeXTranslator class PyLaTeXTranslator(LaTeXTranslator): remap_title = { } roman = (None,None,"ii","iii","iv","v") refuri_override = { "reference" : "reference-guide", "callbacks" : "option-callbacks", } def __init__(self, document): LaTeXTranslator.__init__(self, document) self.label_prefix = "" self.docinfo = {} self.head_prefix = [] self.head = [] self.body_prefix = [] self.in_title = False self.in_anydesc = False # _title is different if it is a funcdesc self.admonition_stack = [] # Disable a bunch of methods from the base class. empty_method = lambda self: None for nodetype in ('field_argument', 'field_body', 'field_list', 'field_name'): setattr(self, 'visit_' + nodetype, empty_method) setattr(self, 'depart_' + nodetype, empty_method) self.head_prefix = [] # definitions must be guarded if multiple modules are included self.definitions = [ "\\ifx\\locallinewidth\\undefined\\newlength{\\locallinewidth}\\fi\n" "\\setlength{\\locallinewidth}{\\linewidth}\n" ] def astext(self): return ''.join(self.definitions + self.head_prefix + self.head + self.body_prefix + self.body + self.body_suffix) def set_label_prefix(self, text): self.label_prefix = text.replace(" ","-") def generate_section_label(self, title): title = title.lower() title = re.sub(r'\([^\)]*\)', '', title) title = re.sub(r'[^\w\s\-]', '', title) title = re.sub(r'\b(the|an?|and|your|are)\b', '', title) title = re.sub(r'(example \d+).*', r'\1', title) return self.label_prefix + "-" + "-".join(title.split()) def visit_document(self, node): pass def depart_document(self, node): pass def visit_docinfo(self, node): pass def depart_docinfo(self, node): # module and summary are mandatory self.body.append( "\\section{\\module{%(module)s} --- %(summary)s}\n" % self.docinfo ) if self.docinfo.has_key("moduletype"): self.body.append( "\\declaremodule{%(moduletype)s}{%(module)s}\n" % self.docinfo ) if self.docinfo.has_key("moduleauthor"): self.body.append( "\\moduleauthor{%(moduleauthor)s}{%(moduleauthoremail)s}\n" % self.docinfo ) if self.docinfo.has_key("synopsis"): self.body.append( "\\modulesynopsis{%(synopsis)s}\n" % self.docinfo ) if self.docinfo.has_key("release"): self.body.append( "\\release{%(release)s}\n" % self.docinfo ) if self.docinfo.has_key("shortversion"): self.body.append( "\\setshortversion{%(shortversion)s}\n" % self.docinfo ) if self.docinfo.has_key("sectionauthor"): self.body.append( "\\sectionauthor{%(sectionauthor)s}{%(sectionauthoremail)s}\n" % self.docinfo ) if self.docinfo.has_key("versionadded"): self.body.append( "\\versionadded{%(versionadded)s}\n" % self.docinfo ) def visit_docinfo_item(self, node, name): if name == "author": (ename, email) = rfc822.parseaddr(node.astext()) self.docinfo["moduleauthor"] = ename self.docinfo["moduleauthoremail"] = email raise nodes.SkipNode def depart_docinfo_item(self, node): pass def visit_field(self, node): if isinstance(node.parent, nodes.docinfo): name = node[0].astext().lower().replace(" ","") if name == "moduleauthor": (ename, email) = rfc822.parseaddr(node[1].astext()) self.docinfo["moduleauthor"] = ename self.docinfo["moduleauthoremail"] = email elif name in ("author", "sectionauthor") : (ename, email) = rfc822.parseaddr(node[1].astext()) self.docinfo["sectionauthor"] = ename self.docinfo["sectionauthoremail"] = email else: if name == "module": self.set_label_prefix(node[1].astext()) self.docinfo[name] = node[1].astext() raise nodes.SkipNode _quoted_string_re = re.compile(r'\"[^\"]*\"') _short_opt_string_re = re.compile(r'-[a-zA-Z]') _long_opt_string_re = re.compile(r'--[a-zA-Z-]+') _identifier_re = re.compile(r'[a-zA-Z_][a-zA-Z_0-9]*' r'(\.[a-zA-Z_][a-zA-Z_0-9]*)*' r'(\(\))?$') def visit_literal(self, node): assert isinstance(node[0], nodes.Text) text = node[0].data if self.in_title: cmd = None elif self._quoted_string_re.match(text): cmd = 'code' elif self._short_opt_string_re.match(text): cmd = 'programopt' elif self._long_opt_string_re.match(text): cmd = 'longprogramopt' text = text[2:] elif self._identifier_re.match(text): cmd = codemarkup.get(text) if cmd is None: ## print "warning: unrecognized code word %r" % text missing.add(text) cmd = 'code' else: cmd = 'code' self.literal = 1 node[0].data = text if cmd is not None: self.body.append('\\%s{' % cmd) # use definition lists for special environments # # definition_list # defintion_list_item # term # classifier # definition # paragraph ? def visit_definition_list(self, node): pass def depart_definition_list(self, node): pass def visit_definition_list_item(self, node): self._dl_term = [] def depart_definition_list_item(self, node): try: self.body.append(self.context.pop()) except: self.body.append("% WARN definition list without classifier\n") def visit_term(self, node): self._dl_term.append(node.astext()) raise nodes.SkipNode def depart_term(self, node): pass def visit_classifier(self, node): # TODO here it should be decided if it is latex or python classifier = node.astext() if classifier in ('datadesc', 'datadescni', 'excdesc', 'classdesc*', 'csimplemacrodesc', 'ctypedesc', 'memberdesc', 'memberdescni', 'cvardesc', 'excclassdesc', 'funcdesc', 'funcdescni', 'methoddesc', 'methoddescni', 'cmemberdesc', 'classdesc', 'cfuncdesc'): pass else: classifier = 'datadescni' self.body.append('\n\\begin{%s}' % classifier) self.in_anydesc = classifier self.body.append(self.anydesc_title(self._dl_term.pop())) self.context.append('\\end{%s}\n' % classifier) self.in_anydesc = None raise nodes.SkipNode def depart_classifier(self, node): pass def visit_definition(self, node): if len(self._dl_term)>0: # no classifier, fake it (maybe make a plain latex description). classifier = 'datadescni' self.body.append('\n\\begin{%s}' % classifier) self.in_anydesc = classifier self.body.append(self.anydesc_title(self._dl_term.pop())) self.context.append('\\end{%s}\n' % classifier) self.in_anydesc = None def depart_definition(self, node): pass def depart_literal(self, node): if not self.in_title: self.body.append('}') self.literal = 0 def visit_literal_block(self, node): self.body.append("\\begin{verbatim}\n") self.verbatim = 1 def depart_literal_block(self, node): self.verbatim = 0 self.body.append("\n\\end{verbatim}\n") def anydesc_title(self, title): """Returns the title for xxxdesc environments.""" def markup_optional_parameters(s): return s.replace('[','\\optional{').replace(']','}') def with_params(s): return markup_optional_parameters( '{%s}' % s.replace('(','}{').replace(')','')) def split_tag_or_typename(s, braces): # "name", "tag name", "name(params)", "type name(params)" param_pos = s.find("(") blank_pos = s.find(" ") if ((blank_pos>0 and param_pos<0) or (blank_pos>0 and blank_pos