1073 lines · 36.4 KB · Python
RawDownload
#! /usr/bin/env python """Perform massive transformations on a document tree created from the LaTeXof the Python documentation, and dump the ESIS data for the transformed tree."""  import errnoimport esistoolsimport reimport sysimport xml.domimport xml.dom.minidom ELEMENT = xml.dom.Node.ELEMENT_NODEENTITY_REFERENCE = xml.dom.Node.ENTITY_REFERENCE_NODETEXT = xml.dom.Node.TEXT_NODE  class ConversionError(Exception):    pass  ewrite = sys.stderr.writetry:    # We can only do this trick on Unix (if tput is on $PATH)!    if sys.platform != "posix" or not sys.stderr.isatty():        raise ImportError    import commandsexcept ImportError:    bwrite = ewriteelse:    def bwrite(s, BOLDON=commands.getoutput("tput bold"),               BOLDOFF=commands.getoutput("tput sgr0")):        ewrite("%s%s%s" % (BOLDON, s, BOLDOFF))  PARA_ELEMENT = "para" DEBUG_PARA_FIXER = 0 if DEBUG_PARA_FIXER:    def para_msg(s):        ewrite("*** %s\n" % s)else:    def para_msg(s):        pass  def get_first_element(doc, gi):    for n in doc.childNodes:        if n.nodeName == gi:            return n def extract_first_element(doc, gi):    node = get_first_element(doc, gi)    if node is not None:        doc.removeChild(node)    return node  def get_documentElement(node):    result = None    for child in node.childNodes:        if child.nodeType == ELEMENT:            result = child    return result  def set_tagName(elem, gi):    elem.nodeName = elem.tagName = gi  def find_all_elements(doc, gi):    nodes = []    if doc.nodeName == gi:        nodes.append(doc)    for child in doc.childNodes:        if child.nodeType == ELEMENT:            if child.tagName == gi:                nodes.append(child)            for node in child.getElementsByTagName(gi):                nodes.append(node)    return nodes def find_all_child_elements(doc, gi):    nodes = []    for child in doc.childNodes:        if child.nodeName == gi:            nodes.append(child)    return nodes  def find_all_elements_from_set(doc, gi_set):    return __find_all_elements_from_set(doc, gi_set, []) def __find_all_elements_from_set(doc, gi_set, nodes):    if doc.nodeName in gi_set:        nodes.append(doc)    for child in doc.childNodes:        if child.nodeType == ELEMENT:            __find_all_elements_from_set(child, gi_set, nodes)    return nodes  def simplify(doc, fragment):    # Try to rationalize the document a bit, since these things are simply    # not valid SGML/XML documents as they stand, and need a little work.    documentclass = "document"    inputs = []    node = extract_first_element(fragment, "documentclass")    if node is not None:        documentclass = node.getAttribute("classname")    node = extract_first_element(fragment, "title")    if node is not None:        inputs.append(node)    # update the name of the root element    node = get_first_element(fragment, "document")    if node is not None:        set_tagName(node, documentclass)        # Move everything that comes before this node into this node;        # this will be the document element.        nodelist = fragment.childNodes        point = node.firstChild        while not nodelist[0].isSameNode(node):            node.insertBefore(nodelist[0], point)    while 1:        node = extract_first_element(fragment, "input")        if node is None:            break        inputs.append(node)    if inputs:        docelem = get_documentElement(fragment)        inputs.reverse()        for node in inputs:            text = doc.createTextNode("\n")            docelem.insertBefore(text, docelem.firstChild)            docelem.insertBefore(node, text)        docelem.insertBefore(doc.createTextNode("\n"), docelem.firstChild)    while fragment.firstChild and fragment.firstChild.nodeType == TEXT:        fragment.removeChild(fragment.firstChild)  def cleanup_root_text(doc):    discards = []    skip = 0    for n in doc.childNodes:        prevskip = skip        skip = 0        if n.nodeType == TEXT and not prevskip:            discards.append(n)        elif n.nodeName == "COMMENT":            skip = 1    for node in discards:        doc.removeChild(node)  DESCRIPTOR_ELEMENTS = (    "cfuncdesc", "cvardesc", "ctypedesc",    "classdesc", "memberdesc", "memberdescni", "methoddesc", "methoddescni",    "excdesc", "funcdesc", "funcdescni", "opcodedesc",    "datadesc", "datadescni",    ) def fixup_descriptors(doc, fragment):    sections = find_all_elements(fragment, "section")    for section in sections:        find_and_fix_descriptors(doc, section)  def find_and_fix_descriptors(doc, container):    children = container.childNodes    for child in children:        if child.nodeType == ELEMENT:            tagName = child.tagName            if tagName in DESCRIPTOR_ELEMENTS:                rewrite_descriptor(doc, child)            elif tagName == "subsection":                find_and_fix_descriptors(doc, child)  def rewrite_descriptor(doc, descriptor):    #    # Do these things:    #   1. Add an "index='no'" attribute to the element if the tagName    #      ends in 'ni', removing the 'ni' from the name.    #   2. Create a <signature> from the name attribute    #   2a.Create an <args> if it appears to be available.    #   3. Create additional <signature>s from <*line{,ni}> elements,    #      if found.    #   4. If a <versionadded> is found, move it to an attribute on the    #      descriptor.    #   5. Move remaining child nodes to a <description> element.    #   6. Put it back together.    #    # 1.    descname = descriptor.tagName    index = descriptor.getAttribute("name") != "no"    desctype = descname[:-4] # remove 'desc'    linename = desctype + "line"    if not index:        linename = linename + "ni"    # 2.    signature = doc.createElement("signature")    name = doc.createElement("name")    signature.appendChild(doc.createTextNode("\n    "))    signature.appendChild(name)    name.appendChild(doc.createTextNode(descriptor.getAttribute("name")))    descriptor.removeAttribute("name")    # 2a.    if descriptor.hasAttribute("var"):        if descname != "opcodedesc":            raise RuntimeError, \                  "got 'var' attribute on descriptor other than opcodedesc"        variable = descriptor.getAttribute("var")        if variable:            args = doc.createElement("args")            args.appendChild(doc.createTextNode(variable))            signature.appendChild(doc.createTextNode("\n    "))            signature.appendChild(args)        descriptor.removeAttribute("var")    newchildren = [signature]    children = descriptor.childNodes    pos = skip_leading_nodes(children)    if pos < len(children):        child = children[pos]        if child.nodeName == "args":            # move <args> to <signature>, or remove if empty:            child.parentNode.removeChild(child)            if len(child.childNodes):                signature.appendChild(doc.createTextNode("\n    "))                signature.appendChild(child)    signature.appendChild(doc.createTextNode("\n  "))    # 3, 4.    pos = skip_leading_nodes(children, pos)    while pos < len(children) \          and children[pos].nodeName in (linename, "versionadded"):        if children[pos].tagName == linename:            # this is really a supplemental signature, create <signature>            oldchild = children[pos].cloneNode(1)            try:                sig = methodline_to_signature(doc, children[pos])            except KeyError:                print oldchild.toxml()                raise            newchildren.append(sig)        else:            # <versionadded added=...>            descriptor.setAttribute(                "added", children[pos].getAttribute("version"))        pos = skip_leading_nodes(children, pos + 1)    # 5.    description = doc.createElement("description")    description.appendChild(doc.createTextNode("\n"))    newchildren.append(description)    move_children(descriptor, description, pos)    last = description.childNodes[-1]    if last.nodeType == TEXT:        last.data = last.data.rstrip() + "\n  "    # 6.    # should have nothing but whitespace and signature lines in <descriptor>;    # discard them    while descriptor.childNodes:        descriptor.removeChild(descriptor.childNodes[0])    for node in newchildren:        descriptor.appendChild(doc.createTextNode("\n  "))        descriptor.appendChild(node)    descriptor.appendChild(doc.createTextNode("\n"))  def methodline_to_signature(doc, methodline):    signature = doc.createElement("signature")    signature.appendChild(doc.createTextNode("\n    "))    name = doc.createElement("name")    name.appendChild(doc.createTextNode(methodline.getAttribute("name")))    methodline.removeAttribute("name")    signature.appendChild(name)    if len(methodline.childNodes):        args = doc.createElement("args")        signature.appendChild(doc.createTextNode("\n    "))        signature.appendChild(args)        move_children(methodline, args)    signature.appendChild(doc.createTextNode("\n  "))    return signature  def move_children(origin, dest, start=0):    children = origin.childNodes    while start < len(children):        node = children[start]        origin.removeChild(node)        dest.appendChild(node)  def handle_appendix(doc, fragment):    # must be called after simplfy() if document is multi-rooted to begin with    docelem = get_documentElement(fragment)    toplevel = docelem.tagName == "manual" and "chapter" or "section"    appendices = 0    nodes = []    for node in docelem.childNodes:        if appendices:            nodes.append(node)        elif node.nodeType == ELEMENT:            appnodes = node.getElementsByTagName("appendix")            if appnodes:                appendices = 1                parent = appnodes[0].parentNode                parent.removeChild(appnodes[0])                parent.normalize()    if nodes:        map(docelem.removeChild, nodes)        docelem.appendChild(doc.createTextNode("\n\n\n"))        back = doc.createElement("back-matter")        docelem.appendChild(back)        back.appendChild(doc.createTextNode("\n"))        while nodes and nodes[0].nodeType == TEXT \              and not nodes[0].data.strip():            del nodes[0]        map(back.appendChild, nodes)        docelem.appendChild(doc.createTextNode("\n"))  def handle_labels(doc, fragment):    for label in find_all_elements(fragment, "label"):        id = label.getAttribute("id")        if not id:            continue        parent = label.parentNode        parentTagName = parent.tagName        if parentTagName == "title":            parent.parentNode.setAttribute("id", id)        else:            parent.setAttribute("id", id)        # now, remove <label id="..."/> from parent:        parent.removeChild(label)        if parentTagName == "title":            parent.normalize()            children = parent.childNodes            if children[-1].nodeType == TEXT:                children[-1].data = children[-1].data.rstrip()  def fixup_trailing_whitespace(doc, fragment, wsmap):    queue = [fragment]    fixups = []    while queue:        node = queue[0]        del queue[0]        if wsmap.has_key(node.nodeName):            fixups.append(node)        for child in node.childNodes:            if child.nodeType == ELEMENT:                queue.append(child)     # reverse the list to process from the inside out    fixups.reverse()    for node in fixups:        node.parentNode.normalize()        lastchild = node.lastChild        before, after = wsmap[node.tagName]        if lastchild.nodeType == TEXT:            data = lastchild.data.rstrip() + before            lastchild.data = data        norm = 0        if wsmap[node.tagName]:            nextnode = node.nextSibling            if nextnode and nextnode.nodeType == TEXT:                nextnode.data = after + nextnode.data.lstrip()            else:                wsnode = doc.createTextNode(after)                node.parentNode.insertBefore(wsnode, nextnode)        # hack to get the title in place:        if node.tagName == "title" \           and node.parentNode.firstChild.nodeType == ELEMENT:            node.parentNode.insertBefore(doc.createTextNode("\n  "),                                         node.parentNode.firstChild)            node.parentNode.normalize()  def normalize(doc):    for node in doc.childNodes:        if node.nodeType == ELEMENT:            node.normalize()  def cleanup_trailing_parens(doc, element_names):    d = {}    for gi in element_names:        d[gi] = gi    rewrite_element = d.has_key    queue = [node for node in doc.childNodes if node.nodeType == ELEMENT]    while queue:        node = queue[0]        del queue[0]        if rewrite_element(node.tagName):            lastchild = node.lastChild            if lastchild and lastchild.nodeType == TEXT:                data = lastchild.data                if data.endswith("()"):                    lastchild.data = data[:-2]        else:            for child in node.childNodes:                if child.nodeType == ELEMENT:                    queue.append(child)  def contents_match(left, right):    left_children = left.childNodes    right_children = right.childNodes    if len(left_children) != len(right_children):        return 0    for l, r in map(None, left_children, right_children):        nodeType = l.nodeType        if nodeType != r.nodeType:            return 0        if nodeType == ELEMENT:            if l.tagName != r.tagName:                return 0            # should check attributes, but that's not a problem here            if not contents_match(l, r):                return 0        elif nodeType == TEXT:            if l.data != r.data:                return 0        else:            # not quite right, but good enough            return 0    return 1  def create_module_info(doc, section):    # Heavy.    node = extract_first_element(section, "modulesynopsis")    if node is None:        return    set_tagName(node, "synopsis")    lastchild = node.childNodes[-1]    if lastchild.nodeType == TEXT \       and lastchild.data[-1:] == ".":        lastchild.data = lastchild.data[:-1]    modauthor = extract_first_element(section, "moduleauthor")    if modauthor:        set_tagName(modauthor, "author")        modauthor.appendChild(doc.createTextNode(            modauthor.getAttribute("name")))        modauthor.removeAttribute("name")    platform = extract_first_element(section, "platform")    if section.tagName == "section":        modinfo_pos = 2        modinfo = doc.createElement("moduleinfo")        moddecl = extract_first_element(section, "declaremodule")        name = None        if moddecl:            modinfo.appendChild(doc.createTextNode("\n    "))            name = moddecl.attributes["name"].value            namenode = doc.createElement("name")            namenode.appendChild(doc.createTextNode(name))            modinfo.appendChild(namenode)            type = moddecl.attributes.get("type")            if type:                type = type.value                modinfo.appendChild(doc.createTextNode("\n    "))                typenode = doc.createElement("type")                typenode.appendChild(doc.createTextNode(type))                modinfo.appendChild(typenode)        versionadded = extract_first_element(section, "versionadded")        if versionadded:            modinfo.setAttribute("added", versionadded.getAttribute("version"))        title = get_first_element(section, "title")        if title:            children = title.childNodes            if len(children) >= 2 \               and children[0].nodeName == "module" \               and children[0].childNodes[0].data == name:                # this is it; morph the <title> into <short-synopsis>                first_data = children[1]                if first_data.data[:4] == " ---":                    first_data.data = first_data.data[4:].lstrip()                set_tagName(title, "short-synopsis")                if children[-1].nodeType == TEXT \                   and children[-1].data[-1:] == ".":                    children[-1].data = children[-1].data[:-1]                section.removeChild(title)                section.removeChild(section.childNodes[0])                title.removeChild(children[0])                modinfo_pos = 0            else:                ewrite("module name in title doesn't match"                       " <declaremodule/>; no <short-synopsis/>\n")        else:            ewrite("Unexpected condition: <section/> without <title/>\n")        modinfo.appendChild(doc.createTextNode("\n    "))        modinfo.appendChild(node)        if title and not contents_match(title, node):            # The short synopsis is actually different,            # and needs to be stored:            modinfo.appendChild(doc.createTextNode("\n    "))            modinfo.appendChild(title)        if modauthor:

Lines 1–500 of 1073.