# Author: Bill Bumgarner # Contact: bbum@mac.com # Copyright: 2002 - Bill Bumgarner - All Rights Reserved # License: The MIT License -- see LICENSE.txt """ This is the DocArticle package. This package provides a writer for DocUtils that spews HTML compliant with O'Reilly's Dev Center article submission guidelines. """ try: x = True except NameError: True = 1 False = 0 __docformat__ = 'reStructuredText' import sys from warnings import warn import re from types import * import docutils from docutils import nodes, utils, writers, languages from DocArticle import DocArticleText class DocArticleWriter(writers.Writer): supported = ('html',) """Formats this writer supports.""" output = None """Final translated form of `document`.""" def __init__(self): writers.Writer.__init__(self) self.translator_class = HTMLDocArticleTranslator def translate(self): visitor = self.translator_class(self.document) self.document.walkabout(visitor) self.output = visitor.astext() SpewNothing = 0 SpewParagraph = 1 SpewBreak = 2 SpewBreakBreak = 3 SpewNothingThenPara = 4 olTypeTranslator = { 'arabic' : '1', 'upperalpha' : 'A', 'loweralpha' : 'a', 'upperroman' : 'I', 'lowerroman' : 'i' } class HTMLDocArticleTranslator(nodes.NodeVisitor): named_tags = {'a': 1, 'applet': 1, 'form': 1, 'frame': 1, 'iframe': 1, 'img': 1, 'map': 1} words_and_spaces = re.compile(r'\S+| +|\n') def __init__(self, document): nodes.NodeVisitor.__init__(self, document) self.section_level = 0 self.headerContent = [] self.bodyContent = [] self.bodySuffix = [] self.metaContent = [] self.context = [] self.spewTextContext = [True] self.spewParaTag = [SpewParagraph] self.paraFormat = [(None,None)] self.colspecs = [] self.body_pre_docinfo = [] self.docinfo = [] self.compact_simple = None self.compact_p = 1 self.firstFootnoteVisited = False # lcode = settings.language_code lcode = 'en' self.language = languages.get_language(lcode) def astext(self): return ''.join([DocArticleText.contentStart, DocArticleText.headerStart] + self.headerContent + [DocArticleText.headerEnd, DocArticleText.bodyStart] + self.body_pre_docinfo + self.docinfo + self.bodyContent + self.bodySuffix + [DocArticleText.bodyEnd, DocArticleText.contentEnd]) def encode(self, text): """Encode special characters in `text` & return.""" # @@@ A codec to do these and all other HTML entities would be nice. text = text.replace('&', '&') text = text.replace('<', '<') text = text.replace('"', '"') text = text.replace('>', '>') return text def popAndAppend(self, node): possiblePoppedContent = self.context.pop() if possiblePoppedContent: self.bodyContent.append(possiblePoppedContent) def attval(self, text, whitespace=re.compile('[\n\r\t\v\f]')): """Cleanse, HTML encode, and return attribute value text.""" return self.encode(whitespace.sub(' ', text)) def emptytag(self, node, tagname, suffix='\n', **attributes): """Construct and return an XML-compatible empty tag.""" return self.starttag(node, tagname, suffix, infix=' /', **attributes) def starttag(self, node, tagname, suffix='\n', infix='', **attributes): tagname = tagname.lower() atts = {} for (name, value) in attributes.items(): atts[name.lower()] = value for att in ('id',): # node attribute overrides if node.has_key(att): atts[att] = node[att] if atts.has_key('id') and self.named_tags.has_key(tagname): atts['name'] = atts['id'] # for compatibility with old browsers attlist = atts.items() attlist.sort() parts = [tagname] for name, value in attlist: if value is None: # boolean attribute # According to the HTML spec, ```` is good, # ```` is bad. # (But the XHTML (XML) spec says the opposite. ) parts.append(name.lower()) elif isinstance(value, ListType): values = [str(v) for v in value] parts.append('%s="%s"' % (name.lower(), self.attval(' '.join(values)))) else: parts.append('%s="%s"' % (name.lower(), self.attval(str(value)))) return '<%s%s>%s' % (' '.join(parts), infix, suffix) def visit_Text(self, node): if self.spewTextContext[-1]: self.bodyContent.append(self.encode(node.astext())) def depart_Text(self, node): pass def visit_address(self, node): self.visit_docinfo_item(node, 'address', meta=None) self.bodyContent.append(self.starttag(node, 'pre')) def depart_address(self, node): self.bodyContent.append('\n\n') self.depart_docinfo_item(node) def visit_admonition(self, node, name='', admonitionCellAtts={}): baseAdmonitionCellAtts = {"width" : "15%"} baseAdmonitionCellAtts.update(admonitionCellAtts) self.bodyContent.append('\n' '
\n') self.bodyContent.append(self.starttag(node, 'td', **baseAdmonitionCellAtts)) if name: self.bodyContent.append(self.language.labels[name.lower()]) self.bodyContent.append('
') def depart_admonition(self, node): self.bodyContent.append('
') attribution_formats = {'dash': ('—', ''), 'parentheses': ('(', ')'), 'parens': ('(', ')'), 'none': ('', '')} def visit_attribution(self, node): # prefix, suffix = self.attribution_formats[self.settings.attribution] prefix, suffix = ('(', ')') self.context.append(suffix) self.bodyContent.append( self.starttag(node, 'p', prefix)) def depart_attribution(self, node): self.bodyContent.append(self.context.pop() + '

