PYTHON   18

html

Guest on 16th May 2022 05:12:45 PM

  1. """Output DOCUTILS nodes as HTML.
  2.  
  3. This is a quick-and-dirty approach to writing out HTML derived from
  4. a DOCUTILS node tree. It maintains a minimum of state, and doesn't attempt
  5. any particular intelligence about the tree structure.
  6.  
  7.    Note: for debugging purposes some HTML elements are output with
  8.    "style" attributes - this is so I can track which elements were
  9.    written for what purpose, and is temporary.
  10.  
  11.    (Use of "class" attributes to make CSS usage easier is a
  12.    separate consideration, to be made later on.)
  13.  
  14. Use of this should ultimately be replaced by use of David's new mechanisms
  15. from the docutils module - but they didn't exist when I started, so we'll live
  16. with this for a little longer.
  17. """
  18.  
  19. import time
  20. import buildhtml
  21.  
  22. __docformat__ = "reST"
  23.  
  24. class HTMLError(Exception):
  25.     pass
  26.  
  27. # ----------------------------------------------------------------------
  28. class Writer:
  29.     """Encapsulate the HTML writing stuff in a class
  30.  
  31.    - it makes it easier to handle values we want to keep around
  32.    """
  33.  
  34.     colours = {"Information": "#FF0000",
  35.                "Warning"    : "#FF0000",
  36.                "Error"      : "#FF0000",
  37.                "Fatal"      : "#FF0000",
  38.                "WarningBG"  : "Silver",   # (was "lightgrey" or #DDDDDD)
  39.                "default"    : "#FFFFCC",
  40.                }
  41.     """Colours to use to distinguish various contexts
  42.  
  43.    Note that the HTML4 spec defines the following colours:
  44.  
  45.    * Black   = "#000000"
  46.    * Silver  = "#C0C0C0"
  47.    * Gray    = "#808080"
  48.    * White   = "#FFFFFF"
  49.    * Maroon  = "#800000"
  50.    * Red     = "#FF0000"
  51.    * Purple  = "#800080"
  52.    * Fuchsia = "#FF00FF"
  53.    * Green   = "#008000"
  54.    * Lime    = "#00FF00"
  55.    * Olive   = "#808000"
  56.    * Yellow  = "#FFFF00"
  57.    * Navy    = "#000080"
  58.    * Blue    = "#0000FF"
  59.    * Teal    = "#008080"
  60.    * Aqua    = "#00FFFF"
  61.    """ #"
  62.  
  63.     role_text = {"package"           : "Package",
  64.                  "module"            : "Module",
  65.                  "class"             : "Class",
  66.                  "method"            : "Method",
  67.                  "function"          : "Function",
  68.                  "module_attribute"  : "Module attribute",
  69.                  "class_attribute"   : "Class attribute",
  70.                  "instance_attribute": "Instance attribute",
  71.                  "variable"          : "Name",
  72.                  "parameter"         : "Argument",
  73.                  "type"              : "Type",
  74.                  "exception_class"   : "Exception class",
  75.                  "exception"         : "Exception",
  76.                  "warning_class"     : "Warning class",
  77.                  "warning"           : "Warning"}
  78.     """If an interpreted text has a role, we want to write that role
  79.    out. We thus need a dictionary to relate role names to the text
  80.    to be written out.
  81.    """
  82.  
  83.     fancy = 0
  84.     """Do we want fancy presentation?"""
  85.  
  86.     want_contents = 0
  87.     """Do we *want* contents for this document?
  88.    """
  89.  
  90.     language = "en"
  91.     """The language that we believe our document to be
  92.    destined for.
  93.        """
  94.  
  95.     showwarnings = 1
  96.     """Should we show warnings, or try to continue silently?
  97.    """
  98.  
  99.     showinforms = 1
  100.     """Should we show informational messages, or ignore them?
  101.    """
  102.  
  103.     visible_targets = 0
  104.     """Show link target names
  105.    """
  106.  
  107.     visible_links = 0
  108.     """Show link references (although not all of the possible links
  109.    we produce).
  110.    """
  111.  
  112.     def __init__(self):
  113.  
  114.         # The current document tree - unset it here just in case...
  115.         self.document = None
  116.  
  117.         # Our HTML builder - ditto
  118.         self.html = None
  119.  
  120.         # Our method cache - we seed it with the entry for "#text"
  121.         # because we can't deduce the method name from that tag
  122.         # ("write_#text" is not a valid Python name!)
  123.         self.method_cache = {"#text":self.write_text}
  124.  
  125.     def __call__(self,document,stream):
  126.         """Output an HTML representation of `document` to `stream`.
  127.  
  128.        Arguments:
  129.  
  130.            * document -- the DOCUTILS tree we are to output as HTML
  131.            * stream   -- something like a File, with a write method
  132.        """
  133.  
  134.         self.document = document
  135.         self.html = buildhtml.BuildHTML(stream)
  136.  
  137.         # Reset things (i.e., so we can be called more than once)
  138.  
  139.         # The header level to use for <section> titles
  140.         # (i.e., <h2>, <h3>, etc).
  141.         self.level = 0
  142.  
  143.         # Have we output Contents for this document?
  144.         self.got_contents = 0
  145.  
  146.         # Are we within the body of a field list item?
  147.         self.infield = 0
  148.  
  149.         # Or in a paragraph? (note - only in the sense that the DOCUTILS
  150.         # tree says that we're in a paragraph)
  151.         self.in_paragraph = 0
  152.  
  153.         # Footnote autonumbers
  154.         self.auto_footnote = 0
  155.         """The current auto-numbered footnote's number.
  156.        This will be stored as attribute "auto-index" on
  157.        the footnote itself, by `find_auto_footnotes()`.
  158.        """
  159.         self.auto_footnote_names = {}
  160.         """A dictionary linking an auto-numbered footnote label
  161.        to the corresponding (generated) footnote number. This
  162.        is populated by `find_auto_footnotes()`.
  163.        """
  164.         self.auto_footnote_list = []
  165.         """A list of the auto-numbered footnote numbers that
  166.        are used for non-named footnotes. This list is then
  167.        used to populate the [#]_ footnote references. It is
  168.        populated by `find_auto_footnotes()`.
  169.        """
  170.         self.auto_footnote_index = 0
  171.         """An index into `self.auto_footnote_list`.
  172.        """
  173.         self.auto_footnote_target = 0
  174.         """This is used to record the numbering for the "link" end
  175.        of footnotes - i.e., the number for autonumbered references.
  176.        """
  177.  
  178.         self.find_auto_footnotes(document)
  179.  
  180.         # Table location
  181.         self.in_table_header = 0
  182.         self.in_table_body = 0
  183.  
  184.         # And now down to work...
  185.         self.html.write_doctype()
  186.         self.html.start("html")
  187.  
  188.         # Hmm - have we been handed a "document" rooted tree,
  189.         # or a DOM-like tree that has "document" as its single child?
  190.         if document.tagname == "document":
  191.             self.write_html(document,stream)
  192.         else:
  193.             for element in document:
  194.                 self.write_html(element,stream)
  195.  
  196.         self.html.end("html")
  197.         self.html.finish()
  198.  
  199.     def find_auto_footnotes(self,element):
  200.         """Locate and number autonumbered footnotes...
  201.        """
  202.  
  203.         if element.tagname == "#text":
  204.             return
  205.         elif element.tagname == "footnote":
  206.             # This is a footnote body - it is the footnote bodies
  207.             # that determine their order and numbering...
  208.             name = auto = None
  209.             if element.hasattr("name"):
  210.                 name = element["name"]
  211.             if element.hasattr("auto"):
  212.                 auto = element["auto"]
  213.                 self.auto_footnote += 1
  214.                 element["auto-index"] = self.auto_footnote
  215.             if auto:
  216.                 if name:
  217.                     if not self.auto_footnote_names.has_key(name):
  218.                         self.auto_footnote_names[name] = self.auto_footnote
  219.                     else:
  220.                         # Well, what should we do?
  221.                         # Removing it seems the best bet...
  222.                         del self.auto_footnote_names[name]
  223.                 else:
  224.                     self.auto_footnote_list.append(self.auto_footnote)
  225.  
  226.         for node in element:
  227.             self.find_auto_footnotes(node)
  228.  
  229.     def write_document(self,element,stream):
  230.         document_is_section = 0
  231.         if element.hasattr("title"):
  232.             title = self.html.escape(element["title"])
  233.         else:
  234.             firstchild = element[0]
  235.             if firstchild.tagname == "title":
  236.                 title = self.html.escape(firstchild.astext())
  237.                 document_is_section = 1
  238.             elif firstchild.hasattr("title"):
  239.                 title = self.html.escape(firstchild["title"])
  240.             else:
  241.                 title = "Document produced by pysource"
  242.  
  243.         self.html.start("head")
  244.         self.html.add("title",title)
  245.         self.html.end("head")
  246.  
  247.         self.html.start("body")
  248.         if document_is_section:
  249.             # There is no internal <section> - instead we just
  250.             # have a <document> whose first element is <title>
  251.             # So, given that, pretend we ARE a section...
  252.             self.write_section(element,stream)
  253.         else:
  254.             for node in element:
  255.                 self.write_html(node,stream)
  256.  
  257.         self.html.add("hr")
  258.         self.html.start("p")
  259.         self.html.add("em","Automatically generated by ",
  260.                       self.html.element("code","pysource"),
  261.                      " on %s\n"%time.ctime(time.time()))
  262.         self.html.end("p")
  263.         self.html.end("body")
  264.  
  265.     def write_text(self,element,stream):
  266.         """Write out plain text.
  267.        """
  268.         self.html.add("text",self.html.escape(element.astext()))
  269.  
  270.     def write_html(self,element,stream):
  271.         """Write out the HTML representation of `element` on `stream`.
  272.        """
  273.  
  274.         name = element.tagname
  275.         try:
  276.             method = self.method_cache[name]
  277.         except KeyError:
  278.             method = getattr(self,"write_%s"%name,self.write_unknown)
  279.             self.method_cache[name] = method
  280.         method(element,stream)
  281.  
  282.     def write_unknown(self,element,stream):
  283.         """Write out an element which we don't recognise.
  284.        """
  285.  
  286.         self.html.add("p",self.html.element("comment","just a spacer"))
  287.  
  288.         self.html.start("font","&lt;%s"%element.tagname,color="red")
  289.         for name,value in element.attlist():
  290.             self.html.add("text"," %s='%s'"%(name,self.html.escape(value)))
  291.         self.html.add("text","&gt;")
  292.         self.html.end("font")
  293.  
  294.         for node in element:
  295.             self.write_html(node,stream)
  296.  
  297.         self.html.add("font","&lt;/%s&gt;"%element.tagname,
  298.                       color="red")
  299.  
  300.     def write_section(self,element,stream):
  301.         """Write a section - i.e., something with a title
  302.        """
  303.  
  304.         self.level += 1
  305.  
  306.         if element.hasattr("name"):
  307.             # Lazily, escape the name so we don't have to worry
  308.             # about single quotes in it...
  309.             name=self.html.escape(element["name"])
  310.  
  311.             # Hmm - we *want* to write "\n\n<a name='...'></a>"
  312.             # which isn't *quite* what this does - maybe have a specialised
  313.             # call in buildhtml.py?
  314.             self.html.add("text",self.html.element("a",name=name))
  315.             if self.visible_links:
  316.                 self.html.start("font",color="green")
  317.                 self.html.add("text","&lt;%s&gt;"%name)
  318.                 self.html.end("font")
  319.  
  320.         for node in element:
  321.             self.write_html(node,stream)
  322.  
  323.         self.level -= 1
  324.  
  325.     def write_title(self,element,stream):
  326.         if 1 <= self.level <= 6:
  327.             self.html.start("h%d"%self.level)
  328.             for node in element:
  329.                 self.write_html(node,stream)
  330.             self.html.end("h%d"%self.level)
  331.         else:
  332.             # Put a warning here?
  333.             self.html.start("p")
  334.             self.html.start("font",size="-1",color="red")
  335.             self.html.add("text","[problem: header level='%d']"%self.level)
  336.             self.html.end("font")
  337.             self.html.start("strong")
  338.             for node in element:
  339.                 self.write_html(node,stream)
  340.             self.html.end("strong")
  341.             self.html.end("p")
  342.  
  343.     def write_transition(self,element,stream):
  344.         self.html.start("p", # hmm - strictly not legal...
  345.                         self.html.element("hr"))
  346.  
  347.     def write_enumerated_list(self,element,stream):
  348.         typedict = {"arabic" : "1",
  349.                     "roman"  : "i",
  350.                     "Roman"  : "I",
  351.                     "alpha"  : "a",
  352.                     "Alpha"  : "A"}
  353.         try:
  354.             enumtype = typedict[element["enumtype"]]
  355.         except:
  356.             enumtype = "1"
  357.  
  358.         # Does this match how DOCUTILS nodes work?
  359.         if element.hasattr("start"):
  360.             self.html.start("ol",type=enumtype,start=element["start"])
  361.         else:
  362.             self.html.start("ol",type=enumtype)
  363.         for node in element:
  364.             self.write_html(node,stream)
  365.         self.html.end("ol")
  366.  
  367.     def write_bullet_list(self,element,stream):
  368.         # Hmm - the translation is fairly arbitrary
  369.         # - but at least consistent
  370.         bulletdict = {"*" : "disc",
  371.                       "-" : "circle",
  372.                       "+" : "square"}
  373.         try:
  374.             bullet = bulletdict[element["bullet"]]
  375.         except:
  376.             bullet = None
  377.  
  378.         if bullet:
  379.             self.html.start("ul",type=bullet)
  380.         else:
  381.             self.html.start("ul")
  382.         for node in element:
  383.             self.write_html(node,stream)
  384.         self.html.end("ul")
  385.  
  386.     def write_definition_list(self,element,stream):
  387.         self.html.start("dl")
  388.         for node in element:
  389.             self.write_html(node,stream)
  390.         self.html.end("dl")
  391.  
  392.     def write_definition_list_item(self,element,stream):
  393.         # Nothing special to do for this one
  394.         for node in element:
  395.             self.write_html(node,stream)
  396.  
  397.     def write_term(self,element,stream):
  398.         self.html.start("dt")
  399.         self.html.start("strong")
  400.         for node in element:
  401.             self.write_html(node,stream)
  402.             self.html.add("text"," ") # to separate consecutive parts,
  403.                                       # in option lists
  404.         self.html.end("strong")
  405.         self.html.end("dt")
  406.  
  407.     def write_list_item(self,element,stream):
  408.         self.html.start("li")
  409.         for node in element:
  410.             self.write_html(node,stream)
  411.         self.html.end("li")
  412.  
  413.     def write_option_list(self,element,stream):
  414.         self.html.start("dl")
  415.         for node in element:
  416.             self.write_html(node,stream)
  417.         self.html.end("dl")
  418.  
  419.     def write_option_list_item(self,element,stream):
  420.         for node in element:
  421.             self.write_html(node,stream)
  422.  
  423.     def write_option(self,element,stream):
  424.         self.html.start("dt")
  425.         self.html.start("strong")
  426.         for node in element:
  427.             self.write_html(node,stream)
  428.             self.html.add("text"," ") # to separate consecutive parts,
  429.                                       # in option lists
  430.         self.html.end("strong")
  431.         self.html.end("dt")
  432.  
  433.     def write_definition(self,element,stream):
  434.         self.html.start("dd")
  435.         for node in element:
  436.             self.write_html(node,stream)
  437.         self.html.end("dd")
  438.  
  439.     def write_short_option(self,element,stream):
  440.         self.html.start("samp")
  441.         for node in element:
  442.             self.write_html(node,stream)
  443.         self.html.end("samp")
  444.  
  445.     def write_long_option(self,element,stream):
  446.         self.html.start("samp")
  447.         for node in element:
  448.             self.write_html(node,stream)
  449.         self.html.end("samp")
  450.  
  451.     def write_vms_option(self,element,stream):
  452.         self.html.start("samp")
  453.         for node in element:
  454.             self.write_html(node,stream)
  455.         self.html.end("samp")
  456.  
  457.     def write_option_argument(self,element,stream):
  458.         self.html.start("samp")
  459.         for node in element:
  460.             self.write_html(node,stream)
  461.         self.html.end("samp")
  462.  
  463.     def write_description(self,element,stream):
  464.         self.html.start("dd")
  465.         for node in element:
  466.             self.write_html(node,stream)
  467.         self.html.end("dd")
  468.  
  469.     def write_field_list(self,element,stream):
  470.         """Write out a fieldlist.
  471.        """
  472.         # The colour is for debugging purposes only!
  473.         self.html.start("table",width="100%",bgcolor="palegreen")
  474.  
  475.         self.infield = 1
  476.         for node in element:
  477.             self.write_html(node,stream)
  478.         self.infield = 0
  479.  
  480.         self.html.end("table")
  481.  
  482.     def write_field(self,element,stream):
  483.         self.html.start("tr",valign="top",dps="field")
  484.         for node in element:
  485.             self.write_html(node,stream)
  486.         self.html.end("tr")
  487.  
  488.     def write_field_name(self,element,stream):
  489.         self.html.start("td",dps="field_name")
  490.         self.html.start("strong")
  491.         for node in element:
  492.             self.write_html(node,stream)
  493.         self.html.end("strong")
  494.         self.html.end("td")
  495.  
  496.     def write_field_body(self,element,stream):
  497.         self.infield = 1
  498.         self.paranum = 0
  499.         self.html.start("td",dps="field_body")
  500.         for node in element:
  501.             self.write_html(node,stream)
  502.         self.html.end("td")
  503.         self.infield = 0
  504.  
  505.     def write_biblio_field(self,element,stream,name):
  506.         """Write out a document bibliographic datum.
  507.        """
  508.         self.infield = 1
  509.         # The colour is for debugging purposes only!
  510.         self.html.start("table",width="100%",bgcolor="palegreen")
  511.         self.html.start("tr",valign="top")
  512.         self.html.start("td",dps="biblio_field")
  513.         self.html.add("strong",name)
  514.  
  515.         if len(element) != 1:
  516.             raise HTMLError,"Found %d children in field %s"%\
  517.                   (len(element),name)
  518.  
  519.         self.write_field_body(element[0],stream)
  520.  
  521.         self.html.end("td","tr","table")
  522.         self.infield = 0
  523.  
  524.     def write_subtitle(self,element,stream):
  525.         self.write_biblio_field(element,stream,"Subtitle")
  526.  
  527.     def write_author(self,element,stream):
  528.         self.write_biblio_field(element,stream,"Author")
  529.  
  530.     def write_authors(self,element,stream):
  531.         self.write_biblio_field(element,stream,"Authors")
  532.  
  533.     def write_organization(self,element,stream):
  534.         self.write_biblio_field(element,stream,"Organisation")
  535.  
  536.     def write_organisation(self,element,stream):
  537.         self.write_biblio_field(element,stream,"Organisation")
  538.  
  539.     def write_contact(self,element,stream):
  540.         self.write_biblio_field(element,stream,"Contact")
  541.  
  542.     def write_version(self,element,stream):
  543.         self.write_biblio_field(element,stream,"Version")
  544.  
  545.     def write_status(self,element,stream):
  546.         self.write_biblio_field(element,stream,"Status")
  547.  
  548.     def write_date(self,element,stream):
  549.         self.write_biblio_field(element,stream,"Date")
  550.  
  551.     def write_revision(self,element,stream):
  552.         self.write_biblio_field(element,stream,"Revision")
  553.  
  554.     def write_copyright(self,element,stream):
  555.         self.write_biblio_field(element,stream,"Copyright")
  556.  
  557.     def write_abstract(self,element,stream):
  558.         self.write_biblio_field(element,stream,"Abstract")
  559.  
  560.     def write_reference(self,element,stream):
  561.         """Write a link - the "pointer" to a target.
  562.  
  563.        Doesn't yet handle "indirect" links...
  564.        """
  565.         if element.hasattr("refuri"):
  566.             name = self.html.escape(element["refuri"])
  567.             self.html.start("a",href=name)
  568.         elif element.hasattr("refname"):
  569.             # Escape the name to match what we do with titles...
  570.             name = "#"+self.html.escape(element["refname"])
  571.             self.html.start("a",href=name)
  572.         else:
  573.             self.write_unknown(element,stream)
  574.             return
  575.         for node in element:
  576.             self.write_html(node,stream)
  577.         self.html.end("a")
  578.         if self.visible_links:
  579.             self.html.start("font",color="green")
  580.             self.html.add("text","&lt;%s&gt;"%name)
  581.             self.html.end("font")
  582.  
  583.     def write_target(self,element,stream):
  584.         """Write the target end of a link.
  585.  
  586.        Ultimately, the user should be able to choose to fold these into
  587.        the document (i.e., into the "link" itself).
  588.        """
  589.         try:
  590.             name = element["name"]
  591.         except:
  592.             name = "**no target name**"
  593.  
  594.         ##if not self.in_paragraph:
  595.         ##    self.html.start("p")
  596.  
  597.         # Provide some "debugging" information
  598.         if self.visible_targets:
  599.             self.html.start("font",color="green")
  600.             self.html.start("strong")
  601.             self.html.start("em")
  602.             self.html.add("text",name)
  603.             self.html.end("em")
  604.             self.html.end("strong")
  605.             self.html.end("font")
  606.  
  607.         self.html.add("a",name=self.html.escape(name))
  608.  
  609.         if element.has_key("refuri"):
  610.             uri = self.html.escape(element["refuri"])
  611.             self.html.add("text",":&nbsp;")
  612.             self.html.add("a",element["refuri"],href=uri)
  613.  
  614.         ##if not self.in_paragraph:
  615.         ##    self.html.end("p")
  616.  
  617.     def write_footnote(self,element,stream):
  618.         """Write out the body of a footnote.
  619.  
  620.        It's not entirely clear how to do this in HTML, so we'll
  621.        just try for something distinctive...
  622.        """
  623.         name = auto = index = None
  624.         if element.hasattr("name"):
  625.             name = element["name"]
  626.         if element.hasattr("auto"):
  627.             auto = element["auto"]
  628.         if element.hasattr("auto-index"):
  629.             index = element["auto-index"]
  630.  
  631.         if auto and index == None:
  632.             raise HTMLError,"Footnote auto-numbering has not been done"
  633.  
  634.         if name and auto:
  635.             self.html.start("p")
  636.             self.html.add("a",name=self.html.escape(name))
  637.             self.html.add("text","   ")
  638.             self.html.add("a",name=`index`)
  639.             self.html.end("p")
  640.         elif name:
  641.             self.html.start("p")
  642.             self.html.add("a",name=self.html.escape(name))
  643.             self.html.end("p")
  644.         elif auto:
  645.             self.html.start("p")
  646.             self.html.add("a",name=`index`)
  647.             self.html.end("p")
  648.         else:
  649.             self.write_message(stream,level=2,
  650.                                text="Footnote doesn't have name"
  651.                                " or auto attributes")
  652.  
  653.         self.html.start("table",align="center",width="80%")
  654.         self.html.start("tr")
  655.         self.html.start("td")
  656.  
  657.         # Automatically numbered footnotes don't contain an explicit
  658.         # <label> element, so we have to do it by hand...
  659.         if auto:
  660.             self.write_label(element,stream,index)
  661.  
  662.         for node in element:
  663.             self.write_html(node,stream)
  664.  
  665.         self.html.end("td","tr","table")
  666.  
  667.     def write_label(self,element,stream,index=None):
  668.         """Write out the label for a footnote.
  669.        """
  670.         self.html.add("p",
  671.                       self.html.element("hr"))
  672.  
  673.         self.html.start("p","Footnote ")
  674.         if index == None:
  675.             self.html.add("strong",element.astext())
  676.         else:
  677.             self.html.add("strong",`index`)
  678.         self.html.end("p")
  679.         self.html.add("br")
  680.         self.html.add("hr")
  681.  
  682.     def write_footnote_reference(self,element,stream):
  683.         """Write out the link to a footnote.
  684.        """
  685.         name = auto = None
  686.         if element.hasattr("refname"):
  687.             name = element["refname"]
  688.         if element.hasattr("auto"):
  689.             auto = element["auto"]
  690.  
  691.         if auto:
  692.             if name:
  693.                 if self.auto_footnote_names.has_key(name):
  694.                     number = self.auto_footnote_names[name]
  695.                 else:
  696.                     self.write_message(stream,level=2,
  697.                                        text="Autonumber footnote name"
  698.                                        " '%s' doesn't match a"
  699.                                        " footnote"%name)
  700.                     number = 0
  701.             else:
  702.                 try:
  703.                     number = self.auto_footnote_list[self.auto_footnote_index]
  704.                     self.auto_footnote_index += 1
  705.                 except IndexError:
  706.                     # Hmm - probably a [#]_ with no footnotes for it to
  707.                     # point to - best we can do is write *something* out
  708.                     self.html.start("font",color=self.colours["Error"])
  709.                     self.html.add("text","[")
  710.                     self.html.add("strong","#")
  711.                     self.html.add("text","]")
  712.                     self.html.end("font")
  713.                     return
  714.  
  715.         if auto:
  716.             self.html.start("a",href="#%d"%number)
  717.             self.html.add("text","[")
  718.             self.html.add("strong",`number`)
  719.             self.html.add("text","]")
  720.             self.html.end("a")
  721.         elif name:
  722.             self.html.start("a",href="#%s"%self.html.escape(name))
  723.             self.html.add("text","[")
  724.             self.html.add("strong",element.astext())
  725.             self.html.add("text","]")
  726.             self.html.end("a")
  727.         else:
  728.             self.write_unknown(element,stream)
  729.             return
  730.  
  731.  
  732.     def write_comment(self,element,stream):
  733.         """Write out a comment
  734.        """
  735.         # Technically, this may not be enough, as we don't know what
  736.         # they might *have* in the comment - but in practice, it's
  737.         # likely to do...
  738.         self.html.start("comment")
  739.         for node in element:
  740.             self.write_html(node,stream)
  741.         self.html.end("comment")
  742.  
  743.     def write_paragraph(self,element,stream):
  744.         """Write a new paragraph.
  745.  
  746.        This is a method simply because I do odd things inside
  747.        a field body, to make it "look" better.
  748.        """
  749.         if self.html.last_tag() not in ["li","dd","td","th"]:
  750.             self.html.start("p")
  751.             write_slash_p = 1
  752.         else:
  753.             write_slash_p = 0
  754.  
  755.         for node in element:
  756.             self.write_html(node,stream)
  757.  
  758.         if write_slash_p:
  759.             self.html.end("p")
  760.         return
  761.  
  762.        
  763.         self.in_paragraph = 1
  764.         if self.infield:
  765.             self.write_field_paragraph(element,stream)
  766.         else:
  767.             self.html.start("p")
  768.             for node in element:
  769.                 self.write_html(node,stream)
  770.             self.html.end("p")
  771.         self.in_paragraph = 0
  772.  
  773.     def write_field_paragraph(self,element,stream):
  774.         """Write a new paragraph inside a field body.
  775.        """
  776.         started_row = 0
  777.         if self.paranum > 0:
  778.             self.html.start("tr")
  779.             self.html.add("td")   # an empty column...
  780.             self.html.start("td",dps="paragraph")
  781.             started_row = 1
  782.         self.paranum += 1
  783.         for node in element:
  784.             self.write_html(node,stream)
  785.         if self.paranum > 1:
  786.             self.html.end("td")
  787.         if started_row:
  788.             self.html.end("tr")
  789.  
  790.     def write_table(self,element,stream):
  791.         """Write out a table - initially in a very visible manner
  792.        """
  793.         self.html.start("table",border="1",align="center")
  794.         for node in element:
  795.             self.write_html(node,stream)
  796.         self.html.end("table")
  797.  
  798.     def write_tgroup(self,element,stream):
  799.         for node in element:
  800.             self.write_html(node,stream)
  801.  
  802.     def write_colspec(self,element,stream):
  803.         for node in element:
  804.             self.write_html(node,stream)
  805.  
  806.     def write_rowspec(self,element,stream):
  807.         for node in element:
  808.             self.write_html(node,stream)
  809.  
  810.     def write_thead(self,element,stream):
  811.         """Write out a table header section
  812.        """
  813.         self.html.start("thead")
  814.         self.in_table_header = 1
  815.         for node in element:
  816.             self.write_html(node,stream)
  817.         self.in_table_header = 0
  818.         self.html.end("thead")
  819.  
  820.     def write_tbody(self,element,stream):
  821.         """Write out a table body section
  822.        """
  823.         self.html.start("tbody")
  824.         self.in_table_body = 1
  825.         for node in element:
  826.             self.write_html(node,stream)
  827.         self.in_table_body = 0
  828.         self.html.end("tbody")
  829.  
  830.     def write_row(self,element,stream):
  831.         """Write out a table row.
  832.        """
  833.         if self.in_table_header:
  834.             self.html.start("tr",valign="top",align="left")
  835.         else:
  836.             self.html.start("tr",valign="top")
  837.         for node in element:
  838.             self.write_html(node,stream)
  839.         self.html.end("tr")
  840.  
  841.     def write_entry(self,element,stream):
  842.         """Write out the equivalent of a table "entry" (e.g., HTML <td>)
  843.        """
  844.         keywords = {}
  845.         if element.hasattr("morecols"):
  846.             keywords["colspan"] = element["morecols"]+1
  847.         if element.hasattr("morerows"):
  848.             keywords["rowspan"] = element["morerows"]+1
  849.         keywords["dps"] = "entry"
  850.         self.html.start("td",**keywords)
  851.  
  852.         if self.in_table_header:
  853.             self.html.start("strong")
  854.  
  855.         if len(element) == 0:
  856.             # Since we're writing a table with a border, it looks better if
  857.             # even an empty entry has *some* content - a non-breaking space
  858.             # will suffice to make sure the entry is properly bordered
  859.             self.html.add("text","&nbsp;")
  860.         else:
  861.             for node in element:
  862.                 self.write_html(node,stream)
  863.  
  864.         if self.in_table_header:
  865.             self.html.end("strong")
  866.  
  867.         self.html.end("td")
  868.  
  869.     def write_message(self,stream,level=2,text=None,element=None):
  870.         names = {1: "Information",
  871.                  2: "Warning",
  872.                  3: "Error",
  873.                  4: "Fatal"}
  874.  
  875.         if level == 0 and not self.showinforms:
  876.             return
  877.         elif level == 1 and not self.showwarnings:
  878.             return
  879.  
  880.         try:
  881.             name = names[level]
  882.             colour = self.colours[name]
  883.         except:
  884.             name = "Unrecognised warning level %d"%level
  885.             colour = self.colours["Fatal"]
  886.         bgcolour = self.colours["WarningBG"]
  887.  
  888.         self.html.start("table",width="100%%",bgcolor=bgcolour)
  889.         self.html.start("tr")
  890.         self.html.start("td")
  891.         self.html.start("font",color=colour)
  892.         self.html.add("strong",name)
  893.         self.html.end("font")
  894.         self.html.end("td","tr")
  895.  
  896.         self.html.start("tr")
  897.         self.html.start("td")
  898.         if text:
  899.             self.html.add("text",text)
  900.         if element:
  901.             for node in element:
  902.                 self.write_html(node,stream)
  903.         self.html.end("td","tr")
  904.         self.html.end("table")
  905.  
  906.     def write_problematic(self,element,stream):
  907.         self.html.start("a",href="#%s"%element["refid"])
  908.         self.html.start("font",color="red")
  909.         for node in element:
  910.             self.write_html(node,stream)
  911.         self.html.end("font")
  912.         self.html.end("a")
  913.  
  914.     def write_system_message(self,element,stream):
  915.         try:
  916.             target = element["refid"]
  917.         except:
  918.             target = None
  919.         if target:
  920.             self.html.add("a","",name="%s"%target)
  921.         self.write_message(stream,level=element["level"],element=element)
  922.  
  923.     def write_group(self,element,stream):
  924.         """By default, we don't do anything with <group> tags.
  925.        """
  926.         for node in element:
  927.             self.write_html(node,stream)
  928.  
  929.     def write_emphasis(self,element,stream):
  930.         self.html.start("em")
  931.         for node in element:
  932.             self.write_html(node,stream)
  933.         self.html.end("em")
  934.  
  935.     def write_strong(self,element,stream):
  936.         self.html.start("strong")
  937.         for node in element:
  938.             self.write_html(node,stream)
  939.         self.html.end("strong")
  940.  
  941.     def write_interpreted(self,element,stream):
  942.         if element.hasattr("refname"):
  943.             self.html.start("a",href="#"+self.html.escape(element["refname"]))
  944.             inref = 1
  945.         else:
  946.             inref = 0
  947.  
  948.         self.html.start("samp",style="interpreted")
  949.         if element.hasattr("role"):
  950.             role = element["role"]
  951.             if self.role_text.has_key(role):
  952.                 self.html.add("text","%s "%self.role_text[role])
  953.  
  954.         for node in element:
  955.             self.write_html(node,stream)
  956.         self.html.end("samp")
  957.  
  958.         if inref:
  959.             self.html.end("a")
  960.             if self.visible_links:
  961.                 self.html.start("font",color="green")
  962.                 self.html.add("text","&lt;%s&gt;"%element["refname"])
  963.                 self.html.end("font")
  964.  
  965.     def write_literal(self,element,stream):
  966.         self.html.start("samp",style="literal")
  967.         for node in element:
  968.             self.write_html(node,stream)
  969.         self.html.end("samp")
  970.  
  971.     def write_literal_block(self,element,stream):
  972.         self.html.start("pre")
  973.         for node in element:
  974.             self.write_html(node,stream)
  975.         self.html.end("pre")
  976.  
  977.     def write_doctest_block(self,element,stream):
  978.         self.html.start("pre",style="doctest")
  979.         for node in element:
  980.             self.write_html(node,stream)
  981.         self.html.end("pre")
  982.  
  983.     def write_block_quote(self,element,stream):
  984.         self.html.start("blockquote")
  985.         for node in element:
  986.             self.write_html(node,stream)
  987.         self.html.end("blockquote")
  988.  
  989. # ----------------------------------------------------------------------
  990. class PythonWriter(Writer):
  991.     """A Writer that understands how Python is represented using DOCUTILS.
  992.  
  993.    Note that as a princple, I don't believe that "modes" should add extra
  994.    DOCUTILS nodes - in other words, any DOCUTILS tree should be presentable by
  995.    the default writer (albeit maybe not very well).
  996.    """
  997.  
  998.     python_colours = {"package"   : "Aqua",
  999.                       "module"    : "#FFFF00",
  1000.                       "class"     : "#99CCFF",  # Python blue
  1001.                       "method"    : "#AAFFAA",
  1002.                       "function"  : "#FFAAFF",
  1003.                       "docstring" : "#FFFFCC",  # pale yellow-ish
  1004.                       "generator" : "pink",
  1005.                       }
  1006.  
  1007.     def write_group(self,element,stream):
  1008.         """Write out a group according to its style.
  1009.        """
  1010.  
  1011.         try:
  1012.             style = element["style"]
  1013.         except:
  1014.             style = None
  1015.  
  1016.         if style == "docstring":
  1017.             self.write_docstring(element,stream)
  1018.         elif style == "generator":
  1019.             self.html.start("table",bgcolor=self.python_colours["generator"],
  1020.                             width="100%")
  1021.             self.html.start("tr")
  1022.             self.html.start("td")
  1023.  
  1024.             for node in element:
  1025.                 self.write_html(node,stream)
  1026.  
  1027.             self.html.end("td")
  1028.             self.html.end("tr")
  1029.             self.html.end("table")
  1030.         else:
  1031.             for node in element:
  1032.                 self.write_html(node,stream)
  1033.  
  1034.     def write_docstring(self,element,stream):
  1035.         """Write out a docstring
  1036.        """
  1037.  
  1038.         self.html.start("table",bgcolor=self.python_colours["docstring"],
  1039.                         width="100%")
  1040.         self.html.start("tr")
  1041.         self.html.start("td")
  1042.  
  1043.         # Within a docstring, we want traditional HTML headers,
  1044.         # starting at <h2> and working downwards
  1045.         self.level = 1
  1046.  
  1047.         for node in element:
  1048.             self.write_html(node,stream)
  1049.  
  1050.         self.html.end("td")
  1051.         self.html.end("tr")
  1052.         self.html.end("table")
  1053.  
  1054.     def write_section(self,element,stream):
  1055.         """Write a section - i.e., something with a title
  1056.        """
  1057.  
  1058.         try:
  1059.             style = element["style"]
  1060.         except:
  1061.             style = None
  1062.  
  1063.         if style in ["package","module","class","method","function"]:
  1064.             self.write_py_section(element,stream,style)
  1065.         else:
  1066.             Writer.write_section(self,element,stream)
  1067.  
  1068.     def write_py_section(self,element,stream,style):
  1069.         """Write out the HTML for a Python section.
  1070.        """
  1071.  
  1072.         self.level += 1
  1073.  
  1074.         try:
  1075.             colour = self.python_colours[style]
  1076.         except:
  1077.             colour = self.colours["default"]
  1078.  
  1079.         self.html.start("table",width="100%",cellspacing="0")
  1080.  
  1081.         # First row - full width, coloured
  1082.         self.html.start("tr",bgcolor=colour)
  1083.         self.html.add("td",self.html.element("hr"),colspan="2")
  1084.         self.html.end("tr")
  1085.  
  1086.         # Now, if the section has a <title> and a <group> with
  1087.         # style="details", we want to treat them specially - and
  1088.         # we *know* that they should, if present, be the first
  1089.         # two child elements...
  1090.         # Optional first-and-a-halfth row
  1091.         offset = self.write_py_section_details(element,stream,colour)
  1092.  
  1093.         if len(element.children) > offset:
  1094.             # Second row - left hand margin coloured, "body" not
  1095.             self.html.start("tr")
  1096.             self.html.add("td","&nbsp;",width="1%",bgcolor=colour)
  1097.             self.html.start("td",width="99%")
  1098.  
  1099.             # More detail needed here?
  1100.             for node in element.children[offset:]:
  1101.                 self.write_html(node,stream)
  1102.  
  1103.             self.html.end("td")
  1104.             self.html.end("tr")
  1105.  
  1106.         # Third row - full width coloured
  1107.         self.html.start("tr",bgcolor=colour)
  1108.         self.html.add("td",self.html.element("hr"),colspan="2")
  1109.         self.html.end("tr")
  1110.  
  1111.         if self.got_contents:
  1112.             # Fourth row - full width coloured
  1113.             self.html.start("tr",bgcolor=colour)
  1114.             self.html.add("td","&nbsp;")
  1115.             self.html.start("td",align="right")
  1116.             self.html.add("text","Return to ")
  1117.             self.html.add("a","Contents",href="#contents")
  1118.             self.html.end("td")
  1119.             self.html.end("tr")
  1120.  
  1121.         self.html.end("table")
  1122.  
  1123.         self.level -= 1
  1124.  
  1125.     def write_py_section_details(self,element,stream,colour):
  1126.         """Deal with the title and details for a Python section.
  1127.  
  1128.        Returns the offset within the <section> elements children
  1129.        at which we should continue processing...
  1130.        """
  1131.         offset = 0
  1132.         if element[0].tagname == "title":
  1133.             offset = 1
  1134.             self.html.start("tr",bgcolor=colour)
  1135.             self.html.start("td",colspan="2")
  1136.             self.html.start("table",width="100%")
  1137.             self.html.start("tr",valign="top")
  1138.             self.html.start("td")
  1139.             # Hmm. It doesn't work too well using <h2>..<h6> as header
  1140.             # elements for nested module, class, etc. Let's do this
  1141.             # by "hand"
  1142.             self.html.start("font",size="+2")
  1143.             for node in element[0]:
  1144.                 self.write_html(node,stream)
  1145.             self.html.end("font")
  1146.             self.html.end("td")
  1147.             self.html.end("tr")
  1148.  
  1149.             if element[1].tagname == "group" and \
  1150.                element[1].hasattr("style") and \
  1151.                element[1]["style"] == "details":
  1152.                 offset = 2
  1153.                 self.html.start("tr")
  1154.                 self.html.start("td",align="right")
  1155.                 self.write_html(element[1],stream)
  1156.                 self.html.end("td")
  1157.                 self.html.end("tr")
  1158.             self.html.end("table")
  1159.             self.html.end("td")
  1160.             self.html.end("tr")
  1161.         return offset

Raw Paste


Login or Register to edit or fork this paste. It's free.