diff options
Diffstat (limited to 'lib/python2.7/site-packages/Twisted-12.2.0-py2.7-linux-x86_64.egg/twisted/lore/tree.py')
-rwxr-xr-x | lib/python2.7/site-packages/Twisted-12.2.0-py2.7-linux-x86_64.egg/twisted/lore/tree.py | 1122 |
1 files changed, 0 insertions, 1122 deletions
diff --git a/lib/python2.7/site-packages/Twisted-12.2.0-py2.7-linux-x86_64.egg/twisted/lore/tree.py b/lib/python2.7/site-packages/Twisted-12.2.0-py2.7-linux-x86_64.egg/twisted/lore/tree.py deleted file mode 100755 index 5cc71aa7..00000000 --- a/lib/python2.7/site-packages/Twisted-12.2.0-py2.7-linux-x86_64.egg/twisted/lore/tree.py +++ /dev/null @@ -1,1122 +0,0 @@ -# Copyright (c) Twisted Matrix Laboratories. -# See LICENSE for details. - - -from itertools import count -import re, os, cStringIO, time, cgi, urlparse -from xml.dom import minidom as dom -from xml.sax.handler import ErrorHandler, feature_validation -from xml.dom.pulldom import SAX2DOM -from xml.sax import make_parser -from xml.sax.xmlreader import InputSource - -from twisted.python import htmlizer, text -from twisted.python.filepath import FilePath -from twisted.web import domhelpers -import process, latex, indexer, numberer, htmlbook - -# relative links to html files -def fixLinks(document, ext): - """ - Rewrite links to XHTML lore input documents so they point to lore XHTML - output documents. - - Any node with an C{href} attribute which does not contain a value starting - with C{http}, C{https}, C{ftp}, or C{mailto} and which does not have a - C{class} attribute of C{absolute} or which contains C{listing} and which - does point to an URL ending with C{html} will have that attribute value - rewritten so that the filename extension is C{ext} instead of C{html}. - - @type document: A DOM Node or Document - @param document: The input document which contains all of the content to be - presented. - - @type ext: C{str} - @param ext: The extension to use when selecting an output file name. This - replaces the extension of the input file name. - - @return: C{None} - """ - supported_schemes=['http', 'https', 'ftp', 'mailto'] - for node in domhelpers.findElementsWithAttribute(document, 'href'): - href = node.getAttribute("href") - if urlparse.urlparse(href)[0] in supported_schemes: - continue - if node.getAttribute("class") == "absolute": - continue - if node.getAttribute("class").find('listing') != -1: - continue - - # This is a relative link, so it should be munged. - if href.endswith('html') or href[:href.rfind('#')].endswith('html'): - fname, fext = os.path.splitext(href) - if '#' in fext: - fext = ext+'#'+fext.split('#', 1)[1] - else: - fext = ext - node.setAttribute("href", fname + fext) - - - -def addMtime(document, fullpath): - """ - Set the last modified time of the given document. - - @type document: A DOM Node or Document - @param document: The output template which defines the presentation of the - last modified time. - - @type fullpath: C{str} - @param fullpath: The file name from which to take the last modified time. - - @return: C{None} - """ - for node in domhelpers.findElementsWithAttribute(document, "class","mtime"): - txt = dom.Text() - txt.data = time.ctime(os.path.getmtime(fullpath)) - node.appendChild(txt) - - - -def _getAPI(node): - """ - Retrieve the fully qualified Python name represented by the given node. - - The name is represented by one or two aspects of the node: the value of the - node's first child forms the end of the name. If the node has a C{base} - attribute, that attribute's value is prepended to the node's value, with - C{.} separating the two parts. - - @rtype: C{str} - @return: The fully qualified Python name. - """ - base = "" - if node.hasAttribute("base"): - base = node.getAttribute("base") + "." - return base+node.childNodes[0].nodeValue - - - -def fixAPI(document, url): - """ - Replace API references with links to API documentation. - - @type document: A DOM Node or Document - @param document: The input document which contains all of the content to be - presented. - - @type url: C{str} - @param url: A string which will be interpolated with the fully qualified - Python name of any API reference encountered in the input document, the - result of which will be used as a link to API documentation for that name - in the output document. - - @return: C{None} - """ - # API references - for node in domhelpers.findElementsWithAttribute(document, "class", "API"): - fullname = _getAPI(node) - anchor = dom.Element('a') - anchor.setAttribute('href', url % (fullname,)) - anchor.setAttribute('title', fullname) - while node.childNodes: - child = node.childNodes[0] - node.removeChild(child) - anchor.appendChild(child) - node.appendChild(anchor) - if node.hasAttribute('base'): - node.removeAttribute('base') - - - -def fontifyPython(document): - """ - Syntax color any node in the given document which contains a Python source - listing. - - @type document: A DOM Node or Document - @param document: The input document which contains all of the content to be - presented. - - @return: C{None} - """ - def matcher(node): - return (node.nodeName == 'pre' and node.hasAttribute('class') and - node.getAttribute('class') == 'python') - for node in domhelpers.findElements(document, matcher): - fontifyPythonNode(node) - - - -def fontifyPythonNode(node): - """ - Syntax color the given node containing Python source code. - - The node must have a parent. - - @return: C{None} - """ - oldio = cStringIO.StringIO() - latex.getLatexText(node, oldio.write, - entities={'lt': '<', 'gt': '>', 'amp': '&'}) - oldio = cStringIO.StringIO(oldio.getvalue().strip()+'\n') - howManyLines = len(oldio.getvalue().splitlines()) - newio = cStringIO.StringIO() - htmlizer.filter(oldio, newio, writer=htmlizer.SmallerHTMLWriter) - lineLabels = _makeLineNumbers(howManyLines) - newel = dom.parseString(newio.getvalue()).documentElement - newel.setAttribute("class", "python") - node.parentNode.replaceChild(newel, node) - newel.insertBefore(lineLabels, newel.firstChild) - - - -def addPyListings(document, dir): - """ - Insert Python source listings into the given document from files in the - given directory based on C{py-listing} nodes. - - Any node in C{document} with a C{class} attribute set to C{py-listing} will - have source lines taken from the file named in that node's C{href} - attribute (searched for in C{dir}) inserted in place of that node. - - If a node has a C{skipLines} attribute, its value will be parsed as an - integer and that many lines will be skipped at the beginning of the source - file. - - @type document: A DOM Node or Document - @param document: The document within which to make listing replacements. - - @type dir: C{str} - @param dir: The directory in which to find source files containing the - referenced Python listings. - - @return: C{None} - """ - for node in domhelpers.findElementsWithAttribute(document, "class", - "py-listing"): - filename = node.getAttribute("href") - outfile = cStringIO.StringIO() - lines = map(str.rstrip, open(os.path.join(dir, filename)).readlines()) - - skip = node.getAttribute('skipLines') or 0 - lines = lines[int(skip):] - howManyLines = len(lines) - data = '\n'.join(lines) - - data = cStringIO.StringIO(text.removeLeadingTrailingBlanks(data)) - htmlizer.filter(data, outfile, writer=htmlizer.SmallerHTMLWriter) - sourceNode = dom.parseString(outfile.getvalue()).documentElement - sourceNode.insertBefore(_makeLineNumbers(howManyLines), sourceNode.firstChild) - _replaceWithListing(node, sourceNode.toxml(), filename, "py-listing") - - - -def _makeLineNumbers(howMany): - """ - Return an element which will render line numbers for a source listing. - - @param howMany: The number of lines in the source listing. - @type howMany: C{int} - - @return: An L{dom.Element} which can be added to the document before - the source listing to add line numbers to it. - """ - # Figure out how many digits wide the widest line number label will be. - width = len(str(howMany)) - - # Render all the line labels with appropriate padding - labels = ['%*d' % (width, i) for i in range(1, howMany + 1)] - - # Create a p element with the right style containing the labels - p = dom.Element('p') - p.setAttribute('class', 'py-linenumber') - t = dom.Text() - t.data = '\n'.join(labels) + '\n' - p.appendChild(t) - return p - - -def _replaceWithListing(node, val, filename, class_): - captionTitle = domhelpers.getNodeText(node) - if captionTitle == os.path.basename(filename): - captionTitle = 'Source listing' - text = ('<div class="%s">%s<div class="caption">%s - ' - '<a href="%s"><span class="filename">%s</span></a></div></div>' % - (class_, val, captionTitle, filename, filename)) - newnode = dom.parseString(text).documentElement - node.parentNode.replaceChild(newnode, node) - - - -def addHTMLListings(document, dir): - """ - Insert HTML source listings into the given document from files in the given - directory based on C{html-listing} nodes. - - Any node in C{document} with a C{class} attribute set to C{html-listing} - will have source lines taken from the file named in that node's C{href} - attribute (searched for in C{dir}) inserted in place of that node. - - @type document: A DOM Node or Document - @param document: The document within which to make listing replacements. - - @type dir: C{str} - @param dir: The directory in which to find source files containing the - referenced HTML listings. - - @return: C{None} - """ - for node in domhelpers.findElementsWithAttribute(document, "class", - "html-listing"): - filename = node.getAttribute("href") - val = ('<pre class="htmlsource">\n%s</pre>' % - cgi.escape(open(os.path.join(dir, filename)).read())) - _replaceWithListing(node, val, filename, "html-listing") - - - -def addPlainListings(document, dir): - """ - Insert text listings into the given document from files in the given - directory based on C{listing} nodes. - - Any node in C{document} with a C{class} attribute set to C{listing} will - have source lines taken from the file named in that node's C{href} - attribute (searched for in C{dir}) inserted in place of that node. - - @type document: A DOM Node or Document - @param document: The document within which to make listing replacements. - - @type dir: C{str} - @param dir: The directory in which to find source files containing the - referenced text listings. - - @return: C{None} - """ - for node in domhelpers.findElementsWithAttribute(document, "class", - "listing"): - filename = node.getAttribute("href") - val = ('<pre>\n%s</pre>' % - cgi.escape(open(os.path.join(dir, filename)).read())) - _replaceWithListing(node, val, filename, "listing") - - - -def getHeaders(document): - """ - Return all H2 and H3 nodes in the given document. - - @type document: A DOM Node or Document - - @rtype: C{list} - """ - return domhelpers.findElements( - document, - lambda n, m=re.compile('h[23]$').match: m(n.nodeName)) - - - -def generateToC(document): - """ - Create a table of contents for the given document. - - @type document: A DOM Node or Document - - @rtype: A DOM Node - @return: a Node containing a table of contents based on the headers of the - given document. - """ - subHeaders = None - headers = [] - for element in getHeaders(document): - if element.tagName == 'h2': - subHeaders = [] - headers.append((element, subHeaders)) - elif subHeaders is None: - raise ValueError( - "No H3 element is allowed until after an H2 element") - else: - subHeaders.append(element) - - auto = count().next - - def addItem(headerElement, parent): - anchor = dom.Element('a') - name = 'auto%d' % (auto(),) - anchor.setAttribute('href', '#' + name) - text = dom.Text() - text.data = domhelpers.getNodeText(headerElement) - anchor.appendChild(text) - headerNameItem = dom.Element('li') - headerNameItem.appendChild(anchor) - parent.appendChild(headerNameItem) - anchor = dom.Element('a') - anchor.setAttribute('name', name) - headerElement.appendChild(anchor) - - toc = dom.Element('ol') - for headerElement, subHeaders in headers: - addItem(headerElement, toc) - if subHeaders: - subtoc = dom.Element('ul') - toc.appendChild(subtoc) - for subHeaderElement in subHeaders: - addItem(subHeaderElement, subtoc) - - return toc - - - -def putInToC(document, toc): - """ - Insert the given table of contents into the given document. - - The node with C{class} attribute set to C{toc} has its children replaced - with C{toc}. - - @type document: A DOM Node or Document - @type toc: A DOM Node - """ - tocOrig = domhelpers.findElementsWithAttribute(document, 'class', 'toc') - if tocOrig: - tocOrig= tocOrig[0] - tocOrig.childNodes = [toc] - - - -def removeH1(document): - """ - Replace all C{h1} nodes in the given document with empty C{span} nodes. - - C{h1} nodes mark up document sections and the output template is given an - opportunity to present this information in a different way. - - @type document: A DOM Node or Document - @param document: The input document which contains all of the content to be - presented. - - @return: C{None} - """ - h1 = domhelpers.findNodesNamed(document, 'h1') - empty = dom.Element('span') - for node in h1: - node.parentNode.replaceChild(empty, node) - - - -def footnotes(document): - """ - Find footnotes in the given document, move them to the end of the body, and - generate links to them. - - A footnote is any node with a C{class} attribute set to C{footnote}. - Footnote links are generated as superscript. Footnotes are collected in a - C{ol} node at the end of the document. - - @type document: A DOM Node or Document - @param document: The input document which contains all of the content to be - presented. - - @return: C{None} - """ - footnotes = domhelpers.findElementsWithAttribute(document, "class", - "footnote") - if not footnotes: - return - footnoteElement = dom.Element('ol') - id = 1 - for footnote in footnotes: - href = dom.parseString('<a href="#footnote-%(id)d">' - '<super>%(id)d</super></a>' - % vars()).documentElement - text = ' '.join(domhelpers.getNodeText(footnote).split()) - href.setAttribute('title', text) - target = dom.Element('a') - target.setAttribute('name', 'footnote-%d' % (id,)) - target.childNodes = [footnote] - footnoteContent = dom.Element('li') - footnoteContent.childNodes = [target] - footnoteElement.childNodes.append(footnoteContent) - footnote.parentNode.replaceChild(href, footnote) - id += 1 - body = domhelpers.findNodesNamed(document, "body")[0] - header = dom.parseString('<h2>Footnotes</h2>').documentElement - body.childNodes.append(header) - body.childNodes.append(footnoteElement) - - - -def notes(document): - """ - Find notes in the given document and mark them up as such. - - A note is any node with a C{class} attribute set to C{note}. - - (I think this is a very stupid feature. When I found it I actually - exclaimed out loud. -exarkun) - - @type document: A DOM Node or Document - @param document: The input document which contains all of the content to be - presented. - - @return: C{None} - """ - notes = domhelpers.findElementsWithAttribute(document, "class", "note") - notePrefix = dom.parseString('<strong>Note: </strong>').documentElement - for note in notes: - note.childNodes.insert(0, notePrefix) - - - -def findNodeJustBefore(target, nodes): - """ - Find the last Element which is a sibling of C{target} and is in C{nodes}. - - @param target: A node the previous sibling of which to return. - @param nodes: A list of nodes which might be the right node. - - @return: The previous sibling of C{target}. - """ - while target is not None: - node = target.previousSibling - while node is not None: - if node in nodes: - return node - node = node.previousSibling - target = target.parentNode - raise RuntimeError("Oops") - - - -def getFirstAncestorWithSectionHeader(entry): - """ - Visit the ancestors of C{entry} until one with at least one C{h2} child - node is found, then return all of that node's C{h2} child nodes. - - @type entry: A DOM Node - @param entry: The node from which to begin traversal. This node itself is - excluded from consideration. - - @rtype: C{list} of DOM Nodes - @return: All C{h2} nodes of the ultimately selected parent node. - """ - for a in domhelpers.getParents(entry)[1:]: - headers = domhelpers.findNodesNamed(a, "h2") - if len(headers) > 0: - return headers - return [] - - - -def getSectionNumber(header): - """ - Retrieve the section number of the given node. - - This is probably intended to interact in a rather specific way with - L{numberDocument}. - - @type header: A DOM Node or L{None} - @param header: The section from which to extract a number. The section - number is the value of this node's first child. - - @return: C{None} or a C{str} giving the section number. - """ - if not header: - return None - return domhelpers.gatherTextNodes(header.childNodes[0]) - - - -def getSectionReference(entry): - """ - Find the section number which contains the given node. - - This function looks at the given node's ancestry until it finds a node - which defines a section, then returns that section's number. - - @type entry: A DOM Node - @param entry: The node for which to determine the section. - - @rtype: C{str} - @return: The section number, as returned by C{getSectionNumber} of the - first ancestor of C{entry} which defines a section, as determined by - L{getFirstAncestorWithSectionHeader}. - """ - headers = getFirstAncestorWithSectionHeader(entry) - myHeader = findNodeJustBefore(entry, headers) - return getSectionNumber(myHeader) - - - -def index(document, filename, chapterReference): - """ - Extract index entries from the given document and store them for later use - and insert named anchors so that the index can link back to those entries. - - Any node with a C{class} attribute set to C{index} is considered an index - entry. - - @type document: A DOM Node or Document - @param document: The input document which contains all of the content to be - presented. - - @type filename: C{str} - @param filename: A link to the output for the given document which will be - included in the index to link to any index entry found here. - - @type chapterReference: ??? - @param chapterReference: ??? - - @return: C{None} - """ - entries = domhelpers.findElementsWithAttribute(document, "class", "index") - if not entries: - return - i = 0; - for entry in entries: - i += 1 - anchor = 'index%02d' % i - if chapterReference: - ref = getSectionReference(entry) or chapterReference - else: - ref = 'link' - indexer.addEntry(filename, anchor, entry.getAttribute('value'), ref) - # does nodeName even affect anything? - entry.nodeName = entry.tagName = entry.endTagName = 'a' - for attrName in entry.attributes.keys(): - entry.removeAttribute(attrName) - entry.setAttribute('name', anchor) - - - -def setIndexLink(template, indexFilename): - """ - Insert a link to an index document. - - Any node with a C{class} attribute set to C{index-link} will have its tag - name changed to C{a} and its C{href} attribute set to C{indexFilename}. - - @type template: A DOM Node or Document - @param template: The output template which defines the presentation of the - version information. - - @type indexFilename: C{str} - @param indexFilename: The address of the index document to which to link. - If any C{False} value, this function will remove all index-link nodes. - - @return: C{None} - """ - indexLinks = domhelpers.findElementsWithAttribute(template, - "class", - "index-link") - for link in indexLinks: - if indexFilename is None: - link.parentNode.removeChild(link) - else: - link.nodeName = link.tagName = link.endTagName = 'a' - for attrName in link.attributes.keys(): - link.removeAttribute(attrName) - link.setAttribute('href', indexFilename) - - - -def numberDocument(document, chapterNumber): - """ - Number the sections of the given document. - - A dot-separated chapter, section number is added to the beginning of each - section, as defined by C{h2} nodes. - - This is probably intended to interact in a rather specific way with - L{getSectionNumber}. - - @type document: A DOM Node or Document - @param document: The input document which contains all of the content to be - presented. - - @type chapterNumber: C{int} - @param chapterNumber: The chapter number of this content in an overall - document. - - @return: C{None} - """ - i = 1 - for node in domhelpers.findNodesNamed(document, "h2"): - label = dom.Text() - label.data = "%s.%d " % (chapterNumber, i) - node.insertBefore(label, node.firstChild) - i += 1 - - - -def fixRelativeLinks(document, linkrel): - """ - Replace relative links in C{str} and C{href} attributes with links relative - to C{linkrel}. - - @type document: A DOM Node or Document - @param document: The output template. - - @type linkrel: C{str} - @param linkrel: An prefix to apply to all relative links in C{src} or - C{href} attributes in the input document when generating the output - document. - """ - for attr in 'src', 'href': - for node in domhelpers.findElementsWithAttribute(document, attr): - href = node.getAttribute(attr) - if not href.startswith('http') and not href.startswith('/'): - node.setAttribute(attr, linkrel+node.getAttribute(attr)) - - - -def setTitle(template, title, chapterNumber): - """ - Add title and chapter number information to the template document. - - The title is added to the end of the first C{title} tag and the end of the - first tag with a C{class} attribute set to C{title}. If specified, the - chapter is inserted before the title. - - @type template: A DOM Node or Document - @param template: The output template which defines the presentation of the - version information. - - @type title: C{list} of DOM Nodes - @param title: Nodes from the input document defining its title. - - @type chapterNumber: C{int} - @param chapterNumber: The chapter number of this content in an overall - document. If not applicable, any C{False} value will result in this - information being omitted. - - @return: C{None} - """ - if numberer.getNumberSections() and chapterNumber: - titleNode = dom.Text() - # This is necessary in order for cloning below to work. See Python - # isuse 4851. - titleNode.ownerDocument = template.ownerDocument - titleNode.data = '%s. ' % (chapterNumber,) - title.insert(0, titleNode) - - for nodeList in (domhelpers.findNodesNamed(template, "title"), - domhelpers.findElementsWithAttribute(template, "class", - 'title')): - if nodeList: - for titleNode in title: - nodeList[0].appendChild(titleNode.cloneNode(True)) - - - -def setAuthors(template, authors): - """ - Add author information to the template document. - - Names and contact information for authors are added to each node with a - C{class} attribute set to C{authors} and to the template head as C{link} - nodes. - - @type template: A DOM Node or Document - @param template: The output template which defines the presentation of the - version information. - - @type authors: C{list} of two-tuples of C{str} - @param authors: List of names and contact information for the authors of - the input document. - - @return: C{None} - """ - - for node in domhelpers.findElementsWithAttribute(template, - "class", 'authors'): - - # First, similarly to setTitle, insert text into an <div - # class="authors"> - container = dom.Element('span') - for name, href in authors: - anchor = dom.Element('a') - anchor.setAttribute('href', href) - anchorText = dom.Text() - anchorText.data = name - anchor.appendChild(anchorText) - if (name, href) == authors[-1]: - if len(authors) == 1: - container.appendChild(anchor) - else: - andText = dom.Text() - andText.data = 'and ' - container.appendChild(andText) - container.appendChild(anchor) - else: - container.appendChild(anchor) - commaText = dom.Text() - commaText.data = ', ' - container.appendChild(commaText) - - node.appendChild(container) - - # Second, add appropriate <link rel="author" ...> tags to the <head>. - head = domhelpers.findNodesNamed(template, 'head')[0] - authors = [dom.parseString('<link rel="author" href="%s" title="%s"/>' - % (href, name)).childNodes[0] - for name, href in authors] - head.childNodes.extend(authors) - - - -def setVersion(template, version): - """ - Add a version indicator to the given template. - - @type template: A DOM Node or Document - @param template: The output template which defines the presentation of the - version information. - - @type version: C{str} - @param version: The version string to add to the template. - - @return: C{None} - """ - for node in domhelpers.findElementsWithAttribute(template, "class", - "version"): - text = dom.Text() - text.data = version - node.appendChild(text) - - - -def getOutputFileName(originalFileName, outputExtension, index=None): - """ - Return a filename which is the same as C{originalFileName} except for the - extension, which is replaced with C{outputExtension}. - - For example, if C{originalFileName} is C{'/foo/bar.baz'} and - C{outputExtension} is C{'quux'}, the return value will be - C{'/foo/bar.quux'}. - - @type originalFileName: C{str} - @type outputExtension: C{stR} - @param index: ignored, never passed. - @rtype: C{str} - """ - return os.path.splitext(originalFileName)[0]+outputExtension - - - -def munge(document, template, linkrel, dir, fullpath, ext, url, config, outfileGenerator=getOutputFileName): - """ - Mutate C{template} until it resembles C{document}. - - @type document: A DOM Node or Document - @param document: The input document which contains all of the content to be - presented. - - @type template: A DOM Node or Document - @param template: The template document which defines the desired - presentation format of the content. - - @type linkrel: C{str} - @param linkrel: An prefix to apply to all relative links in C{src} or - C{href} attributes in the input document when generating the output - document. - - @type dir: C{str} - @param dir: The directory in which to search for source listing files. - - @type fullpath: C{str} - @param fullpath: The file name which contained the input document. - - @type ext: C{str} - @param ext: The extension to use when selecting an output file name. This - replaces the extension of the input file name. - - @type url: C{str} - @param url: A string which will be interpolated with the fully qualified - Python name of any API reference encountered in the input document, the - result of which will be used as a link to API documentation for that name - in the output document. - - @type config: C{dict} - @param config: Further specification of the desired form of the output. - Valid keys in this dictionary:: - - noapi: If present and set to a True value, links to API documentation - will not be generated. - - version: A string which will be included in the output to indicate the - version of this documentation. - - @type outfileGenerator: Callable of C{str}, C{str} returning C{str} - @param outfileGenerator: Output filename factory. This is invoked with the - intput filename and C{ext} and the output document is serialized to the - file with the name returned. - - @return: C{None} - """ - fixRelativeLinks(template, linkrel) - addMtime(template, fullpath) - removeH1(document) - if not config.get('noapi', False): - fixAPI(document, url) - fontifyPython(document) - fixLinks(document, ext) - addPyListings(document, dir) - addHTMLListings(document, dir) - addPlainListings(document, dir) - putInToC(template, generateToC(document)) - footnotes(document) - notes(document) - - setIndexLink(template, indexer.getIndexFilename()) - setVersion(template, config.get('version', '')) - - # Insert the document into the template - chapterNumber = htmlbook.getNumber(fullpath) - title = domhelpers.findNodesNamed(document, 'title')[0].childNodes - setTitle(template, title, chapterNumber) - if numberer.getNumberSections() and chapterNumber: - numberDocument(document, chapterNumber) - index(document, outfileGenerator(os.path.split(fullpath)[1], ext), - htmlbook.getReference(fullpath)) - - authors = domhelpers.findNodesNamed(document, 'link') - authors = [(node.getAttribute('title') or '', - node.getAttribute('href') or '') - for node in authors - if node.getAttribute('rel') == 'author'] - setAuthors(template, authors) - - body = domhelpers.findNodesNamed(document, "body")[0] - tmplbody = domhelpers.findElementsWithAttribute(template, "class", - "body")[0] - tmplbody.childNodes = body.childNodes - tmplbody.setAttribute("class", "content") - - -class _LocationReportingErrorHandler(ErrorHandler): - """ - Define a SAX error handler which can report the location of fatal - errors. - - Unlike the errors reported during parsing by other APIs in the xml - package, this one tries to mismatched tag errors by including the - location of both the relevant opening and closing tags. - """ - def __init__(self, contentHandler): - self.contentHandler = contentHandler - - def fatalError(self, err): - # Unfortunately, the underlying expat error code is only exposed as - # a string. I surely do hope no one ever goes and localizes expat. - if err.getMessage() == 'mismatched tag': - expect, begLine, begCol = self.contentHandler._locationStack[-1] - endLine, endCol = err.getLineNumber(), err.getColumnNumber() - raise process.ProcessingFailure( - "mismatched close tag at line %d, column %d; expected </%s> " - "(from line %d, column %d)" % ( - endLine, endCol, expect, begLine, begCol)) - raise process.ProcessingFailure( - '%s at line %d, column %d' % (err.getMessage(), - err.getLineNumber(), - err.getColumnNumber())) - - -class _TagTrackingContentHandler(SAX2DOM): - """ - Define a SAX content handler which keeps track of the start location of - all open tags. This information is used by the above defined error - handler to report useful locations when a fatal error is encountered. - """ - def __init__(self): - SAX2DOM.__init__(self) - self._locationStack = [] - - def setDocumentLocator(self, locator): - self._docLocator = locator - SAX2DOM.setDocumentLocator(self, locator) - - def startElement(self, name, attrs): - self._locationStack.append((name, self._docLocator.getLineNumber(), self._docLocator.getColumnNumber())) - SAX2DOM.startElement(self, name, attrs) - - def endElement(self, name): - self._locationStack.pop() - SAX2DOM.endElement(self, name) - - -class _LocalEntityResolver(object): - """ - Implement DTD loading (from a local source) for the limited number of - DTDs which are allowed for Lore input documents. - - @ivar filename: The name of the file containing the lore input - document. - - @ivar knownDTDs: A mapping from DTD system identifiers to L{FilePath} - instances pointing to the corresponding DTD. - """ - s = FilePath(__file__).sibling - - knownDTDs = { - None: s("xhtml1-strict.dtd"), - "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd": s("xhtml1-strict.dtd"), - "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd": s("xhtml1-transitional.dtd"), - "xhtml-lat1.ent": s("xhtml-lat1.ent"), - "xhtml-symbol.ent": s("xhtml-symbol.ent"), - "xhtml-special.ent": s("xhtml-special.ent"), - } - del s - - def __init__(self, filename): - self.filename = filename - - - def resolveEntity(self, publicId, systemId): - source = InputSource() - source.setSystemId(systemId) - try: - dtdPath = self.knownDTDs[systemId] - except KeyError: - raise process.ProcessingFailure( - "Invalid DTD system identifier (%r) in %s. Only " - "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd " - "is allowed." % (systemId, self.filename)) - source.setByteStream(dtdPath.open()) - return source - - - -def parseFileAndReport(filename, _open=file): - """ - Parse and return the contents of the given lore XHTML document. - - @type filename: C{str} - @param filename: The name of a file containing a lore XHTML document to - load. - - @raise process.ProcessingFailure: When the contents of the specified file - cannot be parsed. - - @rtype: A DOM Document - @return: The document contained in C{filename}. - """ - content = _TagTrackingContentHandler() - error = _LocationReportingErrorHandler(content) - parser = make_parser() - parser.setContentHandler(content) - parser.setErrorHandler(error) - - # In order to call a method on the expat parser which will be used by this - # parser, we need the expat parser to be created. This doesn't happen - # until reset is called, normally by the parser's parse method. That's too - # late for us, since it will then go on to parse the document without - # letting us do any extra set up. So, force the expat parser to be created - # here, and then disable reset so that the parser created is the one - # actually used to parse our document. Resetting is only needed if more - # than one document is going to be parsed, and that isn't the case here. - parser.reset() - parser.reset = lambda: None - - # This is necessary to make the xhtml1 transitional declaration optional. - # It causes LocalEntityResolver.resolveEntity(None, None) to be called. - # LocalEntityResolver handles that case by giving out the xhtml1 - # transitional dtd. Unfortunately, there is no public API for manipulating - # the expat parser when using xml.sax. Using the private _parser attribute - # may break. It's also possible that make_parser will return a parser - # which doesn't use expat, but uses some other parser. Oh well. :( - # -exarkun - parser._parser.UseForeignDTD(True) - parser.setEntityResolver(_LocalEntityResolver(filename)) - - # This is probably no-op because expat is not a validating parser. Who - # knows though, maybe you figured out a way to not use expat. - parser.setFeature(feature_validation, False) - - fObj = _open(filename) - try: - try: - parser.parse(fObj) - except IOError, e: - raise process.ProcessingFailure( - e.strerror + ", filename was '" + filename + "'") - finally: - fObj.close() - return content.document - - -def makeSureDirectoryExists(filename): - filename = os.path.abspath(filename) - dirname = os.path.dirname(filename) - if (not os.path.exists(dirname)): - os.makedirs(dirname) - -def doFile(filename, linkrel, ext, url, templ, options={}, outfileGenerator=getOutputFileName): - """ - Process the input document at C{filename} and write an output document. - - @type filename: C{str} - @param filename: The path to the input file which will be processed. - - @type linkrel: C{str} - @param linkrel: An prefix to apply to all relative links in C{src} or - C{href} attributes in the input document when generating the output - document. - - @type ext: C{str} - @param ext: The extension to use when selecting an output file name. This - replaces the extension of the input file name. - - @type url: C{str} - @param url: A string which will be interpolated with the fully qualified - Python name of any API reference encountered in the input document, the - result of which will be used as a link to API documentation for that name - in the output document. - - @type templ: A DOM Node or Document - @param templ: The template on which the output document will be based. - This is mutated and then serialized to the output file. - - @type options: C{dict} - @param options: Further specification of the desired form of the output. - Valid keys in this dictionary:: - - noapi: If present and set to a True value, links to API documentation - will not be generated. - - version: A string which will be included in the output to indicate the - version of this documentation. - - @type outfileGenerator: Callable of C{str}, C{str} returning C{str} - @param outfileGenerator: Output filename factory. This is invoked with the - intput filename and C{ext} and the output document is serialized to the - file with the name returned. - - @return: C{None} - """ - doc = parseFileAndReport(filename) - clonedNode = templ.cloneNode(1) - munge(doc, clonedNode, linkrel, os.path.dirname(filename), filename, ext, - url, options, outfileGenerator) - newFilename = outfileGenerator(filename, ext) - _writeDocument(newFilename, clonedNode) - - - -def _writeDocument(newFilename, clonedNode): - """ - Serialize the given node to XML into the named file. - - @param newFilename: The name of the file to which the XML will be - written. If this is in a directory which does not exist, the - directory will be created. - - @param clonedNode: The root DOM node which will be serialized. - - @return: C{None} - """ - makeSureDirectoryExists(newFilename) - f = open(newFilename, 'w') - f.write(clonedNode.toxml('utf-8')) - f.close() |