\n') def visit_author(self, node): self.visit_docinfo_item(node, 'Author') def depart_author(self, node): self.depart_docinfo_item(node) def visit_authors(self, node): pass def depart_authors(self, node): pass def visit_attention(self, node): self.visit_admonition(node, 'attention', admonitionCellAtts={"bgcolor":"#ffffcc"}) def depart_attention(self, node): self.depart_admonition(node) def visit_block_quote(self, node): self.bodyContent.append(self.starttag(node, 'blockquote')) def depart_block_quote(self, node): self.bodyContent.append('\n') def visit_line_block(self, node): self.bodyContent.append(self.starttag(node, 'pre')) def depart_line_block(self, node): self.bodyContent.append('\n\n') def visit_bullet_list(self, node): self.bodyContent.append(self.starttag(node, 'ul')) self.spewParaTag.append(SpewNothing) def depart_bullet_list(self, node): self.bodyContent.append('\n') self.spewParaTag.pop() def visit_caption(self, node): self.bodyContent.append(self.starttag(node, 'p', '')) def depart_caption(self, node): self.bodyContent.append('

\n') def visit_caution(self, node): self.visit_admonition(node, 'caution', admonitionCellAtts={"bgcolor":"#ffff99"}) def depart_caution(self, node): self.depart_admonition(node) def visit_citation(self, node): ##! verify col configuration self.bodyContent.append(self.starttag(node, 'table')) self.bodyContent.append('\n' '\n' '\n' '') self.footnote_backrefs(node) def depart_citation(self, node): self.bodyContent.append('\n' '\n\n') def visit_citation_reference(self, node): href = '' if node.has_key('refid'): href = '#' + node['refid'] elif node.has_key('refname'): href = '#' + self.document.nameids[node['refname']] self.bodyContent.append(self.starttag(node, 'a', '[', href=href)) def depart_citation_reference(self, node): self.bodyContent.append(']') def visit_classifier(self, node): pass def depart_classifier(self, node): pass def visit_colspec(self, node): self.colspecs.append(node) def depart_colspec(self, node): pass def write_colspecs(self): ##! verify width = 0 for node in self.colspecs: width += node['colwidth'] for node in self.colspecs: colwidth = int(node['colwidth'] * 100.0 / width + 0.5) self.bodyContent.append(self.emptytag(node, 'col', width='%i%%' % colwidth)) self.colspecs = [] def visit_comment(self, node, sub=re.compile('-(?=-)').sub): self.bodyContent.append('\n' % sub('- ', node.astext())) raise nodes.SkipNode def visit_contact(self, node): self.visit_docinfo_item(node, 'Contact') def depart_contact(self, node): self.depart_docinfo_item(node) def visit_copyright(self, node): self.visit_docinfo_item(node, 'copyright') def depart_copyright(self, node): self.depart_docinfo_item(node) def visit_danger(self, node): self.visit_admonition(node, 'danger', admonitionCellAtts={"bgcolor":"#ff6666"}) def depart_danger(self, node): self.depart_admonition(node) def visit_date(self, node): self.visit_docinfo_item(node, 'date') def depart_date(self, node): self.depart_docinfo_item(node) def visit_decoration(self, node): pass def depart_decoration(self, node): pass def visit_definition(self, node): self.bodyContent.append('\n') self.bodyContent.append(self.starttag(node, 'dd', '')) def depart_definition(self, node): self.bodyContent.append('\n') def visit_definition_list(self, node): self.bodyContent.append(self.starttag(node, 'dl')) def depart_definition_list(self, node): self.bodyContent.append('\n') def visit_definition_list_item(self, node): pass def depart_definition_list_item(self, node): pass def visit_description(self, node): self.bodyContent.append(self.starttag(node, 'td', '')) def depart_description(self, node): self.bodyContent.append('') def visit_docinfo(self, node): self.context.append(len(self.bodyContent)) self.bodyContent.append(self.starttag(node, 'table')) self.bodyContent.append('\n') self.in_docinfo = 1 def depart_docinfo(self, node): self.bodyContent.append('\n\n') self.in_docinfo = None start = self.context.pop() self.body_pre_docinfo = self.bodyContent[:start] self.docinfo = self.bodyContent[start:] self.bodyContent = [] def visit_docinfo_item(self, node, name, meta=1): if meta: self.headerContent.append('\n' % (name, self.attval(node.astext()))) self.bodyContent.append(self.starttag(node, 'tr', '')) self.bodyContent.append('%s:\n' % self.language.labels[name.lower()]) def depart_docinfo_item(self, node): self.bodyContent.append('\n') def visit_doctest_block(self, node): self.bodyContent.append(self.starttag(node, 'pre')) def depart_doctest_block(self, node): self.bodyContent.append('\n\n') def visit_document(self, node): pass def depart_document(self, node): pass def visit_emphasis(self, node): self.bodyContent.append('') def depart_emphasis(self, node): self.bodyContent.append('') def visit_entry(self, node): if isinstance(node.parent.parent, nodes.thead): tagname = 'th' else: tagname = 'td' atts = {} if node.has_key('morerows'): atts['rowspan'] = node['morerows'] + 1 if node.has_key('morecols'): atts['colspan'] = node['morecols'] + 1 self.bodyContent.append(self.starttag(node, tagname, '', **atts)) self.context.append('\n' % tagname.lower()) if len(node) == 0: # empty cell self.bodyContent.append(' ') def depart_entry(self, node): self.bodyContent.append(self.context.pop()) def visit_enumerated_list(self, node): atts = {} if node.has_key('start'): atts['start'] = node['start'] if node.has_key('enumtype'): atts['type'] = olTypeTranslator[node['enumtype']] self.bodyContent.append(self.starttag(node, 'ol', **atts)) def depart_enumerated_list(self, node): self.bodyContent.append('\n') def visit_error(self, node): self.visit_admonition(node, 'error', admonitionCellAtts={"bgcolor":"#ff6666"}) def depart_error(self, node): self.depart_admonition(node) def visit_field(self, node): self.bodyContent.append(self.starttag(node, 'tr', '')) def depart_field(self, node): self.bodyContent.append('\n') def visit_field_body(self, node): self.bodyContent.append(self.starttag(node, 'td', '')) self.spewParaTag.append(SpewBreak) def depart_field_body(self, node): self.bodyContent.append('\n') self.spewParaTag.pop() def visit_field_list(self, node): self.bodyContent.append(self.starttag(node, 'table')) self.bodyContent.append('\n') def depart_field_list(self, node): self.bodyContent.append('\n\n') def visit_field_name(self, node): atts = {} if len(node.astext()) > 14: atts['colspan'] = 2 self.context.append('\n ') else: self.context.append('') self.bodyContent.append(self.starttag(node, 'th', '', **atts)) def depart_field_name(self, node): self.bodyContent.append(':') self.bodyContent.append(self.context.pop()) def visit_figure(self, node): self.bodyContent.append(self.starttag(node, 'div')) def depart_figure(self, node): self.bodyContent.append('\n') def visit_footer(self, node): self.context.append(len(self.body)) def depart_footer(self, node): start = self.context.pop() footer = (['
\n', self.starttag(node, 'div')] + self.bodyContent[start:] + ['\n']) self.bodySuffix[:0] = footer del self.bodyContent[start:] def check_simple_list(self, node): """Check for a simple list that can be rendered compactly.""" visitor = SimpleListChecker(self.document) try: node.walk(visitor) except nodes.NodeFound: return None else: return 1 def visit_footnote(self, node): if not self.firstFootnoteVisited: self.bodyContent.append('
') self.firstFootnoteVisited = True self.bodyContent.append(self.starttag(node, 'table')) self.bodyContent.append('\n') self.spewParaTag.append(SpewBreak) self.footnote_backrefs(node) def footnote_backrefs(self, node): # if self.settings.footnote_backlinks and node.hasattr('backrefs'): if node.hasattr('backrefs'): backrefs = node['backrefs'] if len(backrefs) == 1: self.context.append('') self.context.append('' % (backrefs[0], node['id'])) else: i = 1 backlinks = [] for backref in backrefs: backlinks.append('%s' % (backref, i)) i += 1 self.context.append('(%s) ' % ', '.join(backlinks)) self.context.append('' % node['id']) else: self.context.append('') self.context.append('' % node['id']) def depart_footnote(self, node): self.spewParaTag.pop() self.paraFormat.pop() self.bodyContent.append('\n\n\n') def visit_footnote_reference(self, node): href = '' if node.has_key('refid'): href = '#' + node['refid'] elif node.has_key('refname'): href = '#' + self.document.nameids[node['refname']] # format = self.settings.footnote_references format = 'superscript' if format == 'brackets': suffix = '[' self.context.append(']') elif format == 'superscript': suffix = '' self.context.append('') else: # shouldn't happen suffix = '???' self.content.append('???') self.bodyContent.append('') self.bodyContent.append(suffix) self.bodyContent.append(self.starttag(node, 'a', '', href=href)) def depart_footnote_reference(self, node): self.bodyContent.append('') self.bodyContent.append(self.context.pop()) self.bodyContent.append('') def visit_rubric(self, node): self.bodyContent.append(self.starttag(node, 'p', '')) def depart_rubric(self, node): self.bodyContent.append('

