epydoc.docwriter.html

Package epydoc :: Package docwriter :: Module html |
[hide private]
[frames] | no frames]

# Source Code for Module epydoc.docwriter.html

       1  #
       2  # epydoc -- HTML output generator
       3  # Edward Loper
       4  #
       5  # Created [01/30/01 05:18 PM]
       6  # $Id: html.py 1674 2008-01-29 06:03:36Z edloper $
       7  #
       8
       9  """
      10  The HTML output generator for epydoc.  The main interface provided by
      11  this module is the L{HTMLWriter} class.
      12
      13  @todo: Add a cache to L{HTMLWriter.url()}?
      14  """
      15  __docformat__ = 'epytext en'
      16
      17  import re, os, sys, codecs, sre_constants, pprint, base64
      18  import urllib
      19  import __builtin__
      20  from epydoc.apidoc import *
      21  import epydoc.docstringparser
      22  import time, epydoc, epydoc.markup, epydoc.markup.epytext
      23  from epydoc.docwriter.html_colorize import PythonSourceColorizer
      24  from epydoc.docwriter import html_colorize
      25  from epydoc.docwriter.html_css import STYLESHEETS
      26  from epydoc.docwriter.html_help import HTML_HELP
      27  from epydoc.docwriter.dotgraph import *
      28  from epydoc import log
      29  from epydoc.util import plaintext_to_html, is_src_filename
      30  from epydoc.compat import * # Backwards compatibility
      31
      32  ######################################################################
      33  ## Template Compiler
      34  ######################################################################
      35  # The compile_template() method defined in this section is used to
      36  # define several of HTMLWriter's methods.
      37
      38 -def compile_template(docstring, template_string,
      39                       output_function='out', debug=epydoc.DEBUG):
      40      """
      41      Given a template string containing inline python source code,
      42      return a python function that will fill in the template, and
      43      output the result.  The signature for this function is taken from
      44      the first line of C{docstring}.  Output is generated by making
      45      repeated calls to the output function with the given name (which
      46      is typically one of the function's parameters).
      47
      48      The templating language used by this function passes through all
      49      text as-is, with three exceptions:
      50
      51        - If every line in the template string is indented by at least
      52          M{x} spaces, then the first M{x} spaces are stripped from each
      53          line.
      54
      55        - Any line that begins with '>>>' (with no indentation)
      56          should contain python code, and will be inserted as-is into
      57          the template-filling function.  If the line begins a control
      58          block (such as 'if' or 'for'), then the control block will
      59          be closed by the first '>>>'-marked line whose indentation is
      60          less than or equal to the line's own indentation (including
      61          lines that only contain comments.)
      62
      63        - In any other line, any expression between two '$' signs will
      64          be evaluated and inserted into the line (using C{str()} to
      65          convert the result to a string).
      66
      67      Here is a simple example:
      68
      69          >>> TEMPLATE = '''
      70          ... <book>
      71          ...   <title>$book.title$</title>
      72          ...   <pages>$book.count_pages()$</pages>
      73          ... >>> for chapter in book.chapters:
      74          ...     <chaptername>$chapter.name$</chaptername>
      75          ... >>> #endfor
      76          ... </book>
      77          >>> write_book = compile_template('write_book(out, book)', TEMPLATE)
      78
      79      @newfield acknowledgements: Acknowledgements
      80      @acknowledgements: The syntax used by C{compile_template} is
      81      loosely based on Cheetah.
      82      """
      83      # Extract signature from the docstring:
      84      signature = docstring.lstrip().split('\n',1)[0].strip()
      85      func_name = signature.split('(',1)[0].strip()
      86
      87      # Regexp to search for inline substitutions:
      88      INLINE = re.compile(r'\$([^\$]+)\$')
      89      # Regexp to search for python statements in the template:
      90      COMMAND = re.compile(r'(^>>>.*)\n?', re.MULTILINE)
      91
      92      # Strip indentation from the template.
      93      template_string = strip_indent(template_string)
      94
      95      # If we're debugging, then we'll store the generated function,
      96      # so we can print it along with any tracebacks that depend on it.
      97      if debug:
      98          signature = re.sub(r'\)\s*$', ', __debug=__debug)', signature)
      99
     100      # Funciton declaration line
     101      pysrc_lines = ['def %s:' % signature]
     102      indents = [-1]
     103
     104      if debug:
     105          pysrc_lines.append('    try:')
     106          indents.append(-1)
     107
     108      commands = COMMAND.split(template_string.strip()+'\n')
     109      for i, command in enumerate(commands):
     110          if command == '': continue
     111
     112          # String literal segment:
     113          if i%2 == 0:
     114              pieces = INLINE.split(command)
     115              for j, piece in enumerate(pieces):
     116                  if j%2 == 0:
     117                      # String piece
     118                      pysrc_lines.append('    '*len(indents)+
     119                                   '%s(%r)' % (output_function, piece))
     120                  else:
     121                      # Variable piece
     122                      pysrc_lines.append('    '*len(indents)+
     123                                   '%s(unicode(%s))' % (output_function, piece))
     124
     125          # Python command:
     126          else:
     127              srcline = command[3:].lstrip()
     128              # Update indentation
     129              indent = len(command)-len(srcline)
     130              while indent <= indents[-1]: indents.pop()
     131              # Add on the line.
     132              srcline = srcline.rstrip()
     133              pysrc_lines.append('    '*len(indents)+srcline)
     134              if srcline.endswith(':'):
     135                  indents.append(indent)
     136
     137      if debug:
     138          pysrc_lines.append('    except Exception,e:')
     139          pysrc_lines.append('        pysrc, func_name = __debug ')
     140          pysrc_lines.append('        lineno = sys.exc_info()[2].tb_lineno')
     141          pysrc_lines.append('        print ("Exception in template %s() on "')
     142          pysrc_lines.append('               "line %d:" % (func_name, lineno))')
     143          pysrc_lines.append('        print pysrc[lineno-1]')
     144          pysrc_lines.append('        raise')
     145
     146      pysrc = '\n'.join(pysrc_lines)+'\n'
     147      #log.debug(pysrc)
     148      if debug: localdict = {'__debug': (pysrc_lines, func_name)}
     149      else: localdict = {}
     150      try: exec pysrc in globals(), localdict
     151      except SyntaxError:
     152          log.error('Error in script:\n' + pysrc + '\n')
     153          raise
     154      template_func = localdict[func_name]
     155      template_func.__doc__ = docstring
     156      return template_func
     157
     158 -def strip_indent(s):
     159      """
     160      Given a multiline string C{s}, find the minimum indentation for
     161      all non-blank lines, and return a new string formed by stripping
     162      that amount of indentation from all lines in C{s}.
     163      """
     164      # Strip indentation from the template.
     165      minindent = sys.maxint
     166      lines = s.split('\n')
     167      for line in lines:
     168          stripline = line.lstrip()
     169          if stripline:
     170              minindent = min(minindent, len(line)-len(stripline))
     171      return '\n'.join([l[minindent:] for l in lines])
     172
     173  ######################################################################
     174  ## HTML Writer
     175  ######################################################################
     176
     177 -class HTMLWriter:
     178      #////////////////////////////////////////////////////////////
     179      # Table of Contents
     180      #////////////////////////////////////////////////////////////
     181      #
     182      # 1. Interface Methods
     183      #
     184      # 2. Page Generation -- write complete web page files
     185      #   2.1. Module Pages
     186      #   2.2. Class Pages
     187      #   2.3. Trees Page
     188      #   2.4. Indices Page
     189      #   2.5. Help Page
     190      #   2.6. Frames-based table of contents pages
     191      #   2.7. Homepage (index.html)
     192      #   2.8. CSS Stylesheet
     193      #   2.9. Javascript file
     194      #   2.10. Graphs
     195      #   2.11. Images
     196      #
     197      # 3. Page Element Generation -- write pieces of a web page file
     198      #   3.1. Page Header
     199      #   3.2. Page Footer
     200      #   3.3. Navigation Bar
     201      #   3.4. Breadcrumbs
     202      #   3.5. Summary Tables
     203      #
     204      # 4. Helper functions
     205
     206 -    def __init__(self, docindex, **kwargs):
     207          """
     208          Construct a new HTML writer, using the given documentation
     209          index.
     210
     211          @param docindex: The documentation index.
     212
     213          @type prj_name: C{string}
     214          @keyword prj_name: The name of the project.  Defaults to
     215                none.
     216          @type prj_url: C{string}
     217          @keyword prj_url: The target for the project hopeage link on
     218                the navigation bar.  If C{prj_url} is not specified,
     219                then no hyperlink is created.
     220          @type prj_link: C{string}
     221          @keyword prj_link: The label for the project link on the
     222                navigation bar.  This link can contain arbitrary HTML
     223                code (e.g. images).  By default, a label is constructed
     224                from C{prj_name}.
     225          @type top_page: C{string}
     226          @keyword top_page: The top page for the documentation.  This
     227                is the default page shown main frame, when frames are
     228                enabled.  C{top} can be a URL, the name of a
     229                module, the name of a class, or one of the special
     230                strings C{"trees.html"}, C{"indices.html"}, or
     231                C{"help.html"}.  By default, the top-level package or
     232                module is used, if there is one; otherwise, C{"trees"}
     233                is used.
     234          @type css: C{string}
     235          @keyword css: The CSS stylesheet file.  If C{css} is a file
     236                name, then the specified file's conents will be used.
     237                Otherwise, if C{css} is the name of a CSS stylesheet in
     238                L{epydoc.docwriter.html_css}, then that stylesheet will
     239                be used.  Otherwise, an error is reported.  If no stylesheet
     240                is specified, then the default stylesheet is used.
     241          @type help_file: C{string}
     242          @keyword help_file: The name of the help file.  If no help file is
     243                specified, then the default help file will be used.
     244          @type show_private: C{boolean}
     245          @keyword show_private: Whether to create documentation for
     246              private objects.  By default, private objects are documented.
     247          @type show_frames: C{boolean})
     248          @keyword show_frames: Whether to create a frames-based table of
     249                contents.  By default, it is produced.
     250          @type show_imports: C{boolean}
     251          @keyword show_imports: Whether or not to display lists of
     252                imported functions and classes.  By default, they are
     253                not shown.
     254          @type variable_maxlines: C{int}
     255          @keyword variable_maxlines: The maximum number of lines that
     256                should be displayed for the value of a variable in the
     257                variable details section.  By default, 8 lines are
     258                displayed.
     259          @type variable_linelength: C{int}
     260          @keyword variable_linelength: The maximum line length used for
     261                displaying the values of variables in the variable
     262                details sections.  If a line is longer than this length,
     263                then it will be wrapped to the next line.  The default
     264                line length is 70 characters.
     265          @type variable_summary_linelength: C{int}
     266          @keyword variable_summary_linelength: The maximum line length
     267                used for displaying the values of variables in the summary
     268                section.  If a line is longer than this length, then it
     269                will be truncated.  The default is 40 characters.
     270          @type variable_tooltip_linelength: C{int}
     271          @keyword variable_tooltip_linelength: The maximum line length
     272                used for tooltips for the values of variables.  If a
     273                line is longer than this length, then it will be
     274                truncated.  The default is 600 characters.
     275          @type property_function_linelength: C{int}
     276          @keyword property_function_linelength: The maximum line length
     277                used to dispaly property functions (C{fget}, C{fset}, and
     278                C{fdel}) that contain something other than a function
     279                object.  The default length is 40 characters.
     280          @type inheritance: C{string}
     281          @keyword inheritance: How inherited objects should be displayed.
     282                If C{inheritance='grouped'}, then inherited objects are
     283                gathered into groups; if C{inheritance='listed'}, then
     284                inherited objects are listed in a short list at the
     285                end of their group; if C{inheritance='included'}, then
     286                inherited objects are mixed in with non-inherited
     287                objects.  The default is 'grouped'.
     288          @type include_source_code: C{boolean}
     289          @keyword include_source_code: If true, then generate colorized
     290                source code files for each python module.
     291          @type include_log: C{boolean}
     292          @keyword include_log: If true, the the footer will include an
     293                href to the page 'epydoc-log.html'.
     294          @type src_code_tab_width: C{int}
     295          @keyword src_code_tab_width: Number of spaces to replace each tab
     296              with in source code listings.
     297          """
     298          self.docindex = docindex
     299
     300          # Process keyword arguments.
     301          self._show_private = kwargs.get('show_private', 1)
     302          """Should private docs be included?"""
     303
     304          self._prj_name = kwargs.get('prj_name', None)
     305          """The project's name (for the project link in the navbar)"""
     306
     307          self._prj_url = kwargs.get('prj_url', None)
     308          """URL for the project link in the navbar"""
     309
     310          self._prj_link = kwargs.get('prj_link', None)
     311          """HTML code for the project link in the navbar"""
     312
     313          self._top_page = kwargs.get('top_page', None)
     314          """The 'main' page"""
     315
     316          self._css = kwargs.get('css')
     317          """CSS stylesheet to use"""
     318
     319          self._helpfile = kwargs.get('help_file', None)
     320          """Filename of file to extract help contents from"""
     321
     322          self._frames_index = kwargs.get('show_frames', 1)
     323          """Should a frames index be created?"""
     324
     325          self._show_imports = kwargs.get('show_imports', False)
     326          """Should imports be listed?"""
     327
     328          self._propfunc_linelen = kwargs.get('property_function_linelength', 40)
     329          """[XXX] Not used!"""
     330
     331          self._variable_maxlines = kwargs.get('variable_maxlines', 8)
     332          """Max lines for variable values"""
     333
     334          self._variable_linelen = kwargs.get('variable_linelength', 70)
     335          """Max line length for variable values"""
     336
     337          self._variable_summary_linelen = \
     338                           kwargs.get('variable_summary_linelength', 65)
     339          """Max length for variable value summaries"""
     340
     341          self._variable_tooltip_linelen = \
     342                           kwargs.get('variable_tooltip_linelength', 600)
     343          """Max length for variable tooltips"""
     344
     345          self._inheritance = kwargs.get('inheritance', 'listed')
     346          """How should inheritance be displayed?  'listed', 'included',
     347          or 'grouped'"""
     348
     349          self._incl_sourcecode = kwargs.get('include_source_code', True)
     350          """Should pages be generated for source code of modules?"""
     351
     352          self._mark_docstrings = kwargs.get('mark_docstrings', False)
     353          """Wrap <span class='docstring'>...</span> around docstrings?"""
     354
     355          self._graph_types = kwargs.get('graphs', ()) or ()
     356          """Graphs that we should include in our output."""
     357
     358          self._include_log = kwargs.get('include_log', False)
     359          """Are we generating an HTML log page?"""
     360
     361          self._src_code_tab_width = kwargs.get('src_code_tab_width', 8)
     362          """Number of spaces to replace each tab with in source code
     363          listings."""
     364
     365          self._callgraph_cache = {}
     366          """Map the callgraph L{uid<DotGraph.uid>} to their HTML
     367          representation."""
     368
     369          self._redundant_details = kwargs.get('redundant_details', False)
     370          """If true, then include objects in the details list even if all
     371          info about them is already provided by the summary table."""
     372
     373          # For use with select_variables():
     374          if self._show_private:
     375              self._public_filter = None
     376          else:
     377              self._public_filter = True
     378
     379          # Make sure inheritance has a sane value.
     380          if self._inheritance not in ('listed', 'included', 'grouped'):
     381              raise ValueError, 'Bad value for inheritance'
     382
     383          # Create the project homepage link, if it was not specified.
     384          if (self._prj_name or self._prj_url) and not self._prj_link:
     385              self._prj_link = plaintext_to_html(self._prj_name or
     386                                                 'Project Homepage')
     387
     388          # Add a hyperlink to _prj_url, if _prj_link doesn't already
     389          # contain any hyperlinks.
     390          if (self._prj_link and self._prj_url and
     391              not re.search(r'<a[^>]*\shref', self._prj_link)):
     392              self._prj_link = ('<a class="navbar" target="_top" href="'+
     393                                self._prj_url+'">'+self._prj_link+'</a>')
     394
     395          # Precompute lists & sets of APIDoc objects that we're
     396          # interested in.
     397          self.valdocs = valdocs = sorted(docindex.reachable_valdocs(
     398              imports=False, packages=False, bases=False, submodules=False,
     399              subclasses=False, private=self._show_private))
     400          self.module_list = [d for d in valdocs if isinstance(d, ModuleDoc)]
     401          """The list of L{ModuleDoc}s for the documented modules."""
     402          self.module_set = set(self.module_list)
     403          """The set of L{ModuleDoc}s for the documented modules."""
     404          self.class_list = [d for d in valdocs if isinstance(d, ClassDoc)]
     405          """The list of L{ClassDoc}s for the documented classes."""
     406          self.class_set = set(self.class_list)
     407          """The set of L{ClassDoc}s for the documented classes."""
     408          self.routine_list = [d for d in valdocs if isinstance(d, RoutineDoc)]
     409          """The list of L{RoutineDoc}s for the documented routines."""
     410          self.indexed_docs = []
     411          """The list of L{APIDoc}s for variables and values that should
     412          be included in the index."""
     413
     414          # URL for 'trees' page
     415          if self.module_list: self._trees_url = 'module-tree.html'
     416          else: self._trees_url = 'class-tree.html'
     417
     418          # Construct the value for self.indexed_docs.
     419          self.indexed_docs += [d for d in valdocs
     420                                if not isinstance(d, GenericValueDoc)]
     421          for doc in valdocs:
     422              if isinstance(doc, NamespaceDoc):
     423                  # add any vars with generic values; but don't include
     424                  # inherited vars.
     425                  self.indexed_docs += [d for d in doc.variables.values() if
     426                                        isinstance(d.value, GenericValueDoc)
     427                                        and d.container == doc]
     428          self.indexed_docs.sort()
     429
     430          # Figure out the url for the top page.
     431          self._top_page_url = self._find_top_page(self._top_page)
     432
     433          # Decide whether or not to split the identifier index.
     434          self._split_ident_index = (len(self.indexed_docs) >=
     435                                     self.SPLIT_IDENT_INDEX_SIZE)
     436
     437          # Figure out how many output files there will be (for progress
     438          # reporting).
     439          self.modules_with_sourcecode = set()
     440          for doc in self.module_list:
     441              if isinstance(doc, ModuleDoc) and is_src_filename(doc.filename):
     442                  self.modules_with_sourcecode.add(doc)
     443          self._num_files = (len(self.class_list) + len(self.module_list) +
     444                             10 + len(self.METADATA_INDICES))
     445          if self._frames_index:
     446              self._num_files += len(self.module_list) + 3
     447
     448          if self._incl_sourcecode:
     449              self._num_files += len(self.modules_with_sourcecode)
     450          if self._split_ident_index:
     451              self._num_files += len(self.LETTERS)
     452
     453 -    def _find_top_page(self, pagename):
     454          """
     455          Find the top page for the API documentation.  This page is
     456          used as the default page shown in the main frame, when frames
     457          are used.  When frames are not used, this page is copied to
     458          C{index.html}.
     459
     460          @param pagename: The name of the page, as specified by the
     461              keyword argument C{top} to the constructor.
     462          @type pagename: C{string}
     463          @return: The URL of the top page.
     464          @rtype: C{string}
     465          """
     466          # If a page name was specified, then we need to figure out
     467          # what it points to.
     468          if pagename:
     469              # If it's a URL, then use it directly.
     470              if pagename.lower().startswith('http:'):
     471                  return pagename
     472
     473              # If it's an object, then use that object's page.
     474              try:
     475                  doc = self.docindex.get_valdoc(pagename)
     476                  return self.url(doc)
     477              except:
     478                  pass
     479
     480              # Otherwise, give up.
     481              log.warning('Could not find top page %r; using %s '
     482                          'instead' % (pagename, self._trees_url))
     483              return self._trees_url
     484
     485          # If no page name was specified, then try to choose one
     486          # automatically.
     487          else:
     488              root = [val_doc for val_doc in self.docindex.root
     489                      if isinstance(val_doc, (ClassDoc, ModuleDoc))]
     490              if len(root) == 0:
     491                  # No docs??  Try the trees page.
     492                  return self._trees_url
     493              elif len(root) == 1:
     494                  # One item in the root; use that.
     495                  return self.url(root[0])
     496              else:
     497                  # Multiple root items; if they're all in one package,
     498                  # then use that.  Otherwise, use self._trees_url
     499                  root = sorted(root, key=lambda v:len(v.canonical_name))
     500                  top = root[0]
     501                  for doc in root[1:]:
     502                      if not top.canonical_name.dominates(doc.canonical_name):
     503                          return self._trees_url
     504                  else:
     505                      return self.url(top)
     506
     507      #////////////////////////////////////////////////////////////
     508      #{ 1. Interface Methods
     509      #////////////////////////////////////////////////////////////
     510
     511 -    def write(self, directory=None):
     512          """
     513          Write the documentation to the given directory.
     514
     515          @type directory: C{string}
     516          @param directory: The directory to which output should be
     517              written.  If no directory is specified, output will be
     518              written to the current directory.  If the directory does
     519              not exist, it will be created.
     520          @rtype: C{None}
     521          @raise OSError: If C{directory} cannot be created.
     522          @raise OSError: If any file cannot be created or written to.
     523          """
     524          # For progress reporting:
     525          self._files_written = 0.
     526
     527          # Set the default values for ValueDoc formatted representations.
     528          orig_valdoc_defaults = (ValueDoc.SUMMARY_REPR_LINELEN,
     529                                  ValueDoc.REPR_LINELEN,
     530                                  ValueDoc.REPR_MAXLINES)
     531          ValueDoc.SUMMARY_REPR_LINELEN = self._variable_summary_linelen
     532          ValueDoc.REPR_LINELEN = self._variable_linelen
     533          ValueDoc.REPR_MAXLINES = self._variable_maxlines
     534
     535          # Use an image for the crarr symbol.
     536          from epydoc.markup.epytext import ParsedEpytextDocstring
     537          orig_crarr_html = ParsedEpytextDocstring.SYMBOL_TO_HTML['crarr']
     538          ParsedEpytextDocstring.SYMBOL_TO_HTML['crarr'] = (
     539              r'<span class="variable-linewrap">'
     540              r'<img src="crarr.png" alt="\" /></span>')
     541
     542          # Keep track of failed xrefs, and report them at the end.
     543          self._failed_xrefs = {}
     544
     545          # Create destination directories, if necessary
     546          if not directory: directory = os.curdir
     547          self._mkdir(directory)
     548          self._directory = directory
     549
     550          # Write the CSS file.
     551          self._files_written += 1
     552          log.progress(self._files_written/self._num_files, 'epydoc.css')
     553          self.write_css(directory, self._css)
     554
     555          # Write the Javascript file.
     556          self._files_written += 1
     557          log.progress(self._files_written/self._num_files, 'epydoc.js')
     558          self.write_javascript(directory)
     559
     560          # Write images
     561          self.write_images(directory)
     562
     563          # Build the indices.
     564          indices = {'ident': self.build_identifier_index(),
     565                     'term': self.build_term_index()}
     566          for (name, label, label2) in self.METADATA_INDICES:
     567              indices[name] = self.build_metadata_index(name)
     568
     569          # Write the identifier index.  If requested, split it into
     570          # separate pages for each letter.
     571          ident_by_letter = self._group_by_letter(indices['ident'])
     572          if not self._split_ident_index:
     573              self._write(self.write_link_index, directory,
     574                          'identifier-index.html', indices,
     575                          'Identifier Index', 'identifier-index.html',
     576                          ident_by_letter)
     577          else:
     578              # Write a page for each section.
     579              for letter in self.LETTERS:
     580                  filename = 'identifier-index-%s.html' % letter
     581                  self._write(self.write_link_index, directory, filename,
     582                              indices, 'Identifier Index', filename,
     583                              ident_by_letter, [letter],
     584                              'identifier-index-%s.html')
     585              # Use the first non-empty section as the main index page.
     586              for letter in self.LETTERS:
     587                  if letter in ident_by_letter:
     588                      filename = 'identifier-index.html'
     589                      self._write(self.write_link_index, directory, filename,
     590                                  indices, 'Identifier Index', filename,
     591                                  ident_by_letter, [letter],
     592                                  'identifier-index-%s.html')
     593                      break
     594
     595          # Write the term index.
     596          if indices['term']:
     597              term_by_letter = self._group_by_letter(indices['term'])
     598              self._write(self.write_link_index, directory, 'term-index.html',
     599                          indices, 'Term Definition Index',
     600                          'term-index.html', term_by_letter)
     601          else:
     602              self._files_written += 1 # (skipped)
     603
     604          # Write the metadata indices.
     605          for (name, label, label2) in self.METADATA_INDICES:
     606              if indices[name]:
     607                  self._write(self.write_metadata_index, directory,
     608                              '%s-index.html' % name, indices, name,
     609                              label, label2)
     610              else:
     611                  self._files_written += 1 # (skipped)
     612
     613          # Write the trees file (package & class hierarchies)
     614          if self.module_list:
     615              self._write(self.write_module_tree, directory, 'module-tree.html')
     616          else:
     617              self._files_written += 1 # (skipped)
     618          if self.class_list:
     619              self._write(self.write_class_tree, directory, 'class-tree.html')
     620          else:
     621              self._files_written += 1 # (skipped)
     622
     623          # Write the help file.
     624          self._write(self.write_help, directory,'help.html')
     625
     626          # Write the frames-based table of contents.
     627          if self._frames_index:
     628              self._write(self.write_frames_index, directory, 'frames.html')
     629              self._write(self.write_toc, directory, 'toc.html')
     630              self._write(self.write_project_toc, directory, 'toc-everything.html')
     631              for doc in self.module_list:
     632                  filename = 'toc-%s' % urllib.unquote(self.url(doc))
     633                  self._write(self.write_module_toc, directory, filename, doc)
     634
     635          # Write the object documentation.
     636          for doc in self.module_list:
     637              filename = urllib.unquote(self.url(doc))
     638              self._write(self.write_module, directory, filename, doc)
     639          for doc in self.class_list:
     640              filename = urllib.unquote(self.url(doc))
     641              self._write(self.write_class, directory, filename, doc)
     642
     643          # Write source code files.
     644          if self._incl_sourcecode:
     645              # Build a map from short names to APIDocs, used when
     646              # linking names in the source code.
     647              name_to_docs = {}
     648              for api_doc in self.indexed_docs:
     649                  if (api_doc.canonical_name is not None and
     650                      self.url(api_doc) is not None):
     651                      name = api_doc.canonical_name[-1]
     652                      name_to_docs.setdefault(name, []).append(api_doc)
     653              # Sort each entry of the name_to_docs list.
     654              for doc_list in name_to_docs.values():
     655                  doc_list.sort()
     656              # Write the source code for each module.
     657              for doc in self.modules_with_sourcecode:
     658                  filename = urllib.unquote(self.pysrc_url(doc))
     659                  self._write(self.write_sourcecode, directory, filename, doc,
     660                              name_to_docs)
     661
     662          # Write the auto-redirect page.
     663          self._write(self.write_redirect_page, directory, 'redirect.html')
     664
     665          # Write the mapping object name -> URL
     666          self._write(self.write_api_list, directory, 'api-objects.txt')
     667
     668          # Write the index.html files.
     669          # (this must be done last, since it might copy another file)
     670          self._files_written += 1
     671          log.progress(self._files_written/self._num_files, 'index.html')
     672          self.write_homepage(directory)
     673
     674          # Don't report references to builtins as missing
     675          for k in self._failed_xrefs.keys(): # have a copy of keys
     676              if hasattr(__builtin__, k):
     677                  del self._failed_xrefs[k]
     678
     679          # Report any failed crossreferences
     680          if self._failed_xrefs:
     681              estr = 'Failed identifier crossreference targets:\n'
     682              failed_identifiers = self._failed_xrefs.keys()
     683              failed_identifiers.sort()
     684              for identifier in failed_identifiers:
     685                  names = self._failed_xrefs[identifier].keys()
     686                  names.sort()
     687                  estr += '- %s' % identifier
     688                  estr += '\n'
     689                  for name in names:
     690                      estr += '      (from %s)\n' % name
     691              log.docstring_warning(estr)
     692
     693          # [xx] testing:
     694          if self._num_files != int(self._files_written):
     695              log.debug("Expected to write %d files, but actually "
     696                        "wrote %d files" %
     697                        (self._num_files, int(self._files_written)))
     698
     699          # Restore defaults that we changed.
     700          (ValueDoc.SUMMARY_REPR_LINELEN, ValueDoc.REPR_LINELEN,
     701           ValueDoc.REPR_MAXLINES) = orig_valdoc_defaults
     702          ParsedEpytextDocstring.SYMBOL_TO_HTML['crarr'] = orig_crarr_html
     703
     704 -    def _write(self, write_func, directory, filename, *args):
     705          # Display our progress.
     706          self._files_written += 1
     707          log.progress(self._files_written/self._num_files, filename)
     708
     709          path = os.path.join(directory, filename)
     710          f = codecs.open(path, 'w', 'ascii', errors='xmlcharrefreplace')
     711          write_func(f.write, *args)
     712          f.close()
     713
     714 -    def _mkdir(self, directory):
     715          """
     716          If the given directory does not exist, then attempt to create it.
     717          @rtype: C{None}
     718          """
     719          if not os.path.isdir(directory):
     720              if os.path.exists(directory):
     721                  raise OSError('%r is not a directory' % directory)
     722              os.mkdir(directory)
     723
     724      #////////////////////////////////////////////////////////////
     725      #{ 2.1. Module Pages
     726      #////////////////////////////////////////////////////////////
     727
     728 -    def write_module(self, out, doc):
     729          """
     730          Write an HTML page containing the API documentation for the
     731          given module to C{out}.
     732
     733          @param doc: A L{ModuleDoc} containing the API documentation
     734          for the module that should be described.
     735          """
     736          longname = doc.canonical_name
     737          shortname = doc.canonical_name[-1]
     738
     739          # Write the page header (incl. navigation bar & breadcrumbs)
     740          self.write_header(out, str(longname))
     741          self.write_navbar(out, doc)
     742          self.write_breadcrumbs(out, doc, self.url(doc))
     743
     744          # Write the name of the module we're describing.
     745          if doc.is_package is True: typ = 'Package'
     746          else: typ = 'Module'
     747          if longname[0].startswith('script-'):
     748              shortname = str(longname)[7:]
     749              typ = 'Script'
     750          out('<!-- ==================== %s ' % typ.upper() +
     751              'DESCRIPTION ==================== -->\n')
     752          out('<h1 class="epydoc">%s %s</h1>' % (typ, shortname))
     753          out('<p class="nomargin-top">%s</p>\n' % self.pysrc_link(doc))
     754
     755          # If the module has a description, then list it.
     756          if doc.descr not in (None, UNKNOWN):
     757              out(self.descr(doc, 2)+'\n\n')
     758
     759          # Write any standarad metadata (todo, author, etc.)
     760          if doc.metadata is not UNKNOWN and doc.metadata:
     761              out('<hr />\n')
     762          self.write_standard_fields(out, doc)
     763
     764          # If it's a package, then list the modules it contains.
     765          if doc.is_package is True:
     766              self.write_module_list(out, doc)
     767
     768          # Write summary tables describing the variables that the
     769          # module defines.
     770          self.write_summary_table(out, "Classes", doc, "class")
     771          self.write_summary_table(out, "Functions", doc, "function")
     772          self.write_summary_table(out, "Variables", doc, "other")
     773
     774          # Write a list of all imported objects.
     775          if self._show_imports:
     776              self.write_imports(out, doc)
     777
     778          # Write detailed descriptions of functions & variables defined
     779          # in this module.
     780          self.write_details_list(out, "Function Details", doc, "function")
     781          self.write_details_list(out, "Variables Details", doc, "other")
     782
     783          # Write the page footer (including navigation bar)
     784          self.write_navbar(out, doc)
     785          self.write_footer(out)
     786
     787      #////////////////////////////////////////////////////////////
     788      #{ 2.??. Source Code Pages
     789      #////////////////////////////////////////////////////////////
     790
     791 -    def write_sourcecode(self, out, doc, name_to_docs):
     792          #t0 = time.time()
     793
     794          filename = doc.filename
     795          name = str(doc.canonical_name)
     796
     797          # Header
     798          self.write_header(out, name)
     799          self.write_navbar(out, doc)
     800          self.write_breadcrumbs(out, doc, self.pysrc_url(doc))
     801
     802          # Source code listing
     803          out('<h1 class="epydoc">Source Code for %s</h1>\n' %
     804              self.href(doc, label='%s %s' % (self.doc_kind(doc), name)))
     805          out('<pre class="py-src">\n')
     806          out(PythonSourceColorizer(filename, name, self.docindex,
     807                                    self.url, name_to_docs,
     808                                    self._src_code_tab_width).colorize())
     809          out('</pre>\n<br />\n')
     810
     811          # Footer
     812          self.write_navbar(out, doc)
     813          self.write_footer(out)
     814
     815          #log.debug('[%6.2f sec] Wrote pysrc for %s' %
     816          #          (time.time()-t0, name))
     817
     818      #////////////////////////////////////////////////////////////
     819      #{ 2.2. Class Pages
     820      #////////////////////////////////////////////////////////////
     821
     822 -    def write_class(self, out, doc):
     823          """
     824          Write an HTML page containing the API documentation for the
     825          given class to C{out}.
     826
     827          @param doc: A L{ClassDoc} containing the API documentation
     828          for the class that should be described.
     829          """
     830          longname = doc.canonical_name
     831          shortname = doc.canonical_name[-1]
     832
     833          # Write the page header (incl. navigation bar & breadcrumbs)
     834          self.write_header(out, str(longname))
     835          self.write_navbar(out, doc)
     836          self.write_breadcrumbs(out, doc, self.url(doc))
     837
     838          # Write the name of the class we're describing.
     839          if doc.is_type(): typ = 'Type'
     840          elif doc.is_exception(): typ = 'Exception'
     841          else: typ = 'Class'
     842          out('<!-- ==================== %s ' % typ.upper() +
     843              'DESCRIPTION ==================== -->\n')
     844          out('<h1 class="epydoc">%s %s</h1>' % (typ, shortname))
     845          out('<p class="nomargin-top">%s</p>\n' % self.pysrc_link(doc))
     846
     847          if ((doc.bases not in (UNKNOWN, None) and len(doc.bases) > 0) or
     848              (doc.subclasses not in (UNKNOWN,None) and len(doc.subclasses)>0)):
     849              # Display bases graphically, if requested.
     850              if 'umlclasstree' in self._graph_types:
     851                  self.write_class_tree_graph(out, doc, uml_class_tree_graph)
     852              elif 'classtree' in self._graph_types:
     853                  self.write_class_tree_graph(out, doc, class_tree_graph)
     854
     855              # Otherwise, use ascii-art.
     856              else:
     857                  # Write the base class tree.
     858                  if doc.bases not in (UNKNOWN, None) and len(doc.bases) > 0:
     859                      out('<pre class="base-tree">\n%s</pre>\n\n' %
     860                          self.base_tree(doc))
     861
     862                  # Write the known subclasses
     863                  if (doc.subclasses not in (UNKNOWN, None) and
     864                      len(doc.subclasses) > 0):
     865                      out('<dl><dt>Known Subclasses:</dt>\n<dd>\n    ')
     866                      out('  <ul class="subclass-list">\n')
     867                      for i, subclass in enumerate(doc.subclasses):
     868                          href = self.href(subclass, context=doc)
     869                          if self._val_is_public(subclass): css = ''
     870                          else: css = ' class="private"'
     871                          if i > 0: href = ', '+href
     872                          out('<li%s>%s</li>' % (css, href))
     873                      out('  </ul>\n')
     874                      out('</dd></dl>\n\n')
     875
     876              out('<hr />\n')
     877
     878          # If the class has a description, then list it.
     879          if doc.descr not in (None, UNKNOWN):
     880              out(self.descr(doc, 2)+'\n\n')
     881
     882          # Write any standarad metadata (todo, author, etc.)
     883          if doc.metadata is not UNKNOWN and doc.metadata:
     884              out('<hr />\n')
     885          self.write_standard_fields(out, doc)
     886
     887          # Write summary tables describing the variables that the
     888          # class defines.
     889          self.write_summary_table(out, "Nested Classes", doc, "class")
     890          self.write_summary_table(out, "Instance Methods", doc,
     891                                   "instancemethod")
     892          self.write_summary_table(out, "Class Methods", doc, "classmethod")
     893          self.write_summary_table(out, "Static Methods", doc, "staticmethod")
     894          self.write_summary_table(out, "Class Variables", doc,
     895                                   "classvariable")
     896          self.write_summary_table(out, "Instance Variables", doc,
     897                                   "instancevariable")
     898          self.write_summary_table(out, "Properties", doc, "property")
     899
     900          # Write a list of all imported objects.
     901          if self._show_imports:
     902              self.write_imports(out, doc)
     903
     904          # Write detailed descriptions of functions & variables defined
     905          # in this class.
     906          # [xx] why group methods into one section but split vars into two?
     907          # seems like we should either group in both cases or split in both
     908          # cases.
     909          self.write_details_list(out, "Method Details", doc, "method")
     910          self.write_details_list(out, "Class Variable Details", doc,
     911                                  "classvariable")
     912          self.write_details_list(out, "Instance Variable Details", doc,
     913                                  "instancevariable")
     914          self.write_details_list(out, "Property Details", doc, "property")
     915
     916          # Write the page footer (including navigation bar)
     917          self.write_navbar(out, doc)
     918          self.write_footer(out)
     919
     920 -    def write_class_tree_graph(self, out, doc, graphmaker):
     921          """
     922          Write HTML code for a class tree graph of C{doc} (a classdoc),
     923          using C{graphmaker} to draw the actual graph.  C{graphmaker}
     924          should be L{class_tree_graph()}, or L{uml_class_tree_graph()},
     925          or any other function with a compatible signature.
     926
     927          If the given class has any private sublcasses (including
     928          recursive subclasses), then two graph images will be generated
     929          -- one to display when private values are shown, and the other
     930          to display when private values are hidden.
     931          """
     932          linker = _HTMLDocstringLinker(self, doc)
     933          private_subcls = self._private_subclasses(doc)
     934          if private_subcls:
     935              out('<center>\n'
     936                  '  <div class="private">%s</div>\n'
     937                  '  <div class="public" style="display:none">%s</div>\n'
     938                  '</center>\n' %
     939                  (self.render_graph(graphmaker(doc, linker, doc)),
     940                   self.render_graph(graphmaker(doc, linker, doc,
     941                                                exclude=private_subcls))))
     942          else:
     943              out('<center>\n%s\n</center>\n' %
     944                  self.render_graph(graphmaker(doc, linker, doc)))
     945
     946      #////////////////////////////////////////////////////////////
     947      #{ 2.3. Trees pages
     948      #////////////////////////////////////////////////////////////
     949
     950 -    def write_module_tree(self, out):
     951          # Header material
     952          self.write_treepage_header(out, 'Module Hierarchy', 'module-tree.html')
     953          out('<h1 class="epydoc">Module Hierarchy</h1>\n')
     954
     955          # Write entries for all top-level modules/packages.
     956          out('<ul class="nomargin-top">\n')
     957          for doc in self.module_list:
     958              if (doc.package in (None, UNKNOWN) or
     959                  doc.package not in self.module_set):
     960                  self.write_module_tree_item(out, doc)
     961          out('</ul>\n')
     962
     963          # Footer material
     964          self.write_navbar(out, 'trees')
     965          self.write_footer(out)
     966
     967 -    def write_class_tree(self, out):
     968          """
     969          Write HTML code for a nested list showing the base/subclass
     970          relationships between all documented classes.  Each element of
     971          the top-level list is a class with no (documented) bases; and
     972          under each class is listed all of its subclasses.  Note that
     973          in the case of multiple inheritance, a class may appear
     974          multiple times.
     975
     976          @todo: For multiple inheritance, don't repeat subclasses the
     977              second time a class is mentioned; instead, link to the
     978              first mention.
     979          """
     980          # [XX] backref for multiple inheritance?
     981          # Header material
     982          self.write_treepage_header(out, 'Class Hierarchy', 'class-tree.html')
     983          out('<h1 class="epydoc">Class Hierarchy</h1>\n')
     984
     985          # Build a set containing all classes that we should list.
     986          # This includes everything in class_list, plus any of those
     987          # class' bases, but not undocumented subclasses.
     988          class_set = self.class_set.copy()
     989          for doc in self.class_list:
     990              if doc.bases != UNKNOWN:
     991                  for base in doc.bases:
     992                      if base not in class_set:
     993                          if isinstance(base, ClassDoc):
     994                              class_set.update(base.mro())
     995                          else:
     996                              # [XX] need to deal with this -- how?
     997                              pass
     998                              #class_set.add(base)
     999
    1000          out('<ul class="nomargin-top">\n')
    1001          for doc in sorted(class_set, key=lambda c:c.canonical_name[-1]):
    1002              if doc.bases != UNKNOWN and len(doc.bases)==0:
    1003                  self.write_class_tree_item(out, doc, class_set)
    1004          out('</ul>\n')
    1005
    1006          # Footer material
    1007          self.write_navbar(out, 'trees')
    1008          self.write_footer(out)
    1009
    1010 -    def write_treepage_header(self, out, title, url):
    1011          # Header material.
    1012          self.write_header(out, title)
    1013          self.write_navbar(out, 'trees')
    1014          self.write_breadcrumbs(out, 'trees', url)
    1015          if self.class_list and self.module_list:
    1016              out('<center><b>\n')
    1017              out(' [ <a href="module-tree.html">Module Hierarchy</a>\n')
    1018              out(' | <a href="class-tree.html">Class Hierarchy</a> ]\n')
    1019              out('</b></center><br />\n')
    1020
    1021
    1022      #////////////////////////////////////////////////////////////
    1023      #{ 2.4. Index pages
    1024      #////////////////////////////////////////////////////////////
    1025
    1026      SPLIT_IDENT_INDEX_SIZE = 3000
    1027      """If the identifier index has more than this number of entries,
    1028      then it will be split into separate pages, one for each
    1029      alphabetical section."""
    1030
    1031      LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ_'
    1032      """The alphabetical sections that are used for link index pages."""
    1033
    1034 -    def write_link_index(self, out, indices, title, url, index_by_section,
    1035                           sections=LETTERS, section_url='#%s'):
    1036
    1037          # Header
    1038          self.write_indexpage_header(out, indices, title, url)
    1039
    1040          # Index title & links to alphabetical sections.
    1041          out('<table border="0" width="100%">\n'
    1042              '<tr valign="bottom"><td>\n')
    1043          out('<h1 class="epydoc">%s</h1>\n</td><td>\n[\n' % title)
    1044          for sec in self.LETTERS:
    1045              if sec in index_by_section:
    1046                  out(' <a href="%s">%s</a>\n' % (section_url % sec, sec))
    1047              else:
    1048                  out('  %s\n' % sec)
    1049          out(']\n')
    1050          out('</td></table>\n')
    1051
    1052          # Alphabetical sections.
    1053          sections = [s for s in sections if s in index_by_section]
    1054          if sections:
    1055              out('<table border="0" width="100%">\n')
    1056              for section in sorted(sections):
    1057                  out('<tr valign="top"><td valign="top" width="1%">')
    1058                  out('<h2 class="epydoc"><a name="%s">%s</a></h2></td>\n' %
    1059                      (section, section))
    1060                  out('<td valign="top">\n')
    1061                  self.write_index_section(out, index_by_section[section], True)
    1062                  out('</td></tr>\n')
    1063              out('</table>\n<br />')
    1064
    1065          # Footer material.
    1066          out('<br />')
    1067          self.write_navbar(out, 'indices')
    1068          self.write_footer(out)
    1069
    1070
    1071 -    def write_metadata_index(self, out, indices, field, title, typ):
    1072          """
    1073          Write an HTML page containing a metadata index.
    1074          """
    1075          index = indices[field]
    1076
    1077          # Header material.
    1078          self.write_indexpage_header(out, indices, title,
    1079                                      '%s-index.html' % field)
    1080
    1081          # Page title.
    1082          out('<h1 class="epydoc"><a name="%s">%s</a></h1>\n<br />\n' %
    1083              (field, title))
    1084
    1085          # Index (one section per arg)
    1086          for arg in sorted(index):
    1087              # Write a section title.
    1088              if arg is not None:
    1089                  if len([1 for (doc, descrs) in index[arg] if
    1090                          not self._doc_or_ancestor_is_private(doc)]) == 0:
    1091                      out('<div class="private">')
    1092                  else:
    1093                      out('<div>')
    1094                  self.write_table_header(out, 'metadata-index', arg)
    1095                  out('</table>')
    1096              # List every descr for this arg.
    1097              for (doc, descrs) in index[arg]:
    1098                  if self._doc_or_ancestor_is_private(doc):
    1099                      out('<div class="private">\n')
    1100                  else:
    1101                      out('<div>\n')
    1102                  out('<table width="100%" class="metadata-index" '
    1103                      'bgcolor="#e0e0e0"><tr><td class="metadata-index">')
    1104                  out('<b>%s in %s</b>' %
    1105                      (typ, self.href(doc, label=doc.canonical_name)))
    1106                  out('    <ul class="nomargin">\n')
    1107                  for descr in descrs:
    1108                      out('      <li>%s</li>\n' %
    1109                          self.docstring_to_html(descr,doc,4))
    1110                  out('    </ul>\n')
    1111                  out('</table></div>\n')
    1112
    1113          # Footer material.
    1114          out('<br />')
    1115          self.write_navbar(out, 'indices')
    1116          self.write_footer(out)
    1117
    1118 -    def write_indexpage_header(self, out, indices, title, url):
    1119          """
    1120          A helper for the index page generation functions, which
    1121          generates a header that can be used to navigate between the
    1122          different indices.
    1123          """
    1124          self.write_header(out, title)
    1125          self.write_navbar(out, 'indices')
    1126          self.write_breadcrumbs(out, 'indices', url)
    1127
    1128          if (indices['term'] or
    1129              [1 for (name,l,l2) in self.METADATA_INDICES if indices[name]]):
    1130              out('<center><b>[\n')
    1131              out(' <a href="identifier-index.html">Identifiers</a>\n')
    1132              if indices['term']:
    1133                  out('| <a href="term-index.html">Term Definitions</a>\n')
    1134              for (name, label, label2) in self.METADATA_INDICES:
    1135                  if indices[name]:
    1136                      out('| <a href="%s-index.html">%s</a>\n' %
    1137                          (name, label2))
    1138              out(']</b></center><br />\n')
    1139
    1140 -    def write_index_section(self, out, items, add_blankline=False):
    1141          out('<table class="link-index" width="100%" border="1">\n')
    1142          num_rows = (len(items)+2)/3
    1143          for row in range(num_rows):
    1144              out('<tr>\n')
    1145              for col in range(3):
    1146                  out('<td width="33%" class="link-index">')
    1147                  i = col*num_rows+row
    1148                  if i < len(items):
    1149                      name, url, container = items[col*num_rows+row]
    1150                      out('<a href="%s">%s</a>' % (url, name))
    1151                      if container is not None:
    1152                          out('<br />\n')
    1153                          if isinstance(container, ModuleDoc):
    1154                              label = container.canonical_name
    1155                          else:
    1156                              label = container.canonical_name[-1]
    1157                          out('<span class="index-where">(in&nbsp;%s)'
    1158                              '</span>' % self.href(container, label))
    1159                  else:
    1160                      out('&nbsp;')
    1161                  out('</td>\n')
    1162              out('</tr>\n')
    1163              if add_blankline and num_rows == 1:
    1164                  blank_cell = '<td class="link-index">&nbsp;</td>'
    1165                  out('<tr>'+3*blank_cell+'</tr>\n')
    1166          out('</table>\n')
    1167
    1168      #////////////////////////////////////////////////////////////
    1169      #{ 2.5. Help Page
    1170      #////////////////////////////////////////////////////////////
    1171
    1172 -    def write_help(self, out):
    1173          """
    1174          Write an HTML help file to the given stream.  If
    1175          C{self._helpfile} contains a help file, then use it;
    1176          otherwise, use the default helpfile from
    1177          L{epydoc.docwriter.html_help}.
    1178          """
    1179          # todo: optionally parse .rst etc help files?
    1180
    1181          # Get the contents of the help file.
    1182          if self._helpfile:
    1183              if os.path.exists(self._helpfile):
    1184                  try: help = open(self._helpfile).read()
    1185                  except: raise IOError("Can't open help file: %r" %
    1186                                        self._helpfile)
    1187              else:
    1188                  raise IOError("Can't find help file: %r" % self._helpfile)
    1189          else:
    1190              if self._prj_name: thisprj = self._prj_name
    1191              else: thisprj = 'this project'
    1192              help = HTML_HELP % {'this_project':thisprj}
    1193
    1194          # Insert the help contents into a webpage.
    1195          self.write_header(out, 'Help')
    1196          self.write_navbar(out, 'help')
    1197          self.write_breadcrumbs(out, 'help', 'help.html')
    1198          out(help)
    1199          self.write_navbar(out, 'help')
    1200          self.write_footer(out)
    1201
    1202      #////////////////////////////////////////////////////////////
    1203      #{ 2.6. Frames-based Table of Contents
    1204      #////////////////////////////////////////////////////////////
    1205
    1206      write_frames_index = compile_template(
    1207          """
    1208          write_frames_index(self, out)
    1209
    1210          Write the frames index file for the frames-based table of
    1211          contents to the given streams.
    1212          """,
    1213          # /------------------------- Template -------------------------\
    1214          '''
    1215          <?xml version="1.0" encoding="iso-8859-1"?>
    1216          <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
    1217                    "DTD/xhtml1-frameset.dtd">
    1218          <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    1219          <head>
    1220            <title> $self._prj_name or "API Documentation"$ </title>
    1221          </head>
    1222          <frameset cols="20%,80%">
    1223            <frameset rows="30%,70%">
    1224              <frame src="toc.html" name="moduleListFrame"
    1225                     id="moduleListFrame" />
    1226              <frame src="toc-everything.html" name="moduleFrame"
    1227                     id="moduleFrame" />
    1228            </frameset>
    1229            <frame src="$self._top_page_url$" name="mainFrame" id="mainFrame" />
    1230          </frameset>
    1231          </html>
    1232          ''')
    1233          # \------------------------------------------------------------/
    1234
    1235      write_toc = compile_template(
    1236          """
    1237          write_toc(self, out)
    1238          """,
    1239          # /------------------------- Template -------------------------\
    1240          '''
    1241          >>> self.write_header(out, "Table of Contents")
    1242          <h1 class="toc">Table&nbsp;of&nbsp;Contents</h1>
    1243          <hr />
    1244            <a target="moduleFrame" href="toc-everything.html">Everything</a>
    1245            <br />
    1246          >>> self.write_toc_section(out, "Modules", self.module_list)
    1247          <hr />
    1248          >>> if self._show_private:
    1249            $self.PRIVATE_LINK$
    1250          >>> #endif
    1251          >>> self.write_footer(out, short=True)
    1252          ''')
    1253          # \------------------------------------------------------------/
    1254
    1283
    1311
    1345
    1346      #////////////////////////////////////////////////////////////
    1347      #{ 2.7. Project homepage (index.html)
    1348      #////////////////////////////////////////////////////////////
    1349
    1350 -    def write_homepage(self, directory):
    1351          """
    1352          Write an C{index.html} file in the given directory.  The
    1353          contents of this file are copied or linked from an existing
    1354          page, so this method must be called after all pages have been
    1355          written.  The page used is determined by L{_frames_index} and
    1356          L{_top_page}:
    1357              - If L{_frames_index} is true, then C{frames.html} is
    1358                copied.
    1359              - Otherwise, the page specified by L{_top_page} is
    1360                copied.
    1361          """
    1362          filename = os.path.join(directory, 'index.html')
    1363          if self._frames_index: top = 'frames.html'
    1364          else: top = self._top_page_url
    1365
    1366          # Copy the non-frames index file from top, if it's internal.
    1367          if top[:5] != 'http:' and '/' not in top:
    1368              try:
    1369                  # Read top into `s`.
    1370                  topfile = os.path.join(directory, top)
    1371                  s = open(topfile, 'r').read()
    1372
    1373                  # Write the output file.
    1374                  open(filename, 'w').write(s)
    1375                  return
    1376              except:
    1377                  log.error('Warning: error copying index; '
    1378                            'using a redirect page')
    1379
    1380          # Use a redirect if top is external, or if we faild to copy.
    1381          name = self._prj_name or 'this project'
    1382          f = open(filename, 'w')
    1383          self.write_redirect_index(f.write, top, name)
    1384          f.close()
    1385
    1386      write_redirect_index = compile_template(
    1387          """
    1388          write_redirect_index(self, out, top, name)
    1389          """,
    1390          # /------------------------- Template -------------------------\
    1391          '''
    1392          <?xml version="1.0" encoding="iso-8859-1"?>
    1393          <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    1394                    "DTD/xhtml1-strict.dtd">
    1395          <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    1396          <head>
    1397            <title> Redirect </title>
    1398            <meta http-equiv="refresh" content="1;url=$top$" />
    1399            <link rel="stylesheet" href="epydoc.css" type="text/css"></link>
    1400          </head>
    1401          <body>
    1402            <p>Redirecting to the API documentation for
    1403              <a href="$top$">$self._prj_name or "this project"$</a>...</p>
    1404          </body>
    1405          </html>
    1406          ''')
    1407          # \------------------------------------------------------------/
    1408
    1409      #////////////////////////////////////////////////////////////
    1410      #{ 2.8. Stylesheet (epydoc.css)
    1411      #////////////////////////////////////////////////////////////
    1412
    1413 -    def write_css(self, directory, cssname):
    1414          """
    1415          Write the CSS stylesheet in the given directory.  If
    1416          C{cssname} contains a stylesheet file or name (from
    1417          L{epydoc.docwriter.html_css}), then use that stylesheet;
    1418          otherwise, use the default stylesheet.
    1419
    1420          @rtype: C{None}
    1421          """
    1422          filename = os.path.join(directory, 'epydoc.css')
    1423
    1424          # Get the contents for the stylesheet file.
    1425          if cssname is None:
    1426              css = STYLESHEETS['default'][0]
    1427          else:
    1428              if os.path.exists(cssname):
    1429                  try: css = open(cssname).read()
    1430                  except: raise IOError("Can't open CSS file: %r" % cssname)
    1431              elif cssname in STYLESHEETS:
    1432                  css = STYLESHEETS[cssname][0]
    1433              else:
    1434                  raise IOError("Can't find CSS file: %r" % cssname)
    1435
    1436          # Write the stylesheet.
    1437          cssfile = open(filename, 'w')
    1438          cssfile.write(css)
    1439          cssfile.close()
    1440
    1441      #////////////////////////////////////////////////////////////
    1442      #{ 2.9. Javascript (epydoc.js)
    1443      #////////////////////////////////////////////////////////////
    1444
    1445 -    def write_javascript(self, directory):
    1446          jsfile = open(os.path.join(directory, 'epydoc.js'), 'w')
    1447          print >> jsfile, self.TOGGLE_PRIVATE_JS
    1448          print >> jsfile, self.SHOW_PRIVATE_JS
    1449          print >> jsfile, self.GET_COOKIE_JS
    1450          print >> jsfile, self.SET_FRAME_JS
    1451          print >> jsfile, self.HIDE_PRIVATE_JS
    1452          print >> jsfile, self.TOGGLE_CALLGRAPH_JS
    1453          print >> jsfile, html_colorize.PYSRC_JAVASCRIPTS
    1454          print >> jsfile, self.GET_ANCHOR_JS
    1455          print >> jsfile, self.REDIRECT_URL_JS
    1456          jsfile.close()
    1457
    1458      #: A javascript that is used to show or hide the API documentation
    1459      #: for private objects.  In order for this to work correctly, all
    1460      #: documentation for private objects should be enclosed in
    1461      #: C{<div class="private">...</div>} elements.
    1462      TOGGLE_PRIVATE_JS = '''
    1463        function toggle_private() {
    1464          // Search for any private/public links on this page.  Store
    1465          // their old text in "cmd," so we will know what action to
    1466          // take; and change their text to the opposite action.
    1467          var cmd = "?";
    1468          var elts = document.getElementsByTagName("a");
    1469          for(var i=0; i<elts.length; i++) {
    1470            if (elts[i].className == "privatelink") {
    1471              cmd = elts[i].innerHTML;
    1472              elts[i].innerHTML = ((cmd && cmd.substr(0,4)=="show")?
    1473                                      "hide&nbsp;private":"show&nbsp;private");
    1474            }
    1475          }
    1476          // Update all DIVs containing private objects.
    1477          var elts = document.getElementsByTagName("div");
    1478          for(var i=0; i<elts.length; i++) {
    1479            if (elts[i].className == "private") {
    1480              elts[i].style.display = ((cmd && cmd.substr(0,4)=="hide")?"none":"block");
    1481            }
    1482            else if (elts[i].className == "public") {
    1483              elts[i].style.display = ((cmd && cmd.substr(0,4)=="hide")?"block":"none");
    1484            }
    1485          }
    1486          // Update all table rows containing private objects.  Note, we
    1487          // use "" instead of "block" becaue IE & firefox disagree on what
    1488          // this should be (block vs table-row), and "" just gives the
    1489          // default for both browsers.
    1490          var elts = document.getElementsByTagName("tr");
    1491          for(var i=0; i<elts.length; i++) {
    1492            if (elts[i].className == "private") {
    1493              elts[i].style.display = ((cmd && cmd.substr(0,4)=="hide")?"none":"");
    1494            }
    1495          }
    1496          // Update all list items containing private objects.
    1497          var elts = document.getElementsByTagName("li");
    1498          for(var i=0; i<elts.length; i++) {
    1499            if (elts[i].className == "private") {
    1500              elts[i].style.display = ((cmd && cmd.substr(0,4)=="hide")?
    1501                                          "none":"");
    1502            }
    1503          }
    1504          // Update all list items containing private objects.
    1505          var elts = document.getElementsByTagName("ul");
    1506          for(var i=0; i<elts.length; i++) {
    1507            if (elts[i].className == "private") {
    1508              elts[i].style.display = ((cmd && cmd.substr(0,4)=="hide")?"none":"block");
    1509            }
    1510          }
    1511          // Set a cookie to remember the current option.
    1512          document.cookie = "EpydocPrivate="+cmd;
    1513        }
    1514        '''.strip()
    1515
    1516      #: A javascript that is used to read the value of a cookie.  This
    1517      #: is used to remember whether private variables should be shown or
    1518      #: hidden.
    1519      GET_COOKIE_JS = '''
    1520        function getCookie(name) {
    1521          var dc = document.cookie;
    1522          var prefix = name + "=";
    1523          var begin = dc.indexOf("; " + prefix);
    1524          if (begin == -1) {
    1525            begin = dc.indexOf(prefix);
    1526            if (begin != 0) return null;
    1527          } else
    1528          { begin += 2; }
    1529          var end = document.cookie.indexOf(";", begin);
    1530          if (end == -1)
    1531          { end = dc.length; }
    1532          return unescape(dc.substring(begin + prefix.length, end));
    1533        }
    1534        '''.strip()
    1535
    1536      #: A javascript that is used to set the contents of two frames at
    1537      #: once.  This is used by the project table-of-contents frame to
    1538      #: set both the module table-of-contents frame and the main frame
    1539      #: when the user clicks on a module.
    1540      SET_FRAME_JS = '''
    1541        function setFrame(url1, url2) {
    1542            parent.frames[1].location.href = url1;
    1543            parent.frames[2].location.href = url2;
    1544        }
    1545      '''.strip()
    1546
    1547      #: A javascript that is used to hide private variables, unless
    1548      #: either: (a) the cookie says not to; or (b) we appear to be
    1549      #: linking to a private variable.
    1550      HIDE_PRIVATE_JS = '''
    1551        function checkCookie() {
    1552          var cmd=getCookie("EpydocPrivate");
    1553          if (cmd && cmd.substr(0,4)!="show" && location.href.indexOf("#_") < 0)
    1554              toggle_private();
    1555        }
    1556      '''.strip()
    1557
    1558      TOGGLE_CALLGRAPH_JS = '''
    1559        function toggleCallGraph(id) {
    1560          var elt = document.getElementById(id);
    1561          if (elt.style.display == "none")
    1562              elt.style.display = "block";
    1563          else
    1564              elt.style.display = "none";
    1565        }
    1566      '''.strip()
    1567
    1568      SHOW_PRIVATE_JS = '''
    1569        function show_private() {
    1570          var elts = document.getElementsByTagName("a");
    1571          for(var i=0; i<elts.length; i++) {
    1572            if (elts[i].className == "privatelink") {
    1573              cmd = elts[i].innerHTML;
    1574              if (cmd && cmd.substr(0,4)=="show")
    1575                  toggle_private();
    1576            }
    1577          }
    1578        }
    1579      '''.strip()
    1580
    1581      GET_ANCHOR_JS = '''
    1582        function get_anchor() {
    1583            var href = location.href;
    1584            var start = href.indexOf("#")+1;
    1585            if ((start != 0) && (start != href.length))
    1586                return href.substring(start, href.length);
    1587        }
    1588      '''.strip()
    1589
    1590      #: A javascript that is used to implement the auto-redirect page.
    1591      #: When the user visits <redirect.html#dotted.name>, they will
    1592      #: automatically get redirected to the page for the object with
    1593      #: the given fully-qualified dotted name.  E.g., for epydoc,
    1594      #: <redirect.html#epydoc.apidoc.UNKNOWN> redirects the user to
    1595      #: <epydoc.apidoc-module.html#UNKNOWN>.
    1596      REDIRECT_URL_JS = '''
    1597        function redirect_url(dottedName) {
    1598            // Scan through each element of the "pages" list, and check
    1599            // if "name" matches with any of them.
    1600            for (var i=0; i<pages.length; i++) {
    1601
    1602                // Each page has the form "<pagename>-m" or "<pagename>-c";
    1603                // extract the <pagename> portion & compare it to dottedName.
    1604                var pagename = pages[i].substring(0, pages[i].length-2);
    1605                if (pagename == dottedName.substring(0,pagename.length)) {
    1606
    1607                    // We\'ve found a page that matches `dottedName`;
    1608                    // construct its URL, using leftover `dottedName`
    1609                    // content to form an anchor.
    1610                    var pagetype = pages[i].charAt(pages[i].length-1);
    1611                    var url = pagename + ((pagetype=="m")?"-module.html":
    1612                                                          "-class.html");
    1613                    if (dottedName.length > pagename.length)
    1614                        url += "#" + dottedName.substring(pagename.length+1,
    1615                                                          dottedName.length);
    1616                    return url;
    1617                }
    1618            }
    1619        }
    1620      '''.strip()
    1621
    1622
    1623      #////////////////////////////////////////////////////////////
    1624      #{ 2.10. Graphs
    1625      #////////////////////////////////////////////////////////////
    1626
    1627 -    def render_graph(self, graph):
    1628          if graph is None: return ''
    1629          graph.caption = graph.title = None
    1630          image_url = '%s.gif' % graph.uid
    1631          image_file = os.path.join(self._directory, image_url)
    1632          return graph.to_html(image_file, image_url)
    1633
    1634      RE_CALLGRAPH_ID = re.compile(r"""["'](.+-div)['"]""")
    1635
    1636 -    def render_callgraph(self, callgraph, token=""):
    1637          """Render the HTML chunk of a callgraph.
    1638
    1639          If C{callgraph} is a string, use the L{_callgraph_cache} to return
    1640          a pre-rendered HTML chunk. This mostly avoids to run C{dot} twice for
    1641          the same callgraph. Else, run the graph and store its HTML output in
    1642          the cache.
    1643
    1644          @param callgraph: The graph to render or its L{uid<DotGraph.uid>}.
    1645          @type callgraph: L{DotGraph} or C{str}
    1646          @param token: A string that can be used to make the C{<div>} id
    1647              unambiguous, if the callgraph is used more than once in a page.
    1648          @type token: C{str}
    1649          @return: The HTML representation of the graph.
    1650          @rtype: C{str}
    1651          """
    1652          if callgraph is None: return ""
    1653
    1654          if isinstance(callgraph, basestring):
    1655              uid = callgraph
    1656              rv = self._callgraph_cache.get(callgraph, "")
    1657
    1658          else:
    1659              uid = callgraph.uid
    1660              graph_html = self.render_graph(callgraph)
    1661              if graph_html == '':
    1662                  rv = ""
    1663              else:
    1664                  rv = ('<div style="display:none" id="%%s-div"><center>\n'
    1665                        '<table border="0" cellpadding="0" cellspacing="0">\n'
    1666                        '  <tr><td>%s</td></tr>\n'
    1667                        '  <tr><th>Call Graph</th></tr>\n'
    1668                        '</table><br />\n</center></div>\n' % graph_html)
    1669
    1670              # Store in the cache the complete HTML chunk without the
    1671              # div id, which may be made unambiguous by the token
    1672              self._callgraph_cache[uid] = rv
    1673
    1674          # Mangle with the graph
    1675          if rv: rv = rv % (uid + token)
    1676          return rv
    1677
    1678 -    def callgraph_link(self, callgraph, token=""):
    1679          """Render the HTML chunk of a callgraph link.
    1680
    1681          The link can toggles the visibility of the callgraph rendered using
    1682          L{render_callgraph} with matching parameters.
    1683
    1684          @param callgraph: The graph to render or its L{uid<DotGraph.uid>}.
    1685          @type callgraph: L{DotGraph} or C{str}
    1686          @param token: A string that can be used to make the C{<div>} id
    1687              unambiguous, if the callgraph is used more than once in a page.
    1688          @type token: C{str}
    1689          @return: The HTML representation of the graph link.
    1690          @rtype: C{str}
    1691          """
    1692          # Use class=codelink, to match style w/ the source code link.
    1693          if callgraph is None: return ''
    1694
    1695          if isinstance(callgraph, basestring):
    1696              uid = callgraph
    1697          else:
    1698              uid = callgraph.uid
    1699
    1700          return ('<br /><span class="codelink"><a href="javascript:void(0);" '
    1701                  'onclick="toggleCallGraph(\'%s-div\');return false;">'
    1702                  'call&nbsp;graph</a></span>&nbsp;' % (uid + token))
    1703
    1704      #////////////////////////////////////////////////////////////
    1705      #{ 2.11. Images
    1706      #////////////////////////////////////////////////////////////
    1707
    1708      IMAGES = {'crarr.png': # Carriage-return arrow, used for LINEWRAP.
    1709                'iVBORw0KGgoAAAANSUhEUgAAABEAAAAKCAMAAABlokWQAAAALHRFWHRD'
    1710                'cmVhdGlvbiBUaW1lAFR1\nZSAyMiBBdWcgMjAwNiAwMDo0MzoxMCAtMD'
    1711                'UwMGAMEFgAAAAHdElNRQfWCBYFASkQ033WAAAACXBI\nWXMAAB7CAAAe'
    1712                'wgFu0HU+AAAABGdBTUEAALGPC/xhBQAAAEVQTFRF////zcOw18/AgGY0'
    1713                'c1cg4dvQ\ninJEYEAAYkME3NXI6eTcloFYe2Asr5+AbE4Uh29A9fPwqp'
    1714                'l4ZEUI8O3onopk0Ma0lH5U1nfFdgAA\nAAF0Uk5TAEDm2GYAAABNSURB'
    1715                'VHjaY2BAAbzsvDAmK5oIlxgfioiwCAe7KJKIgKAQOzsLLwTwA0VY\n+d'
    1716                'iRAT8T0AxuIIMHqoaXCWIPGzsHJ6orGJiYWRjQASOcBQAocgMSPKMTIg'
    1717                'AAAABJRU5ErkJggg==\n',
    1718                }
    1719
    1720 -    def write_images(self, directory):
    1721          for (name, data) in self.IMAGES.items():
    1722              f = open(os.path.join(directory, name), 'wb')
    1723              f.write(base64.decodestring(data))
    1724              f.close()
    1725
    1726      #////////////////////////////////////////////////////////////
    1727      #{ 3.1. Page Header
    1728      #////////////////////////////////////////////////////////////
    1729
    1730      write_header = compile_template(
    1731          """
    1732          write_header(self, out, title)
    1733
    1734          Generate HTML code for the standard page header, and write it
    1735          to C{out}.  C{title} is a string containing the page title.
    1736          It should be appropriately escaped/encoded.
    1737          """,
    1738          # /------------------------- Template -------------------------\
    1739          '''
    1740          <?xml version="1.0" encoding="ascii"?>
    1741          <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    1742                    "DTD/xhtml1-transitional.dtd">
    1743          <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    1744          <head>
    1745            <title>$title$</title>
    1746            <link rel="stylesheet" href="epydoc.css" type="text/css" />
    1747            <script type="text/javascript" src="epydoc.js"></script>
    1748          </head>
    1749
    1750          <body bgcolor="white" text="black" link="blue" vlink="#204080"
    1751                alink="#204080">
    1752          ''')
    1753          # \------------------------------------------------------------/
    1754
    1755      #////////////////////////////////////////////////////////////
    1756      #{ 3.2. Page Footer
    1757      #////////////////////////////////////////////////////////////
    1758
    1759      write_footer = compile_template(
    1760          """
    1761          write_footer(self, out, short=False)
    1762
    1763          Generate HTML code for the standard page footer, and write it
    1764          to C{out}.
    1765          """,
    1766          # /------------------------- Template -------------------------\
    1767          '''
    1768          >>> if not short:
    1769          <table border="0" cellpadding="0" cellspacing="0" width="100%%">
    1770            <tr>
    1771              <td align="left" class="footer">
    1772          >>>   if self._include_log:
    1773              <a href="epydoc-log.html">Generated by Epydoc
    1774              $epydoc.__version__$ on $time.asctime()$</a>
    1775          >>>   else:
    1776              Generated by Epydoc $epydoc.__version__$ on $time.asctime()$
    1777          >>>   #endif
    1778              </td>
    1779              <td align="right" class="footer">
    1780                <a target="mainFrame" href="http://epydoc.sourceforge.net"
    1781                  >http://epydoc.sourceforge.net</a>
    1782              </td>
    1783            </tr>
    1784          </table>
    1785          >>> #endif
    1786
    1787          <script type="text/javascript">
    1788            <!--
    1789            // Private objects are initially displayed (because if
    1790            // javascript is turned off then we want them to be
    1791            // visible); but by default, we want to hide them.  So hide
    1792            // them unless we have a cookie that says to show them.
    1793            checkCookie();
    1794            // -->
    1795          </script>
    1796          </body>
    1797          </html>
    1798          ''')
    1799          # \------------------------------------------------------------/
    1800
    1801      #////////////////////////////////////////////////////////////
    1802      #{ 3.3. Navigation Bar
    1803      #////////////////////////////////////////////////////////////
    1804
    1805      write_navbar = compile_template(
    1806          """
    1807          write_navbar(self, out, context)
    1808
    1809          Generate HTML code for the navigation bar, and write it to
    1810          C{out}.  The navigation bar typically looks like::
    1811
    1812               [ Home Trees Index Help             Project ]
    1813
    1814          @param context: A value indicating what page we're generating
    1815          a navigation bar for.  If we're generating an API
    1816          documentation page for an object, then C{context} is a
    1817          L{ValueDoc} containing the documentation for that object;
    1818          otherwise, C{context} is a string name for the page.  The
    1819          following string names are recognized: C{'tree'}, C{'index'},
    1820          and C{'help'}.
    1821          """,
    1822          # /------------------------- Template -------------------------\
    1823          '''
    1824          <!-- ==================== NAVIGATION BAR ==================== -->
    1825          <table class="navbar" border="0" width="100%" cellpadding="0"
    1826                 bgcolor="#a0c0ff" cellspacing="0">
    1827            <tr valign="middle">
    1828          >>> if self._top_page_url not in (self._trees_url, "identifier-index.html", "help.html"):
    1829            <!-- Home link -->
    1830          >>>   if (isinstance(context, ValueDoc) and
    1831          >>>       self._top_page_url == self.url(context.canonical_name)):
    1832                <th bgcolor="#70b0f0" class="navbar-select"
    1833                    >&nbsp;&nbsp;&nbsp;Home&nbsp;&nbsp;&nbsp;</th>
    1834          >>>   else:
    1835                <th>&nbsp;&nbsp;&nbsp;<a
    1836                  href="$self._top_page_url$">Home</a>&nbsp;&nbsp;&nbsp;</th>
    1837          >>> #endif
    1838
    1839            <!-- Tree link -->
    1840          >>> if context == "trees":
    1841                <th bgcolor="#70b0f0" class="navbar-select"
    1842                    >&nbsp;&nbsp;&nbsp;Trees&nbsp;&nbsp;&nbsp;</th>
    1843          >>> else:
    1844                <th>&nbsp;&nbsp;&nbsp;<a
    1845                  href="$self._trees_url$">Trees</a>&nbsp;&nbsp;&nbsp;</th>
    1846          >>> #endif
    1847
    1848            <!-- Index link -->
    1849          >>> if context == "indices":
    1850                <th bgcolor="#70b0f0" class="navbar-select"
    1851                    >&nbsp;&nbsp;&nbsp;Indices&nbsp;&nbsp;&nbsp;</th>
    1852          >>> else:
    1853                <th>&nbsp;&nbsp;&nbsp;<a
    1854                  href="identifier-index.html">Indices</a>&nbsp;&nbsp;&nbsp;</th>
    1855          >>> #endif
    1856
    1857            <!-- Help link -->
    1858          >>> if context == "help":
    1859                <th bgcolor="#70b0f0" class="navbar-select"
    1860                    >&nbsp;&nbsp;&nbsp;Help&nbsp;&nbsp;&nbsp;</th>
    1861          >>> else:
    1862                <th>&nbsp;&nbsp;&nbsp;<a
    1863                  href="help.html">Help</a>&nbsp;&nbsp;&nbsp;</th>
    1864          >>> #endif
    1865
    1866          >>> if self._prj_link:
    1867            <!-- Project homepage -->
    1868                <th class="navbar" align="right" width="100%">
    1869                  <table border="0" cellpadding="0" cellspacing="0">
    1870                    <tr><th class="navbar" align="center"
    1871                      >$self._prj_link.strip()$</th>
    1872                    </tr></table></th>
    1873          >>> else:
    1874                <th class="navbar" width="100%"></th>
    1875          >>> #endif
    1876            </tr>
    1877          </table>
    1878          ''')
    1879          # \------------------------------------------------------------/
    1880
    1881      #////////////////////////////////////////////////////////////
    1882      #{ 3.4. Breadcrumbs
    1883      #////////////////////////////////////////////////////////////
    1884
    1885      write_breadcrumbs = compile_template(
    1886          """
    1887          write_breadcrumbs(self, out, context, context_url)
    1888
    1889          Generate HTML for the breadcrumbs line, and write it to
    1890          C{out}.  The breadcrumbs line is an invisible table with a
    1891          list of pointers to the current object's ancestors on the
    1892          left; and the show/hide private selector and the
    1893          frames/noframes selector on the right.
    1894
    1895          @param context: The API documentation for the object whose
    1896          breadcrumbs we should generate.
    1897          @type context: L{ValueDoc}
    1898          """,
    1899          # /------------------------- Template -------------------------\
    1900          '''
    1901          <table width="100%" cellpadding="0" cellspacing="0">
    1902            <tr valign="top">
    1903          >>> if isinstance(context, APIDoc):
    1904              <td width="100%">
    1905                <span class="breadcrumbs">
    1906          >>>   crumbs = self.breadcrumbs(context)
    1907          >>>   for crumb in crumbs[:-1]:
    1908                  $crumb$ ::
    1909          >>>   #endfor
    1910                  $crumbs[-1]$
    1911                </span>
    1912              </td>
    1913          >>> else:
    1914              <td width="100%">&nbsp;</td>
    1915          >>> #endif
    1916              <td>
    1917                <table cellpadding="0" cellspacing="0">
    1918                  <!-- hide/show private -->
    1919          >>> if self._show_private:
    1920                  <tr><td align="right">$self.PRIVATE_LINK$</td></tr>
    1921          >>> #endif
    1922          >>> if self._frames_index:
    1923                  <tr><td align="right"><span class="options"
    1924                      >[<a href="frames.html" target="_top">frames</a
    1925                      >]&nbsp;|&nbsp;<a href="$context_url$"
    1926                      target="_top">no&nbsp;frames</a>]</span></td></tr>
    1927          >>> #endif
    1928                </table>
    1929              </td>
    1930            </tr>
    1931          </table>
    1932          ''')
    1933          # \------------------------------------------------------------/
    1934
    1935 -    def breadcrumbs(self, doc):
    1936          crumbs = [self._crumb(doc)]
    1937
    1938          # Generate the crumbs for uid's ancestors.
    1939          while True:
    1940              container = self.docindex.container(doc)
    1941              assert doc != container, 'object is its own container?'
    1942              if container is None:
    1943                  if doc.canonical_name is UNKNOWN:
    1944                      return ['??']+crumbs
    1945                  elif isinstance(doc, ModuleDoc):
    1946                      return ['Package&nbsp;%s' % ident
    1947                              for ident in doc.canonical_name[:-1]]+crumbs
    1948                  else:
    1949                      return list(doc.canonical_name)+crumbs
    1950              else:
    1951                  label = self._crumb(container)
    1952                  name = container.canonical_name
    1953                  crumbs.insert(0, self.href(container, label)) # [xx] code=0??
    1954                  doc = container
    1955
    1956 -    def _crumb(self, doc):
    1957          if (len(doc.canonical_name)==1 and
    1958              doc.canonical_name[0].startswith('script-')):
    1959              return 'Script&nbsp;%s' % doc.canonical_name[0][7:]
    1960          return '%s&nbsp;%s' % (self.doc_kind(doc), doc.canonical_name[-1])
    1961
    1962      #////////////////////////////////////////////////////////////
    1963      #{ 3.5. Summary Tables
    1964      #////////////////////////////////////////////////////////////
    1965
    1966 -    def write_summary_table(self, out, heading, doc, value_type):
    1967          """
    1968          Generate HTML code for a summary table, and write it to
    1969          C{out}.  A summary table is a table that includes a one-row
    1970          description for each variable (of a given type) in a module
    1971          or class.
    1972
    1973          @param heading: The heading for the summary table; typically,
    1974              this indicates what kind of value the table describes
    1975              (e.g., functions or classes).
    1976          @param doc: A L{ValueDoc} object containing the API
    1977              documentation for the module or class whose variables
    1978              we should summarize.
    1979          @param value_type: A string indicating what type of value
    1980              should be listed in this summary table.  This value
    1981              is passed on to C{doc}'s C{select_variables()} method.
    1982          """
    1983          # inh_var_groups is a dictionary used to hold "inheritance
    1984          # pseudo-groups", which are created when inheritance is
    1985          # 'grouped'.  It maps each base to a list of vars inherited
    1986          # from that base.
    1987          grouped_inh_vars = {}
    1988
    1989          # Divide all public variables of the given type into groups.
    1990          groups = [(plaintext_to_html(group_name),
    1991                     doc.select_variables(group=group_name, imported=False,
    1992                                          value_type=value_type,
    1993                                          public=self._public_filter))
    1994                    for group_name in doc.group_names()]
    1995
    1996          # Discard any empty groups; and return if they're all empty.
    1997          groups = [(g,vars) for (g,vars) in groups if vars]
    1998          if not groups: return
    1999
    2000          # Write a header
    2001          self.write_table_header(out, "summary", heading)
    2002
    2003          # Write a section for each group.
    2004          for name, var_docs in groups:
    2005              self.write_summary_group(out, doc, name,
    2006                                       var_docs, grouped_inh_vars)
    2007
    2008          # Write a section for each inheritance pseudo-group (used if
    2009          # inheritance=='grouped')
    2010          if grouped_inh_vars:
    2011              for base in doc.mro():
    2012                  if base in grouped_inh_vars:
    2013                      hdr = 'Inherited from %s' % self.href(base, context=doc)
    2014                      tr_class = ''
    2015                      if len([v for v in grouped_inh_vars[base]
    2016                              if v.is_public]) == 0:
    2017                          tr_class = ' class="private"'
    2018                      self.write_group_header(out, hdr, tr_class)
    2019                      for var_doc in grouped_inh_vars[base]:
    2020                          self.write_summary_line(out, var_doc, doc)
    2021
    2022          # Write a footer for the table.
    2023          out(self.TABLE_FOOTER)
    2024
    2025 -    def write_summary_group(self, out, doc, name, var_docs, grouped_inh_vars):
    2026          # Split up the var_docs list, according to the way each var
    2027          # should be displayed:
    2028          #   - listed_inh_vars -- for listed inherited variables.
    2029          #   - grouped_inh_vars -- for grouped inherited variables.
    2030          #   - normal_vars -- for all other variables.
    2031          listed_inh_vars = {}
    2032          normal_vars = []
    2033          for var_doc in var_docs:
    2034              if var_doc.container != doc:
    2035                  base = var_doc.container
    2036                  if not isinstance(base, ClassDoc):
    2037                      # This *should* never happen:
    2038                      log.warning("%s's container is not a class!" % var_doc)
    2039                      normal_vars.append(var_doc)
    2040                  elif (base not in self.class_set or
    2041                      self._inheritance == 'listed'):
    2042                      listed_inh_vars.setdefault(base,[]).append(var_doc)
    2043                  elif self._inheritance == 'grouped':
    2044                      grouped_inh_vars.setdefault(base,[]).append(var_doc)
    2045                  else:
    2046                      normal_vars.append(var_doc)
    2047              else:
    2048                  normal_vars.append(var_doc)
    2049
    2050          # Write a header for the group.
    2051          if name != '':
    2052              tr_class = ''
    2053              if len([v for v in var_docs if v.is_public]) == 0:
    2054                  tr_class = ' class="private"'
    2055              self.write_group_header(out, name, tr_class)
    2056
    2057          # Write a line for each normal var:
    2058          for var_doc in normal_vars:
    2059              self.write_summary_line(out, var_doc, doc)
    2060          # Write a subsection for inherited vars:
    2061          if listed_inh_vars:
    2062              self.write_inheritance_list(out, doc, listed_inh_vars)
    2063
    2064 -    def write_inheritance_list(self, out, doc, listed_inh_vars):
    2065          out('  <tr>\n    <td colspan="2" class="summary">\n')
    2066          for base in doc.mro():
    2067              if base not in listed_inh_vars: continue
    2068              public_vars = [v for v in listed_inh_vars[base]
    2069                             if v.is_public]
    2070              private_vars = [v for v in listed_inh_vars[base]
    2071                              if not v.is_public]
    2072              if public_vars:
    2073                  out('    <p class="indent-wrapped-lines">'
    2074                      '<b>Inherited from <code>%s</code></b>:\n' %
    2075                      self.href(base, context=doc))
    2076                  self.write_var_list(out, public_vars)
    2077                  out('      </p>\n')
    2078              if private_vars and self._show_private:
    2079                  out('    <div class="private">')
    2080                  out('    <p class="indent-wrapped-lines">'
    2081                      '<b>Inherited from <code>%s</code></b> (private):\n' %
    2082                      self.href(base, context=doc))
    2083                  self.write_var_list(out, private_vars)
    2084                  out('      </p></div>\n')
    2085          out('    </td>\n  </tr>\n')
    2086
    2087 -    def write_var_list(self, out, vardocs):
    2088          out('      ')
    2089          out(',\n      '.join(['<code>%s</code>' % self.href(v,v.name)
    2090                                for v in vardocs])+'\n')
    2091
    2092 -    def write_summary_line(self, out, var_doc, container):
    2093          """
    2094          Generate HTML code for a single line of a summary table, and
    2095          write it to C{out}.  See L{write_summary_table} for more
    2096          information.
    2097
    2098          @param var_doc: The API documentation for the variable that
    2099              should be described by this line of the summary table.
    2100          @param container: The API documentation for the class or
    2101              module whose summary table we're writing.
    2102          """
    2103          pysrc_link = None
    2104          callgraph = None
    2105
    2106          # If it's a private variable, then mark its <tr>.
    2107          if var_doc.is_public: tr_class = ''
    2108          else: tr_class = ' class="private"'
    2109
    2110          # Decide an anchor or a link is to be generated.
    2111          link_name = self._redundant_details or var_doc.is_detailed()
    2112          anchor = not link_name
    2113
    2114          # Construct the HTML code for the type (cell 1) & description
    2115          # (cell 2).
    2116          if isinstance(var_doc.value, RoutineDoc):
    2117              typ = self.return_type(var_doc, indent=6)
    2118              description = self.function_signature(var_doc, is_summary=True,
    2119                  link_name=link_name, anchor=anchor)
    2120              pysrc_link = self.pysrc_link(var_doc.value)
    2121
    2122              # Perpare the call-graph, if requested
    2123              if 'callgraph' in self._graph_types:
    2124                  linker = _HTMLDocstringLinker(self, var_doc.value)
    2125                  callgraph = call_graph([var_doc.value], self.docindex,
    2126                                         linker, var_doc, add_callers=True,
    2127                                         add_callees=True)
    2128                  if callgraph and callgraph.nodes:
    2129                      var_doc.value.callgraph_uid = callgraph.uid
    2130                  else:
    2131                      callgraph = None
    2132          else:
    2133              typ = self.type_descr(var_doc, indent=6)
    2134              description = self.summary_name(var_doc,
    2135                  link_name=link_name, anchor=anchor)
    2136              if isinstance(var_doc.value, GenericValueDoc):
    2137                  # The summary max length has been chosen setting
    2138                  # L{ValueDoc.SUMMARY_REPR_LINELEN} in the constructor
    2139                  max_len=self._variable_summary_linelen-3-len(var_doc.name)
    2140                  val_repr = var_doc.value.summary_pyval_repr(max_len)
    2141                  tooltip = self.variable_tooltip(var_doc)
    2142                  description += (' = <code%s>%s</code>' %
    2143                                  (tooltip, val_repr.to_html(None)))
    2144
    2145          # Add the summary to the description (if there is one).
    2146          summary = self.summary(var_doc, indent=6)
    2147          if summary: description += '<br />\n      %s' % summary
    2148
    2149          # If it's inherited, then add a note to the description.
    2150          if var_doc.container != container and self._inheritance=="included":
    2151              description += ("\n      <em>(Inherited from " +
    2152                          self.href(var_doc.container) + ")</em>")
    2153
    2154          # Write the summary line.
    2155          self._write_summary_line(out, typ, description, tr_class, pysrc_link,
    2156                                   callgraph)
    2157
    2158      _write_summary_line = compile_template(
    2159          "_write_summary_line(self, out, typ, description, tr_class, "
    2160                              "pysrc_link, callgraph)",
    2161          # /------------------------- Template -------------------------\
    2162          '''
    2163            <tr$tr_class$>
    2164              <td width="15%" align="right" valign="top" class="summary">
    2165                <span class="summary-type">$typ or "&nbsp;"$</span>
    2166              </td><td class="summary">
    2167          >>> if pysrc_link is not None or callgraph is not None:
    2168                <table width="100%" cellpadding="0" cellspacing="0" border="0">
    2169                  <tr>
    2170                    <td>$description$</td>
    2171                    <td align="right" valign="top">
    2172                      $pysrc_link$
    2173                      $self.callgraph_link(callgraph, token='-summary')$
    2174                    </td>
    2175                  </tr>
    2176                </table>
    2177                $self.render_callgraph(callgraph, token='-summary')$
    2178          >>> #endif
    2179          >>> if pysrc_link is None and callgraph is None:
    2180                  $description$
    2181          >>> #endif
    2182              </td>
    2183            </tr>
    2184          ''')
    2185          # \------------------------------------------------------------/
    2186
    2187      #////////////////////////////////////////////////////////////
    2188      #{ 3.6. Details Lists
    2189      #////////////////////////////////////////////////////////////
    2190
    2191 -    def write_details_list(self, out, heading, doc, value_type):
    2192          # Get a list of the VarDocs we should describe.
    2193          if self._redundant_details:
    2194              detailed = None
    2195          else:
    2196              detailed = True
    2197          if isinstance(doc, ClassDoc):
    2198              var_docs = doc.select_variables(value_type=value_type,
    2199                                              imported=False, inherited=False,
    2200                                              public=self._public_filter,
    2201                                              detailed=detailed)
    2202          else:
    2203              var_docs = doc.select_variables(value_type=value_type,
    2204                                              imported=False,
    2205                                              public=self._public_filter,
    2206                                              detailed=detailed)
    2207          if not var_docs: return
    2208
    2209          # Write a header
    2210          self.write_table_header(out, "details", heading)
    2211          out(self.TABLE_FOOTER)
    2212
    2213          for var_doc in var_docs:
    2214              self.write_details_entry(out, var_doc)
    2215
    2216          out('<br />\n')
    2217
    2218 -    def write_details_entry(self, out, var_doc):
    2219          descr = self.descr(var_doc, indent=2) or ''
    2220          if var_doc.is_public: div_class = ''
    2221          else: div_class = ' class="private"'
    2222
    2223          # Functions
    2224          if isinstance(var_doc.value, RoutineDoc):
    2225              rtype = self.return_type(var_doc, indent=10)
    2226              rdescr = self.return_descr(var_doc, indent=10)
    2227              arg_descrs = []
    2228              args = set()
    2229              # Find the description for each arg.  (Leave them in the
    2230              # same order that they're listed in the docstring.)
    2231              for (arg_names, arg_descr) in var_doc.value.arg_descrs:
    2232                  args.update(arg_names)
    2233                  lhs = ', '.join([self.arg_name_to_html(var_doc.value, n)
    2234                                   for n in arg_names])
    2235                  rhs = self.docstring_to_html(arg_descr, var_doc.value, 10)
    2236                  arg_descrs.append( (lhs, rhs) )
    2237              # Check for arguments for which we have @type but not @param;
    2238              # and add them to the arg_descrs list.
    2239              for arg in var_doc.value.arg_types:
    2240                  if arg not in args:
    2241                      argname = self.arg_name_to_html(var_doc.value, arg)
    2242                      arg_descrs.append( (argname,'') )
    2243
    2244              self.write_function_details_entry(out, var_doc, descr,
    2245                                                var_doc.value.callgraph_uid,
    2246                                                rtype, rdescr, arg_descrs,
    2247                                                div_class)
    2248
    2249          # Properties
    2250          elif isinstance(var_doc.value, PropertyDoc):
    2251              prop_doc = var_doc.value
    2252              accessors = [ (name,
    2253                             self.property_accessor_to_html(val_doc, prop_doc),
    2254                             self.summary(val_doc))
    2255                           for (name, val_doc) in
    2256                              [('Get', prop_doc.fget), ('Set', prop_doc.fset),
    2257                               ('Delete', prop_doc.fdel)]
    2258                              if val_doc not in (None, UNKNOWN)
    2259                              and val_doc.pyval is not None ]
    2260
    2261              self.write_property_details_entry(out, var_doc, descr,
    2262                                                accessors, div_class)
    2263
    2264          # Variables
    2265          else:
    2266              self.write_variable_details_entry(out, var_doc, descr, div_class)
    2267
    2268 -    def labelled_list_item(self, lhs, rhs):
    2269          # If the RHS starts with a paragraph, then move the
    2270          # paragraph-start tag to the beginning of the lhs instead (so
    2271          # there won't be a line break after the '-').
    2272          m = re.match(r'^<p( [^>]+)?>', rhs)
    2273          if m:
    2274              lhs = m.group() + lhs
    2275              rhs = rhs[m.end():]
    2276
    2277          if rhs:
    2278              return '<li>%s - %s</li>' % (lhs, rhs)
    2279          else:
    2280              return '<li>%s</li>' % (lhs,)
    2281
    2282 -    def property_accessor_to_html(self, val_doc, context=None):
    2283          if val_doc not in (None, UNKNOWN):
    2284              if isinstance(val_doc, RoutineDoc):
    2285                  return self.function_signature(val_doc, is_summary=True,
    2286                                                 link_name=True, context=context)
    2287              elif isinstance(val_doc, GenericValueDoc):
    2288                  return self.pprint_value(val_doc)
    2289              else:
    2290                  return self.href(val_doc, context=context)
    2291          else:
    2292              return '??'
    2293
    2294 -    def arg_name_to_html(self, func_doc, arg_name):
    2295          """
    2296          A helper function used to format an argument name, for use in
    2297          the argument description list under a routine's details entry.
    2298          This just wraps strong & code tags around the arg name; and if
    2299          the arg name is associated with a type, then adds it
    2300          parenthetically after the name.
    2301          """
    2302          s = '<strong class="pname"><code>%s</code></strong>' % arg_name
    2303          if arg_name in func_doc.arg_types:
    2304              typ = func_doc.arg_types[arg_name]
    2305              typ_html = self.docstring_to_html(typ, func_doc, 10)
    2306              s += " (%s)" % typ_html
    2307          return s
    2308
    2309      write_function_details_entry = compile_template(
    2310          '''
    2311          write_function_details_entry(self, out, var_doc, descr, callgraph, \
    2312                                       rtype, rdescr, arg_descrs, div_class)
    2313          ''',
    2314          # /------------------------- Template -------------------------\
    2315          '''
    2316          >>> func_doc = var_doc.value
    2317          <a name="$var_doc.name$"></a>
    2318          <div$div_class$>
    2319          >>> self.write_table_header(out, "details")
    2320          <tr><td>
    2321            <table width="100%" cellpadding="0" cellspacing="0" border="0">
    2322            <tr valign="top"><td>
    2323            <h3 class="epydoc">$self.function_signature(var_doc)$
    2324          >>> if var_doc.name in self.SPECIAL_METHODS:
    2325              <br /><em class="fname">($self.SPECIAL_METHODS[var_doc.name]$)</em>
    2326          >>> #endif
    2327          >>> if isinstance(func_doc, ClassMethodDoc):
    2328              <br /><em class="fname">Class Method</em>
    2329          >>> #endif
    2330          >>> if isinstance(func_doc, StaticMethodDoc):
    2331              <br /><em class="fname">Static Method</em>
    2332          >>> #endif
    2333            </h3>
    2334            </td><td align="right" valign="top"
    2335              >$self.pysrc_link(func_doc)$&nbsp;
    2336              $self.callgraph_link(callgraph)$</td>
    2337            </tr></table>
    2338            $self.render_callgraph(callgraph)$
    2339            $descr$
    2340            <dl class="fields">
    2341          >>> # === parameters ===
    2342          >>> if arg_descrs:
    2343              <dt>Parameters:</dt>
    2344              <dd><ul class="nomargin-top">
    2345          >>>   for lhs, rhs in arg_descrs:
    2346                  $self.labelled_list_item(lhs, rhs)$
    2347          >>>   #endfor
    2348              </ul></dd>
    2349          >>> #endif
    2350          >>> # === return type ===
    2351          >>> if rdescr and rtype:
    2352              <dt>Returns: $rtype$</dt>
    2353                  <dd>$rdescr$</dd>
    2354          >>> elif rdescr:
    2355              <dt>Returns:</dt>
    2356                  <dd>$rdescr$</dd>
    2357          >>> elif rtype:
    2358              <dt>Returns: $rtype$</dt>
    2359          >>> #endif
    2360          >>> # === decorators ===
    2361          >>> if func_doc.decorators not in (None, UNKNOWN):
    2362          >>>   # (staticmethod & classmethod are already shown, above)
    2363          >>>   decos = filter(lambda deco:
    2364          >>>     not ((deco=="staticmethod" and
    2365          >>>            isinstance(func_doc, StaticMethodDoc)) or
    2366          >>>          (deco=="classmethod" and
    2367          >>>           isinstance(func_doc, ClassMethodDoc))),
    2368          >>>     func_doc.decorators)
    2369          >>> else:
    2370          >>>   decos = None
    2371          >>> #endif
    2372          >>> if decos:
    2373              <dt>Decorators:</dt>
    2374              <dd><ul class="nomargin-top">
    2375          >>>   for deco in decos:
    2376                  <li><code>@$deco$</code></li>
    2377          >>>   #endfor
    2378              </ul></dd>
    2379          >>> #endif
    2380          >>> # === exceptions ===
    2381          >>> if func_doc.exception_descrs not in (None, UNKNOWN, (), []):
    2382              <dt>Raises:</dt>
    2383              <dd><ul class="nomargin-top">
    2384          >>>   for name, descr in func_doc.exception_descrs:
    2385          >>>     exc_name = self.docindex.find(name, func_doc)
    2386          >>>     if exc_name is not None:
    2387          >>>       name = self.href(exc_name, label=str(name))
    2388          >>>     #endif
    2389                  $self.labelled_list_item(
    2390                      "<code><strong class=\'fraise\'>" +
    2391                      str(name) + "</strong></code>",
    2392                      self.docstring_to_html(descr, func_doc, 8))$
    2393          >>>   #endfor
    2394              </ul></dd>
    2395          >>> #endif
    2396          >>> # === overrides ===
    2397          >>> if var_doc.overrides not in (None, UNKNOWN):
    2398              <dt>Overrides:
    2399          >>>   # Avoid passing GenericValueDoc to href()
    2400          >>>   if isinstance(var_doc.overrides.value, RoutineDoc):
    2401                  $self.href(var_doc.overrides.value, context=var_doc)$
    2402          >>>   else:
    2403          >>>     # In this case, a less interesting label is generated.
    2404                  $self.href(var_doc.overrides, context=var_doc)$
    2405          >>>   #endif
    2406          >>>   if (func_doc.docstring in (None, UNKNOWN) and
    2407          >>>       var_doc.overrides.value.docstring not in (None, UNKNOWN)):
    2408                  <dd><em class="note">(inherited documentation)</em></dd>
    2409          >>>   #endif
    2410              </dt>
    2411          >>> #endif
    2412            </dl>
    2413          >>> # === metadata ===
    2414          >>> self.write_standard_fields(out, func_doc)
    2415          </td></tr></table>
    2416          </div>
    2417          ''')
    2418          # \------------------------------------------------------------/
    2419
    2420      # Names for the __special__ methods.
    2421      SPECIAL_METHODS ={
    2422      '__init__': 'Constructor',
    2423      '__del__': 'Destructor',
    2424      '__add__': 'Addition operator',
    2425      '__sub__': 'Subtraction operator',
    2426      '__and__': 'And operator',
    2427      '__or__': 'Or operator',
    2428      '__xor__': 'Exclusive-Or operator',
    2429      '__repr__': 'Representation operator',
    2430      '__call__': 'Call operator',
    2431      '__getattr__': 'Qualification operator',
    2432      '__getitem__': 'Indexing operator',
    2433      '__setitem__': 'Index assignment operator',
    2434      '__delitem__': 'Index deletion operator',
    2435      '__delslice__': 'Slice deletion operator',
    2436      '__setslice__': 'Slice assignment operator',
    2437      '__getslice__': 'Slicling operator',
    2438      '__len__': 'Length operator',
    2439      '__cmp__': 'Comparison operator',
    2440      '__eq__': 'Equality operator',
    2441      '__in__': 'Containership operator',
    2442      '__gt__': 'Greater-than operator',
    2443      '__lt__': 'Less-than operator',
    2444      '__ge__': 'Greater-than-or-equals operator',
    2445      '__le__': 'Less-than-or-equals operator',
    2446      '__radd__': 'Right-side addition operator',
    2447      '__hash__': 'Hashing function',
    2448      '__contains__': 'In operator',
    2449      '__nonzero__': 'Boolean test operator',
    2450      '__str__': 'Informal representation operator',
    2451      }
    2452
    2453      write_property_details_entry = compile_template(
    2454          '''
    2455          write_property_details_entry(self, out, var_doc, descr, \
    2456                                       accessors, div_class)
    2457          ''',
    2458          # /------------------------- Template -------------------------\
    2459          '''
    2460          >>> prop_doc = var_doc.value
    2461          <a name="$var_doc.name$"></a>
    2462          <div$div_class$>
    2463          >>> self.write_table_header(out, "details")
    2464          <tr><td>
    2465            <h3 class="epydoc">$var_doc.name$</h3>
    2466            $descr$
    2467            <dl class="fields">
    2468          >>> for (name, val, summary) in accessors:
    2469              <dt>$name$ Method:</dt>
    2470              <dd class="value">$val$
    2471          >>>     if summary:
    2472                  - $summary$
    2473          >>>     #endif
    2474              </dd>
    2475          >>> #endfor
    2476          >>> if prop_doc.type_descr not in (None, UNKNOWN):
    2477              <dt>Type:</dt>
    2478                <dd>$self.type_descr(var_doc, indent=6)$</dd>
    2479          >>> #endif
    2480            </dl>
    2481          >>> self.write_standard_fields(out, prop_doc)
    2482          </td></tr></table>
    2483          </div>
    2484          ''')
    2485          # \------------------------------------------------------------/
    2486
    2487      write_variable_details_entry = compile_template(
    2488          '''
    2489          write_variable_details_entry(self, out, var_doc, descr, div_class)
    2490          ''',
    2491          # /------------------------- Template -------------------------\
    2492          '''
    2493          <a name="$var_doc.name$"></a>
    2494          <div$div_class$>
    2495          >>> self.write_table_header(out, "details")
    2496          <tr><td>
    2497            <h3 class="epydoc">$var_doc.name$</h3>
    2498            $descr$
    2499            <dl class="fields">
    2500          >>> if var_doc.type_descr not in (None, UNKNOWN):
    2501              <dt>Type:</dt>
    2502                <dd>$self.type_descr(var_doc, indent=6)$</dd>
    2503          >>> #endif
    2504            </dl>
    2505          >>> self.write_standard_fields(out, var_doc)
    2506          >>> if var_doc.value is not UNKNOWN:
    2507            <dl class="fields">
    2508              <dt>Value:</dt>
    2509                <dd>$self.pprint_value(var_doc.value)$</dd>
    2510            </dl>
    2511          >>> #endif
    2512          </td></tr></table>
    2513          </div>
    2514          ''')
    2515          # \------------------------------------------------------------/
    2516
    2517 -    def variable_tooltip(self, var_doc):
    2518          if var_doc.value in (None, UNKNOWN):
    2519              return ''
    2520          s = var_doc.value.pyval_repr().to_plaintext(None)
    2521          if len(s) > self._variable_tooltip_linelen:
    2522              s = s[:self._variable_tooltip_linelen-3]+'...'
    2523          return ' title="%s"' % plaintext_to_html(s)
    2524
    2525 -    def pprint_value(self, val_doc):
    2526          if val_doc is UNKNOWN:
    2527              return '??'
    2528          elif isinstance(val_doc, GenericValueDoc):
    2529              return ('<table><tr><td><pre class="variable">\n' +
    2530                      val_doc.pyval_repr().to_html(None) +
    2531                      '\n</pre></td></tr></table>\n')
    2532          else:
    2533              return self.href(val_doc)
    2534
    2535      #////////////////////////////////////////////////////////////
    2536      #{ Base Tree
    2537      #////////////////////////////////////////////////////////////
    2538
    2539 -    def base_tree(self, doc, width=None, postfix='', context=None):
    2540          """
    2541          @return: The HTML code for a class's base tree.  The tree is
    2542              drawn 'upside-down' and right justified, to allow for
    2543              multiple inheritance.
    2544          @rtype: C{string}
    2545          """
    2546          if context is None:
    2547              context = doc.defining_module
    2548          if width == None: width = self.find_tree_width(doc, context)
    2549          if isinstance(doc, ClassDoc) and doc.bases != UNKNOWN:
    2550              bases = doc.bases
    2551          else:
    2552              bases = []
    2553
    2554          if postfix == '':
    2555              # [XX] use var name instead of canonical name?
    2556              s = (' '*(width-2) + '<strong class="uidshort">'+
    2557                     self.contextual_label(doc, context)+'</strong>\n')
    2558          else: s = ''
    2559          for i in range(len(bases)-1, -1, -1):
    2560              base = bases[i]
    2561              label = self.contextual_label(base, context)
    2562              s = (' '*(width-4-len(label)) + self.href(base, label)
    2563                     +' --+'+postfix+'\n' +
    2564                     ' '*(width-4) +
    2565                     '   |'+postfix+'\n' +
    2566                     s)
    2567              if i != 0:
    2568                  s = (self.base_tree(base, width-4, '   |'+postfix, context)+s)
    2569              else:
    2570                  s = (self.base_tree(base, width-4, '    '+postfix, context)+s)
    2571          return s
    2572
    2573 -    def find_tree_width(self, doc, context):
    2574          """
    2575          Helper function for L{base_tree}.
    2576          @return: The width of a base tree, when drawn
    2577              right-justified.  This is used by L{base_tree} to
    2578              determine how far to indent lines of the base tree.
    2579          @rtype: C{int}
    2580          """
    2581          if not isinstance(doc, ClassDoc): return 2
    2582          if doc.bases == UNKNOWN: return 2
    2583          width = 2
    2584          for base in doc.bases:
    2585              width = max(width, len(self.contextual_label(base, context))+4,
    2586                          self.find_tree_width(base, context)+4)
    2587          return width
    2588
    2589 -    def contextual_label(self, doc, context):
    2590          """
    2591          Return the label for C{doc} to be shown in C{context}.
    2592          """
    2593          if doc.canonical_name is None:
    2594              if doc.parse_repr is not None:
    2595                  return doc.parse_repr
    2596              else:
    2597                  return '??'
    2598          else:
    2599              if context is UNKNOWN:
    2600                  return str(doc.canonical_name)
    2601              else:
    2602                  context_name = context.canonical_name
    2603                  return str(doc.canonical_name.contextualize(context_name))
    2604
    2605      #////////////////////////////////////////////////////////////
    2606      #{ Function Signatures
    2607      #////////////////////////////////////////////////////////////
    2608
    2609 -    def function_signature(self, api_doc, is_summary=False,
    2610                             link_name=False, anchor=False, context=None):
    2611          """Render a function signature in HTML.
    2612
    2613          @param api_doc: The object whose name is to be rendered. If a
    2614              C{VariableDoc}, its C{value} should be a C{RoutineDoc}
    2615          @type api_doc: L{VariableDoc} or L{RoutineDoc}
    2616          @param is_summary: True if the fuction is to be rendered in the summary.
    2617          type css_class: C{bool}
    2618          @param link_name: If True, the name is a link to the object anchor.
    2619          @type link_name: C{bool}
    2620          @param anchor: If True, the name is the object anchor.
    2621          @type anchor: C{bool}
    2622          @param context: If set, represent the function name from this context.
    2623              Only useful when C{api_doc} is a L{RoutineDoc}.
    2624          @type context: L{DottedName}
    2625
    2626          @return: The HTML code for the object.
    2627          @rtype: C{str}
    2628          """
    2629          if is_summary: css_class = 'summary-sig'
    2630          else: css_class = 'sig'
    2631
    2632          # [XX] clean this up!
    2633          if isinstance(api_doc, VariableDoc):
    2634              func_doc = api_doc.value
    2635              # This should never happen, but just in case:
    2636              if api_doc.value in (None, UNKNOWN):
    2637                  return (('<span class="%s"><span class="%s-name">%s'+
    2638                           '</span>(...)</span>') %
    2639                          (css_class, css_class, api_doc.name))
    2640              # Get the function's name.
    2641              name = self.summary_name(api_doc, css_class=css_class+'-name',
    2642                                       link_name=link_name, anchor=anchor)
    2643          else:
    2644              func_doc = api_doc
    2645              name = self.href(api_doc, css_class=css_class+'-name',
    2646                               context=context)
    2647
    2648          if func_doc.posargs == UNKNOWN:
    2649              args = ['...']
    2650          else:
    2651              args = [self.func_arg(n, d, css_class) for (n, d)
    2652                      in zip(func_doc.posargs, func_doc.posarg_defaults)]
    2653          if func_doc.vararg not in (None, UNKNOWN):
    2654              if func_doc.vararg == '...':
    2655                  args.append('<span class="%s-arg">...</span>' % css_class)
    2656              else:
    2657                  args.append('<span class="%s-arg">*%s</span>' %
    2658                              (css_class, func_doc.vararg))
    2659          if func_doc.kwarg not in (None, UNKNOWN):
    2660              args.append('<span class="%s-arg">**%s</span>' %
    2661                          (css_class, func_doc.kwarg))
    2662
    2663          return ('<span class="%s">%s(%s)</span>' %
    2664                  (css_class, name, ',\n        '.join(args)))
    2665
    2666 -    def summary_name(self, api_doc, css_class='summary-name',
    2667                       link_name=False, anchor=False):
    2668          """Render an object name in HTML.
    2669
    2670          @param api_doc: The object whose name is to be rendered
    2671          @type api_doc: L{APIDoc}
    2672          @param css_class: The CSS class to assign to the rendered name
    2673          type css_class: C{str}
    2674          @param link_name: If True, the name is a link to the object anchor.
    2675          @type link_name: C{bool}
    2676          @param anchor: If True, the name is the object anchor.
    2677          @type anchor: C{bool}
    2678
    2679          @return: The HTML code for the object.
    2680          @rtype: C{str}
    2681          """
    2682          if anchor:
    2683              rv = '<a name="%s"></a>' % api_doc.name
    2684          else:
    2685              rv = ''
    2686
    2687          if link_name:
    2688              rv += self.href(api_doc, css_class=css_class)
    2689          else:
    2690              rv += '<span class="%s">%s</span>' % (css_class, api_doc.name)
    2691
    2692          return rv
    2693
    2694      # [xx] tuple args???
    2695 -    def func_arg(self, name, default, css_class):
    2696          name = self._arg_name(name)
    2697          s = '<span class="%s-arg">%s</span>' % (css_class, name)
    2698          if default is not None:
    2699              s += ('=<span class="%s-default">%s</span>' %
    2700                      (css_class, default.summary_pyval_repr().to_html(None)))
    2701          return s
    2702
    2703 -    def _arg_name(self, arg):
    2704          if isinstance(arg, basestring):
    2705              return arg
    2706          elif len(arg) == 1:
    2707              return '(%s,)' % self._arg_name(arg[0])
    2708          else:
    2709              return '(%s)' % (', '.join([self._arg_name(a) for a in arg]))
    2710
    2711
    2712
    2713
    2714      #////////////////////////////////////////////////////////////
    2715      #{ Import Lists
    2716      #////////////////////////////////////////////////////////////
    2717
    2718 -    def write_imports(self, out, doc):
    2719          assert isinstance(doc, NamespaceDoc)
    2720          imports = doc.select_variables(imported=True,
    2721                                         public=self._public_filter)
    2722          if not imports: return
    2723
    2724          out('<p class="indent-wrapped-lines">')
    2725          out('<b>Imports:</b>\n  ')
    2726          out(',\n  '.join([self._import(v, doc) for v in imports]))
    2727          out('\n</p><br />\n')
    2728
    2729 -    def _import(self, var_doc, context):
    2730          if var_doc.imported_from not in (None, UNKNOWN):
    2731              return self.href(var_doc.imported_from,
    2732                               var_doc.name, context=context,
    2733                               tooltip='%s' % var_doc.imported_from)
    2734          elif (var_doc.value not in (None, UNKNOWN) and not
    2735                isinstance(var_doc.value, GenericValueDoc)):
    2736              return self.href(var_doc.value,
    2737                               var_doc.name, context=context,
    2738                               tooltip='%s' % var_doc.value.canonical_name)
    2739          else:
    2740              return plaintext_to_html(var_doc.name)
    2741
    2742      #////////////////////////////////////////////////////////////
    2743      #{ Function Attributes
    2744      #////////////////////////////////////////////////////////////
    2745
    2746      #////////////////////////////////////////////////////////////
    2747      #{ Module Trees
    2748      #////////////////////////////////////////////////////////////
    2749
    2750 -    def write_module_list(self, out, doc):
    2751          if len(doc.submodules) == 0: return
    2752          self.write_table_header(out, "summary", "Submodules")
    2753
    2754          for group_name in doc.group_names():
    2755              if not doc.submodule_groups[group_name]: continue
    2756              if group_name:
    2757                  self.write_group_header(out, group_name)
    2758              out('  <tr><td class="summary">\n'
    2759                  '  <ul class="nomargin">\n')
    2760              for submodule in doc.submodule_groups[group_name]:
    2761                  self.write_module_tree_item(out, submodule, package=doc)
    2762              out('  </ul></td></tr>\n')
    2763
    2764          out(self.TABLE_FOOTER+'\n<br />\n')
    2765
    2766 -    def write_module_tree_item(self, out, doc, package=None):
    2767          # If it's a private variable, then mark its <li>.
    2768          var = package and package.variables.get(doc.canonical_name[-1])
    2769          priv = ((var is not None and var.is_public is False) or
    2770                  (var is None and doc.canonical_name[-1].startswith('_')))
    2771          out('    <li%s> <strong class="uidlink">%s</strong>'
    2772              % (priv and ' class="private"' or '', self.href(doc)))
    2773          if doc.summary not in (None, UNKNOWN):
    2774              out(': <em class="summary">'+
    2775                  self.description(doc.summary, doc, 8)+'</em>')
    2776          if doc.submodules != UNKNOWN and doc.submodules:
    2777              if priv: out('\n    <ul class="private">\n')
    2778              else: out('\n    <ul>\n')
    2779              for submodule in doc.submodules:
    2780                  self.write_module_tree_item(out, submodule, package=doc)
    2781              out('    </ul>\n')
    2782          out('    </li>\n')
    2783
    2784      #////////////////////////////////////////////////////////////
    2785      #{ Class trees
    2786      #////////////////////////////////////////////////////////////
    2787
    2788      write_class_tree_item = compile_template(
    2789          '''
    2790          write_class_tree_item(self, out, doc, class_set)
    2791          ''',
    2792          # /------------------------- Template -------------------------\
    2793          '''
    2794          >>> if doc.summary in (None, UNKNOWN):
    2795              <li> <strong class="uidlink">$self.href(doc)$</strong>
    2796          >>> else:
    2797              <li> <strong class="uidlink">$self.href(doc)$</strong>:
    2798                <em class="summary">$self.description(doc.summary, doc, 8)$</em>
    2799          >>> # endif
    2800          >>> if doc.subclasses:
    2801              <ul>
    2802          >>>   for subclass in sorted(set(doc.subclasses), key=lambda c:c.canonical_name[-1]):
    2803          >>>     if subclass in class_set:
    2804          >>>       self.write_class_tree_item(out, subclass, class_set)
    2805          >>>     #endif
    2806          >>>   #endfor
    2807              </ul>
    2808          >>> #endif
    2809              </li>
    2810          ''')
    2811          # \------------------------------------------------------------/
    2812
    2813      #////////////////////////////////////////////////////////////
    2814      #{ Standard Fields
    2815      #////////////////////////////////////////////////////////////
    2816
    2817 -    def write_standard_fields(self, out, doc):
    2818          """
    2819          Write HTML code containing descriptions of any standard markup
    2820          fields that are defined by the given L{APIDoc} object (such as
    2821          C{@author} and C{@todo} fields).
    2822
    2823          @param doc: The L{APIDoc} object containing the API documentation
    2824              for the object whose standard markup fields should be
    2825              described.
    2826          """
    2827          fields = []
    2828          field_values = {}
    2829
    2830          for (field, arg, descr) in doc.metadata:
    2831              if field not in field_values:
    2832                  fields.append(field)
    2833              if field.takes_arg:
    2834                  subfields = field_values.setdefault(field,{})
    2835                  subfields.setdefault(arg,[]).append(descr)
    2836              else:
    2837                  field_values.setdefault(field,[]).append(descr)
    2838
    2839          if not fields: return
    2840
    2841          out('<div class="fields">')
    2842          for field in fields:
    2843              if field.takes_arg:
    2844                  for arg, descrs in field_values[field].items():
    2845                      self.write_standard_field(out, doc, field, descrs, arg)
    2846
    2847              else:
    2848                  self.write_standard_field(out, doc, field, field_values[field])
    2849
    2850          out('</div>')
    2851
    2852      write_standard_field = compile_template(
    2853          """
    2854          write_standard_field(self, out, doc, field, descrs, arg='')
    2855
    2856          """,
    2857          # /------------------------- Template -------------------------\
    2858          '''
    2859          >>> if arg: arglabel = " (%s)" % arg
    2860          >>> else: arglabel = ""
    2861          >>>   if len(descrs) == 1:
    2862                <p><strong>$field.singular+arglabel$:</strong>
    2863                  $self.description(descrs[0], doc, 8)$
    2864                </p>
    2865          >>>   elif field.short:
    2866                <dl><dt>$field.plural+arglabel$:</dt>
    2867                  <dd>
    2868          >>>     for descr in descrs[:-1]:
    2869                    $self.description(descr, doc, 10)$,
    2870          >>>     # end for
    2871                    $self.description(descrs[-1], doc, 10)$
    2872                  </dd>
    2873                </dl>
    2874          >>>   else:
    2875                <strong>$field.plural+arglabel$:</strong>
    2876                <ul class="nomargin-top">
    2877          >>>     for descr in descrs:
    2878                  <li>
    2879                  $self.description(descr, doc, 8)$
    2880                  </li>
    2881          >>>     # end for
    2882                </ul>
    2883          >>>   # end else
    2884          >>> # end for
    2885          ''')
    2886          # \------------------------------------------------------------/
    2887
    2888      #////////////////////////////////////////////////////////////
    2889      #{ Index generation
    2890      #////////////////////////////////////////////////////////////
    2891
    2892      #: A list of metadata indices that should be generated.  Each
    2893      #: entry in this list is a tuple C{(tag, label, short_label)},
    2894      #: where C{tag} is the cannonical tag of a metadata field;
    2895      #: C{label} is a label for the index page; and C{short_label}
    2896      #: is a shorter label, used in the index selector.
    2897      METADATA_INDICES = [('bug', 'Bug List', 'Bugs'),
    2898                          ('todo', 'To Do List', 'To Do'),
    2899                          ('change', 'Change Log', 'Changes'),
    2900                          ('deprecated', 'Deprecation List', 'Deprecations'),
    2901                          ('since', 'Introductions List', 'Introductions'),
    2902                          ]
    2903
    2904 -    def build_identifier_index(self):
    2905          items = []
    2906          for doc in self.indexed_docs:
    2907              name = plaintext_to_html(doc.canonical_name[-1])
    2908              if isinstance(doc, RoutineDoc): name += '()'
    2909              url = self.url(doc)
    2910              if not url: continue
    2911              container = self.docindex.container(doc)
    2912              items.append( (name, url, container) )
    2913          return sorted(items, key=lambda v:v[0].lower())
    2914
    2915 -    def _group_by_letter(self, items):
    2916          """Preserves sort order of the input."""
    2917          index = {}
    2918          for item in items:
    2919              first_letter = item[0][0].upper()
    2920              if not ("A" <= first_letter <= "Z"):
    2921                  first_letter = '_'
    2922              index.setdefault(first_letter, []).append(item)
    2923          return index
    2924
    2925 -    def build_term_index(self):
    2926          items = []
    2927          for doc in self.indexed_docs:
    2928              url = self.url(doc)
    2929              items += self._terms_from_docstring(url, doc, doc.descr)
    2930              for (field, arg, descr) in doc.metadata:
    2931                  items += self._terms_from_docstring(url, doc, descr)
    2932                  if hasattr(doc, 'type_descr'):
    2933                      items += self._terms_from_docstring(url, doc,
    2934                                                          doc.type_descr)
    2935                  if hasattr(doc, 'return_descr'):
    2936                      items += self._terms_from_docstring(url, doc,
    2937                                                          doc.return_descr)
    2938                  if hasattr(doc, 'return_type'):
    2939                      items += self._terms_from_docstring(url, doc,
    2940                                                          doc.return_type)
    2941          return sorted(items, key=lambda v:v[0].lower())
    2942
    2943 -    def _terms_from_docstring(self, base_url, container, parsed_docstring):
    2944          if parsed_docstring in (None, UNKNOWN): return []
    2945          terms = []
    2946          # Strip any existing anchor off:
    2947          base_url = re.sub('#.*', '', '%s' % (base_url,))
    2948          for term in parsed_docstring.index_terms():
    2949              anchor = self._term_index_to_anchor(term)
    2950              url = '%s#%s' % (base_url, anchor)
    2951              terms.append( (term.to_plaintext(None), url, container) )
    2952          return terms
    2953
    2954 -    def build_metadata_index(self, field_name):
    2955          # Build the index.
    2956          index = {}
    2957          for doc in self.indexed_docs:
    2958              if (not self._show_private and
    2959                  self._doc_or_ancestor_is_private(doc)):
    2960                  continue
    2961              descrs = {}
    2962              if doc.metadata is not UNKNOWN:
    2963                  for (field, arg, descr) in doc.metadata:
    2964                      if field.tags[0] == field_name:
    2965                          descrs.setdefault(arg, []).append(descr)
    2966              for (arg, descr_list) in descrs.iteritems():
    2967                  index.setdefault(arg, []).append( (doc, descr_list) )
    2968          return index
    2969
    2970 -    def _term_index_to_anchor(self, term):
    2971          """
    2972          Given the name of an inline index item, construct a URI anchor.
    2973          These anchors are used to create links from the index page to each
    2974          index item.
    2975          """
    2976          # Include "-" so we don't accidentally collide with the name
    2977          # of a python identifier.
    2978          s = re.sub(r'\s\s+', '-', term.to_plaintext(None))
    2979          return "index-"+re.sub("[^a-zA-Z0-9]", "_", s)
    2980
    2981      #////////////////////////////////////////////////////////////
    2982      #{ Redirect page
    2983      #////////////////////////////////////////////////////////////
    2984
    2985 -    def write_redirect_page(self, out):
    2986          """
    2987          Build the auto-redirect page, which translates dotted names to
    2988          URLs using javascript.  When the user visits
    2989          <redirect.html#dotted.name>, they will automatically get
    2990          redirected to the page for the object with the given
    2991          fully-qualified dotted name.  E.g., for epydoc,
    2992          <redirect.html#epydoc.apidoc.UNKNOWN> redirects the user to
    2993          <epydoc.apidoc-module.html#UNKNOWN>.
    2994          """
    2995          # Construct a list of all the module & class pages that we're
    2996          # documenting.  The redirect_url javascript will scan through
    2997          # this list, looking for a page name that matches the
    2998          # requested dotted name.
    2999          pages = (['%s-m' % val_doc.canonical_name
    3000                    for val_doc in self.module_list] +
    3001                   ['%s-c' % val_doc.canonical_name
    3002                    for val_doc in self.class_list])
    3003          # Sort the pages from longest to shortest.  This ensures that
    3004          # we find e.g. "x.y.z" in the list before "x.y".
    3005          pages = sorted(pages, key=lambda p:-len(p))
    3006
    3007          # Write the redirect page.
    3008          self._write_redirect_page(out, pages)
    3009
    3010      _write_redirect_page = compile_template(
    3011          '''
    3012          _write_redirect_page(self, out, pages)
    3013          ''',
    3014          # /------------------------- Template -------------------------\
    3015          '''
    3016          <html><head><title>Epydoc Redirect Page</title>
    3017          <meta http-equiv="cache-control" content="no-cache" />
    3018          <meta http-equiv="expires" content="0" />
    3019          <meta http-equiv="pragma" content="no-cache" />
    3020            <script type="text/javascript" src="epydoc.js"></script>
    3021          </head>
    3022          <body>
    3023          <script type="text/javascript">
    3024          <!--
    3025          var pages = $"[%s]" % ", ".join(['"%s"' % v for v in pages])$;
    3026          var dottedName = get_anchor();
    3027          if (dottedName) {
    3028              var target = redirect_url(dottedName);
    3029              if (target) window.location.replace(target);
    3030          }
    3031          // -->
    3032          </script>
    3033
    3034          <h3>Epydoc Auto-redirect page</h3>
    3035
    3036          <p>When javascript is enabled, this page will redirect URLs of
    3037          the form <tt>redirect.html#<i>dotted.name</i></tt> to the
    3038          documentation for the object with the given fully-qualified
    3039          dotted name.</p>
    3040          <p><a id="message"> &nbsp; </a></p>
    3041
    3042          <script type="text/javascript">
    3043          <!--
    3044          if (dottedName) {
    3045              var msg = document.getElementById("message");
    3046              msg.innerHTML = "No documentation found for <tt>"+
    3047                              dottedName+"</tt>";
    3048          }
    3049          // -->
    3050          </script>
    3051
    3052          </body>
    3053          </html>
    3054          ''')
    3055          # \------------------------------------------------------------/
    3056
    3057      #////////////////////////////////////////////////////////////
    3058      #{ URLs list
    3059      #////////////////////////////////////////////////////////////
    3060
    3061 -    def write_api_list(self, out):
    3062          """
    3063          Write a list of mapping name->url for all the documented objects.
    3064          """
    3065          # Construct a list of all the module & class pages that we're
    3066          # documenting.  The redirect_url javascript will scan through
    3067          # this list, looking for a page name that matches the
    3068          # requested dotted name.
    3069          skip = (ModuleDoc, ClassDoc, type(UNKNOWN))
    3070          for val_doc in self.module_list:
    3071              self.write_url_record(out, val_doc)
    3072              for var in val_doc.variables.itervalues():
    3073                  if not isinstance(var.value, skip):
    3074                      self.write_url_record(out, var)
    3075
    3076          for val_doc in self.class_list:
    3077              self.write_url_record(out, val_doc)
    3078              for var in val_doc.variables.itervalues():
    3079                  self.write_url_record(out, var)
    3080
    3081 -    def write_url_record(self, out, obj):
    3082          url = self.url(obj)
    3083          if url is not None:
    3084              out("%s\t%s\n" % (obj.canonical_name, url))
    3085
    3086      #////////////////////////////////////////////////////////////
    3087      #{ Helper functions
    3088      #////////////////////////////////////////////////////////////
    3089
    3090 -    def _val_is_public(self, valdoc):
    3091          """Make a best-guess as to whether the given class is public."""
    3092          container = self.docindex.container(valdoc)
    3093          if isinstance(container, NamespaceDoc):
    3094              for vardoc in container.variables.values():
    3095                  if vardoc in (UNKNOWN, None): continue
    3096                  if vardoc.value is valdoc:
    3097                      return vardoc.is_public
    3098          return True
    3099
    3100      # [XX] Is it worth-while to pull the anchor tricks that I do here?
    3101      # Or should I just live with the fact that show/hide private moves
    3102      # stuff around?
    3103      write_table_header = compile_template(
    3104          '''
    3105          write_table_header(self, out, css_class, heading=None, \
    3106                             private_link=True, colspan=2)
    3107          ''',
    3108          # /------------------------- Template -------------------------\
    3109          '''
    3110          >>> if heading is not None:
    3111          >>>     anchor = "section-%s" % re.sub("\W", "", heading)
    3112          <!-- ==================== $heading.upper()$ ==================== -->
    3113          <a name="$anchor$"></a>
    3114          >>> #endif
    3115          <table class="$css_class$" border="1" cellpadding="3"
    3116                 cellspacing="0" width="100%" bgcolor="white">
    3117          >>> if heading is not None:
    3118          <tr bgcolor="#70b0f0" class="table-header">
    3119          >>>     if private_link and self._show_private:
    3120            <td colspan="$colspan$" class="table-header">
    3121              <table border="0" cellpadding="0" cellspacing="0" width="100%">
    3122                <tr valign="top">
    3123                  <td align="left"><span class="table-header">$heading$</span></td>
    3124                  <td align="right" valign="top"
    3125                   ><span class="options">[<a href="#$anchor$"
    3126                   class="privatelink" onclick="toggle_private();"
    3127                   >hide private</a>]</span></td>
    3128                </tr>
    3129              </table>
    3130            </td>
    3131          >>>     else:
    3132            <td align="left" colspan="2" class="table-header">
    3133              <span class="table-header">$heading$</span></td>
    3134          >>>     #endif
    3135          </tr>
    3136          >>> #endif
    3137          ''')
    3138          # \------------------------------------------------------------/
    3139
    3140      TABLE_FOOTER = '</table>\n'
    3141
    3142      PRIVATE_LINK = '''
    3143      <span class="options">[<a href="javascript:void(0);" class="privatelink"
    3144      onclick="toggle_private();">hide&nbsp;private</a>]</span>
    3145      '''.strip()
    3146
    3147      write_group_header = compile_template(
    3148          '''
    3149          write_group_header(self, out, group, tr_class='')
    3150          ''',
    3151          # /------------------------- Template -------------------------\
    3152          '''
    3153          <tr bgcolor="#e8f0f8" $tr_class$>
    3154            <th colspan="2" class="group-header"
    3155              >&nbsp;&nbsp;&nbsp;&nbsp;$group$</th></tr>
    3156          ''')
    3157          # \------------------------------------------------------------/
    3158
    3159      _url_cache = {}
    3160 -    def url(self, obj):
    3161          """
    3162          Return the URL for the given object, which can be a
    3163          C{VariableDoc}, a C{ValueDoc}, or a C{DottedName}.
    3164          """
    3165          cached_url = self._url_cache.get(id(obj))
    3166          if cached_url is not None:
    3167              return cached_url
    3168          else:
    3169              url = self._url_cache[id(obj)] = self._url(obj)
    3170              return url
    3171
    3172 -    def _url(self, obj):
    3173          """
    3174          Internal helper for L{url}.
    3175          """
    3176          # Module: <canonical_name>-module.html
    3177          if isinstance(obj, ModuleDoc):
    3178              if obj not in self.module_set: return None
    3179              return urllib.quote('%s'%obj.canonical_name) + '-module.html'
    3180          # Class: <canonical_name>-class.html
    3181          elif isinstance(obj, ClassDoc):
    3182              if obj not in self.class_set: return None
    3183              return urllib.quote('%s'%obj.canonical_name) + '-class.html'
    3184          # Variable
    3185          elif isinstance(obj, VariableDoc):
    3186              val_doc = obj.value
    3187              if isinstance(val_doc, (ModuleDoc, ClassDoc)):
    3188                  return self.url(val_doc)
    3189              elif obj.container in (None, UNKNOWN):
    3190                  if val_doc in (None, UNKNOWN): return None
    3191                  return self.url(val_doc)
    3192              elif obj.is_imported == True:
    3193                  if obj.imported_from is not UNKNOWN:
    3194                      return self.url(obj.imported_from)
    3195                  else:
    3196                      return None
    3197              else:
    3198                  container_url = self.url(obj.container)
    3199                  if container_url is None: return None
    3200                  return '%s#%s' % (container_url, urllib.quote('%s'%obj.name))
    3201          # Value (other than module or class)
    3202          elif isinstance(obj, ValueDoc):
    3203              container = self.docindex.container(obj)
    3204              if container is None:
    3205                  return None # We couldn't find it!
    3206              else:
    3207                  container_url = self.url(container)
    3208                  if container_url is None: return None
    3209                  anchor = urllib.quote('%s'%obj.canonical_name[-1])
    3210                  return '%s#%s' % (container_url, anchor)
    3211          # Dotted name: look up the corresponding APIDoc
    3212          elif isinstance(obj, DottedName):
    3213              val_doc = self.docindex.get_valdoc(obj)
    3214              if val_doc is None: return None
    3215              return self.url(val_doc)
    3216          # Special pages:
    3217          elif obj == 'indices':
    3218              return 'identifier-index.html'
    3219          elif obj == 'help':
    3220              return 'help.html'
    3221          elif obj == 'trees':
    3222              return self._trees_url
    3223          else:
    3224              raise ValueError, "Don't know what to do with %r" % obj
    3225
    3226 -    def pysrc_link(self, api_doc):
    3227          if not self._incl_sourcecode:
    3228              return ''
    3229          url = self.pysrc_url(api_doc)
    3230          if url is not None:
    3231              return ('<span class="codelink"><a href="%s">source&nbsp;'
    3232                      'code</a></span>' % url)
    3233          else:
    3234              return ''
    3235
    3236 -    def pysrc_url(self, api_doc):
    3237          if isinstance(api_doc, VariableDoc):
    3238              if api_doc.value not in (None, UNKNOWN):
    3239                  return pysrc_url(api_doc.value)
    3240              else:
    3241                  return None
    3242          elif isinstance(api_doc, ModuleDoc):
    3243              if api_doc in self.modules_with_sourcecode:
    3244                  return ('%s-pysrc.html' %
    3245                         urllib.quote('%s' % api_doc.canonical_name))
    3246              else:
    3247                  return None
    3248          else:
    3249              module = api_doc.defining_module
    3250              if module == UNKNOWN: return None
    3251              module_pysrc_url = self.pysrc_url(module)
    3252              if module_pysrc_url is None: return None
    3253              module_name = module.canonical_name
    3254              if not module_name.dominates(api_doc.canonical_name, True):
    3255                  log.debug('%r is in %r but name does not dominate' %
    3256                            (api_doc, module))
    3257                  return module_pysrc_url
    3258              mname_len = len(module.canonical_name)
    3259              anchor = '%s' % api_doc.canonical_name[mname_len:]
    3260              return '%s#%s' % (module_pysrc_url, urllib.quote(anchor))
    3261
    3262          # We didn't find it:
    3263          return None
    3264
    3265      # [xx] add code to automatically do <code> wrapping or the like?
    3266 -    def href(self, target, label=None, css_class=None, context=None,
    3267               tooltip=None):
    3268          """
    3269          Return the HTML code for an HREF link to the given target
    3270          (which can be a C{VariableDoc}, a C{ValueDoc}, or a
    3271          C{DottedName}.
    3272          If a C{NamespaceDoc} C{context} is specified, the target label is
    3273          contextualized to it.
    3274          """
    3275          assert isinstance(target, (APIDoc, DottedName))
    3276
    3277          # Pick a label, if none was given.
    3278          if label is None:
    3279              if isinstance(target, VariableDoc):
    3280                  label = target.name
    3281              elif (isinstance(target, ValueDoc) and
    3282                    target.canonical_name is not UNKNOWN):
    3283                  label = target.canonical_name
    3284              elif isinstance(target, DottedName):
    3285                  label = target
    3286              elif isinstance(target, GenericValueDoc):
    3287                  raise ValueError("href() should not be called with "
    3288                                   "GenericValueDoc objects (perhaps you "
    3289                                   "meant to use the containing variable?)")
    3290              else:
    3291                  raise ValueError("Unable to find a label for %r" % target)
    3292
    3293              if context is not None and isinstance(label, DottedName):
    3294                  label = label.contextualize(context.canonical_name.container())
    3295
    3296              label = plaintext_to_html(str(label))
    3297
    3298              # Munge names for scripts & unreachable values
    3299              if label.startswith('script-'):
    3300                  label = label[7:] + ' (script)'
    3301              if label.startswith('??'):
    3302                  label = '<i>unreachable</i>' + label[2:]
    3303                  label = re.sub(r'-\d+$', '', label)
    3304
    3305          # Get the url for the target.
    3306          url = self.url(target)
    3307          if url is None:
    3308              if tooltip: return '<span title="%s">%s</span>' % (tooltip, label)
    3309              else: return label
    3310
    3311          # Construct a string for the class attribute.
    3312          if css_class is None:
    3313              css = ''
    3314          else:
    3315              css = ' class="%s"' % css_class
    3316
    3317          onclick = ''
    3318          if ((isinstance(target, VariableDoc) and not target.is_public) or
    3319              (isinstance(target, ValueDoc) and
    3320               not isinstance(target, GenericValueDoc) and
    3321               not self._val_is_public(target))):
    3322              onclick = ' onclick="show_private();"'
    3323
    3324          if tooltip:
    3325              tooltip = ' title="%s"' % tooltip
    3326          else:
    3327              tooltip = ''
    3328
    3329          return '<a href="%s"%s%s%s>%s</a>' % (url, css, onclick, tooltip, label)
    3330
    3331 -    def _attr_to_html(self, attr, api_doc, indent):
    3332          if api_doc in (None, UNKNOWN):
    3333              return ''
    3334          pds = getattr(api_doc, attr, None) # pds = ParsedDocstring.
    3335          if pds not in (None, UNKNOWN):
    3336              return self.docstring_to_html(pds, api_doc, indent)
    3337          elif isinstance(api_doc, VariableDoc):
    3338              return self._attr_to_html(attr, api_doc.value, indent)
    3339
    3340 -    def summary(self, api_doc, indent=0):
    3341          return self._attr_to_html('summary', api_doc, indent)
    3342
    3343 -    def descr(self, api_doc, indent=0):
    3344          return self._attr_to_html('descr', api_doc, indent)
    3345
    3346 -    def type_descr(self, api_doc, indent=0):
    3347          return self._attr_to_html('type_descr', api_doc, indent)
    3348
    3349 -    def return_type(self, api_doc, indent=0):
    3350          return self._attr_to_html('return_type', api_doc, indent)
    3351
    3352 -    def return_descr(self, api_doc, indent=0):
    3353          return self._attr_to_html('return_descr', api_doc, indent)
    3354
    3355 -    def docstring_to_html(self, parsed_docstring, where=None, indent=0):
    3356          if parsed_docstring in (None, UNKNOWN): return ''
    3357          linker = _HTMLDocstringLinker(self, where)
    3358          s = parsed_docstring.to_html(linker, indent=indent,
    3359                                       directory=self._directory,
    3360                                       docindex=self.docindex,
    3361                                       context=where).strip()
    3362          if self._mark_docstrings:
    3363              s = '<span class="docstring">%s</span><!--end docstring-->' % s
    3364          return s
    3365
    3366 -    def description(self, parsed_docstring, where=None, indent=0):
    3367          assert isinstance(where, (APIDoc, type(None)))
    3368          if parsed_docstring in (None, UNKNOWN): return ''
    3369          linker = _HTMLDocstringLinker(self, where)
    3370          descr = parsed_docstring.to_html(linker, indent=indent,
    3371                                           directory=self._directory,
    3372                                           docindex=self.docindex,
    3373                                           context=where).strip()
    3374          if descr == '': return '&nbsp;'
    3375          return descr
    3376
    3377      # [xx] Should this be defined by the APIDoc classes themselves??
    3378 -    def doc_kind(self, doc):
    3379          if isinstance(doc, ModuleDoc) and doc.is_package == True:
    3380              return 'Package'
    3381          elif (isinstance(doc, ModuleDoc) and
    3382                doc.canonical_name[0].startswith('script')):
    3383              return 'Script'
    3384          elif isinstance(doc, ModuleDoc):
    3385              return 'Module'
    3386          elif isinstance(doc, ClassDoc):
    3387              return 'Class'
    3388          elif isinstance(doc, ClassMethodDoc):
    3389              return 'Class Method'
    3390          elif isinstance(doc, StaticMethodDoc):
    3391              return 'Static Method'
    3392          elif isinstance(doc, RoutineDoc):
    3393              if isinstance(self.docindex.container(doc), ClassDoc):
    3394                  return 'Method'
    3395              else:
    3396                  return 'Function'
    3397          else:
    3398              return 'Variable'
    3399
    3400 -    def _doc_or_ancestor_is_private(self, api_doc):
    3401          name = api_doc.canonical_name
    3402          for i in range(len(name), 0, -1):
    3403              # Is it (or an ancestor) a private var?
    3404              var_doc = self.docindex.get_vardoc(name[:i])
    3405              if var_doc is not None and var_doc.is_public == False:
    3406                  return True
    3407              # Is it (or an ancestor) a private module?
    3408              val_doc = self.docindex.get_valdoc(name[:i])
    3409              if (val_doc is not None and isinstance(val_doc, ModuleDoc) and
    3410                  val_doc.canonical_name[-1].startswith('_')):
    3411                  return True
    3412          return False
    3413
    3414 -    def _private_subclasses(self, class_doc):
    3415          """Return a list of all subclasses of the given class that are
    3416          private, as determined by L{_val_is_private}.  Recursive
    3417          subclasses are included in this list."""
    3418          queue = [class_doc]
    3419          private = set()
    3420          for cls in queue:
    3421              if (isinstance(cls, ClassDoc) and
    3422                  cls.subclasses not in (None, UNKNOWN)):
    3423                  queue.extend(cls.subclasses)
    3424                  private.update([c for c in cls.subclasses if
    3425                                  not self._val_is_public(c)])
    3426          return private
    3427
    3428 -class _HTMLDocstringLinker(epydoc.markup.DocstringLinker):
    3429 -    def __init__(self, htmlwriter, container):
    3430          self.htmlwriter = htmlwriter
    3431          self.docindex = htmlwriter.docindex
    3432          self.container = container
    3433
    3434 -    def translate_indexterm(self, indexterm):
    3435          key = self.htmlwriter._term_index_to_anchor(indexterm)
    3436          return ('<a name="%s"></a><i class="indexterm">%s</i>' %
    3437                  (key, indexterm.to_html(self)))
    3438
    3439 -    def translate_identifier_xref(self, identifier, label=None):
    3440          # Pick a label for this xref.
    3441          if label is None: label = plaintext_to_html(identifier)
    3442
    3443          # Find the APIDoc for it (if it's available).
    3444          doc = self.docindex.find(identifier, self.container)
    3445
    3446          # If we didn't find a target, then try checking in the contexts
    3447          # of the ancestor classes.
    3448          if doc is None and isinstance(self.container, RoutineDoc):
    3449              container = self.docindex.get_vardoc(
    3450                  self.container.canonical_name)
    3451              while (doc is None and container not in (None, UNKNOWN)
    3452                     and container.overrides not in (None, UNKNOWN)):
    3453                  container = container.overrides
    3454                  doc = self.docindex.find(identifier, container)
    3455
    3456          # Translate it into HTML.
    3457          if doc is None:
    3458              self._failed_xref(identifier)
    3459              return '<code class="link">%s</code>' % label
    3460          else:
    3461              return self.htmlwriter.href(doc, label, 'link')
    3462
    3463      # [xx] Should this be added to the DocstringLinker interface???
    3464      # Currently, this is *only* used by dotgraph.
    3465 -    def url_for(self, identifier):
    3466          if isinstance(identifier, (basestring, DottedName)):
    3467              doc = self.docindex.find(identifier, self.container)
    3468              if doc:
    3469                  return self.htmlwriter.url(doc)
    3470              else:
    3471                  return None
    3472
    3473          elif isinstance(identifier, APIDoc):
    3474              return self.htmlwriter.url(identifier)
    3475              doc = identifier
    3476
    3477          else:
    3478              raise TypeError('Expected string or APIDoc')
    3479
    3480 -    def _failed_xref(self, identifier):
    3481          """Add an identifier to the htmlwriter's failed crossreference
    3482          list."""
    3483          # Don't count it as a failed xref if it's a parameter of the
    3484          # current function.
    3485          if (isinstance(self.container, RoutineDoc) and
    3486              identifier in self.container.all_args()):
    3487              return
    3488
    3489          failed_xrefs = self.htmlwriter._failed_xrefs
    3490          context = self.container.canonical_name
    3491          failed_xrefs.setdefault(identifier,{})[context] = 1
    3492