\n') def visit_generated(self, node): pass def depart_generated(self, node): pass def visit_hint(self, node): self.visit_admonition(node, 'hint', admonitionCellAtts={"bgcolor":"#99ff99"}) def depart_hint(self, node): self.depart_admonition(node) def visit_image(self, node): atts = node.attributes.copy() atts['src'] = atts['uri'] del atts['uri'] if not atts.has_key('alt'): atts['alt'] = atts['src'] if isinstance(node.parent, nodes.TextElement): self.context.append(None) else: self.bodyContent.append('

') self.context.append('

\n') self.bodyContent.append(self.emptytag(node, 'img', '', **atts)) depart_image = popAndAppend def visit_important(self, node): self.visit_admonition(node, 'important', admonitionCellAtts={"bgcolor":"#ffcccc"}) def depart_important(self, node): self.depart_admonition(node) def visit_interpreted(self, node): ###! no idea what to do here pass def depart_interpreted(self, node): pass def visit_label(self, node): self.bodyContent.append(self.starttag(node, 'td', '[%s' % self.context.pop())) def depart_label(self, node): self.paraFormat.append(('', '')) self.bodyContent.append(']%s' % self.context.pop()) def visit_legend(self, node): self.bodyContent.append(self.starttag(node, 'div')) def depart_legend(self, node): self.bodyContent.append('\n') def visit_list_item(self, node): self.bodyContent.append(self.starttag(node, 'li', '')) self.spewParaTag.append(SpewNothingThenPara) def depart_list_item(self, node): self.bodyContent.append('\n') self.spewParaTag.pop() def visit_literal(self, node): self.bodyContent.append(self.starttag(node, 'code', '')) text = node.astext() for token in self.words_and_spaces.findall(text): if token.strip(): # Protect text like "--an-option" from bad line wrapping: self.bodyContent.append('%s' % self.encode(token)) elif token in ('\n', ' '): # Allow breaks at whitespace: self.bodyContent.append(token) else: # Protect runs of multiple spaces; the last space can wrap: self.bodyContent.append(' ' * (len(token) - 1) + ' ') self.bodyContent.append('') # Content already processed: raise nodes.SkipNode def visit_literal_block(self, node): self.bodyContent.append(self.starttag(node, 'pre')) def depart_literal_block(self, node): self.bodyContent.append('\n\n') def visit_meta(self, node): self.headerContent.append(self.emptytag(node, 'meta', **node.attributes)) def depart_meta(self, node): pass def visit_note(self, node): self.visit_admonition(node, 'note') def depart_note(self, node): self.depart_admonition(node) def visit_option(self, node): if self.context[-1]: self.bodyContent.append(', ') def depart_option(self, node): self.context[-1] += 1 def visit_option_argument(self, node): self.bodyContent.append(node.get('delimiter', ' ')) self.bodyContent.append(self.starttag(node, 'var', '')) def depart_option_argument(self, node): self.bodyContent.append('') def visit_option_group(self, node): atts = {} if len(node.astext()) > 14: atts['colspan'] = 2 self.context.append('\n ') else: self.context.append('') self.bodyContent.append(self.starttag(node, 'td', **atts)) self.bodyContent.append('') ###! What tag is this? self.context.append(0) # count number of options def depart_option_group(self, node): self.context.pop() self.bodyContent.append('\n') self.bodyContent.append(self.context.pop()) def visit_option_list(self, node): self.bodyContent.append(self.starttag(node, 'table')) self.bodyContent.append('\n') def depart_option_list(self, node): self.bodyContent.append('\n\n') def visit_option_list_item(self, node): self.bodyContent.append(self.starttag(node, 'tr', '')) def depart_option_list_item(self, node): self.bodyContent.append('\n') def visit_option_string(self, node): self.bodyContent.append(self.starttag(node, 'span', '')) def depart_option_string(self, node): self.bodyContent.append('') def visit_organization(self, node): self.visit_docinfo_item(node, 'organization') def depart_organization(self, node): self.depart_docinfo_item(node) def visit_paragraph(self, node): currentSpewParaTag = self.spewParaTag[-1] if currentSpewParaTag == SpewParagraph: self.bodyContent.append(self.starttag(node, 'p', '')) self.context.append('

\n') elif currentSpewParaTag == SpewBreak: self.context.append('
\n') elif currentSpewParaTag == SpewBreakBreak: self.context.append('

\n') elif currentSpewParaTag == SpewNothingThenPara: self.context.append(None) self.spewParaTag[-1] = SpewParagraph else: self.context.append(None) start, end = self.paraFormat[-1] if start: self.bodyContent.append(start) if end: self.context.append(end) else: self.context.append(None) def depart_paragraph(self, node): self.popAndAppend(node) # pop end formatting tag, if any self.popAndAppend(node) # pop end paragraph tag, if any def visit_problematic(self, node): if node.hasattr('refid'): self.bodyContent.append('' % (node['refid'], node['id'])) self.context.append('') else: self.context.append('') self.bodyContent.append(self.starttag(node, 'span', '')) def depart_problematic(self, node): self.bodyContent.append('') self.bodyContent.append(self.context.pop()) def visit_reference(self, node): if node.has_key('refuri'): href = node['refuri'] elif node.has_key('refid'): href = '#' + node['refid'] elif node.has_key('refname'): href = '#' + self.document.nameids[node['refname']] self.bodyContent.append(self.starttag(node, 'a', '', href=href)) self.context.append('') depart_reference = popAndAppend def visit_revision(self, node): self.visit_docinfo_item(node, 'revision') def depart_revision(self, node): self.depart_docinfo_item(node) def visit_row(self, node): self.bodyContent.append(self.starttag(node, 'tr', '')) def depart_row(self, node): self.bodyContent.append('\n') def visit_section(self, node): self.section_level += 1 #hTag = 'h%s'% self.section_level #self.bodyContent.append(self.starttag(node, hTag)) #self.bodyContent.append('\n' % hTag) def depart_section(self, node): self.section_level -= 1 def visit_sidebar(self, node): self.bodyContent.append('
') self.bodyContent.append('
') def depart_sidebar(self, node): self.bodyContent.append('
\n') self.bodyContent.append('
') def visit_status(self, node): self.visit_docinfo_item(node, 'status', meta=None) def depart_status(self, node): self.depart_docinfo_item(node) def visit_strong(self, node): self.bodyContent.append('') def depart_strong(self, node): self.bodyContent.append('') def visit_subscript(self, node): self.bodyContent.append(self.starttag(node, 'sub', '')) def depart_subscript(self, node): self.bodyContent.append('') def visit_substitution_definition(self, node): raise nodes.SkipNode # internal def visit_substitution_reference(self, node): pass def visit_subtitle(self, node): self.bodyContent.append(self.starttag(node, 'h3', '')) def depart_subtitle(self, node): self.bodyContent.append('\n') def visit_superscript(self, node): self.bodyContent.append(self.starttag(node, 'sup', '')) def depart_superscript(self, node): self.bodyContent.append('') def visit_table(self, node): self.bodyContent.append(self.starttag(node, 'table')) def depart_table(self, node): self.bodyContent.append('\n') def visit_target(self, node): if not (node.has_key('refuri') or node.has_key('refid') or node.has_key('refname')): self.bodyContent.append(self.starttag(node, 'a', '')) self.context.append('') else: self.context.append(None) depart_target = popAndAppend def visit_tbody(self, node): self.write_colspecs() self.bodyContent.append(self.context.pop()) # '\n' or '' self.bodyContent.append(self.starttag(node, 'tbody', valign='top')) def depart_tbody(self, node): self.bodyContent.append('\n') def visit_term(self, node): self.bodyContent.append(self.starttag(node, 'dt', '')) def depart_term(self, node): pass def visit_tgroup(self, node): ###! verify # Mozilla needs : self.bodyContent.append(self.starttag(node, 'colgroup')) # Appended by thead or tbody: self.context.append('\n') def depart_tgroup(self, node): pass def visit_thead(self, node): self.write_colspecs() self.bodyContent.append(self.context.pop()) # '\n' # There may or may not be a ; this is for to use: self.context.append('') self.bodyContent.append(self.starttag(node, 'thead', valign='bottom')) def depart_thead(self, node): self.bodyContent.append('\n') def visit_tip(self, node): self.visit_admonition(node, 'tip') def depart_tip(self, node): self.depart_admonition(node) def visit_title(self, node): if isinstance(node.parent, nodes.topic) and (node.parent.attributes.get('name', None) != 'contents'): self.bodyContent.append('
') if node.parent.hasattr('id'): self.bodyContent.append(self.starttag({},'a','',name=node.parent['id'])) self.context.append('

\n') else: self.context.append('
\n') elif self.section_level == 0: self.headerContent.append(DocArticleText.titleStart) self.headerContent.append(self.encode(node.astext())) self.headerContent.append(DocArticleText.titleEnd) self.bodyContent.append(self.starttag(node, 'h2', '')) self.context.append('\n') else: ### O'Reilly uses h2 to denote title and h3 for all sections. In theory, ### nothing should hang below h3. In practice, we leave it up to the ### author. level = self.section_level + 1 self.bodyContent.append(self.starttag(node, 'h%s' % level, '')) atts = {} if node.parent.hasattr('id'): atts['name'] = node.parent['id'] if node.hasattr('refid'): atts['href'] = '#' + node['refid'] self.bodyContent.append(self.starttag({}, 'a', '', **atts)) self.context.append('\n' % level) def depart_title(self, node): self.popAndAppend(node) def visit_title_reference(self, node): self.bodyContent.append(self.starttag(node, 'cite', '')) def depart_title_reference(self, node): self.bodyContent.append('') def visit_topic(self, node): if node.attributes.get('name', None) != 'contents': self.spewParaTag.append(SpewNothingThenPara) self.bodyContent.append('
') def depart_topic(self, node): if node.attributes.get('name', None) != 'contents': self.bodyContent.append('
\n') self.spewParaTag.pop() def visit_transition(self, node): self.bodyContent.append(self.emptytag(node, 'hr')) def depart_transition(self, node): pass def visit_version(self, node): self.visit_docinfo_item(node, 'version') def depart_version(self, node): self.depart_docinfo_item(node) def visit_warning(self, node): self.visit_admonition(node, 'warning', admonitionCellAtts={"bgcolor":"#ffff33"}) def depart_warning(self, node): self.depart_admonition(node) def unknown_visit(self, node): print 'Node: %s' % node.__class__.__name__ print "Failure processing at line (%s) of node:\n %s" % (node.line, node.pformat()) raise NotImplementedError('visiting unknown node type: %s' % node.__class__.__name__) def visit_system_message(self, node): if node['level'] < self.document.reporter['writer'].report_level: # Level is too low to display: raise nodes.SkipNode self.bodyContent.append(self.starttag(node, 'div')) self.bodyContent.append('

') attr = {} backref_text = '' if node.hasattr('id'): attr['name'] = node['id'] if node.hasattr('backrefs'): backrefs = node['backrefs'] if len(backrefs) == 1: backref_text = ('; backlink' % backrefs[0]) else: i = 1 backlinks = [] for backref in backrefs: backlinks.append('%s' % (backref, i)) i += 1 backref_text = ('; backlinks: %s' % ', '.join(backlinks)) if node.hasattr('line'): line = ', line %s' % node['line'] else: line = '' if attr: a_start = self.starttag({}, 'a', '', **attr) a_end = '' else: a_start = a_end = '' self.bodyContent.append('System Message: %s%s/%s%s (%s%s)%s

\n' % (a_start, node['type'], node['level'], a_end, node['source'], line, backref_text)) def depart_system_message(self, node): self.bodyContent.append('\n')