/python/helpers/epydoc/docwriter/html.py
Python | 3491 lines | 3150 code | 99 blank | 242 comment | 188 complexity | 42cd7770ed9539825cbbc40e0712c977 MD5 | raw file
Possible License(s): BSD-3-Clause, Apache-2.0, MPL-2.0-no-copyleft-exception, MIT, EPL-1.0, AGPL-1.0
- #
- # epydoc -- HTML output generator
- # Edward Loper
- #
- # Created [01/30/01 05:18 PM]
- # $Id: html.py 1674 2008-01-29 06:03:36Z edloper $
- #
- """
- The HTML output generator for epydoc. The main interface provided by
- this module is the L{HTMLWriter} class.
- @todo: Add a cache to L{HTMLWriter.url()}?
- """
- __docformat__ = 'epytext en'
- import re, os, sys, codecs, sre_constants, pprint, base64
- import urllib
- import __builtin__
- from epydoc.apidoc import *
- import epydoc.docstringparser
- import time, epydoc, epydoc.markup, epydoc.markup.epytext
- from epydoc.docwriter.html_colorize import PythonSourceColorizer
- from epydoc.docwriter import html_colorize
- from epydoc.docwriter.html_css import STYLESHEETS
- from epydoc.docwriter.html_help import HTML_HELP
- from epydoc.docwriter.dotgraph import *
- from epydoc import log
- from epydoc.util import plaintext_to_html, is_src_filename
- from epydoc.compat import * # Backwards compatibility
- ######################################################################
- ## Template Compiler
- ######################################################################
- # The compile_template() method defined in this section is used to
- # define several of HTMLWriter's methods.
- def compile_template(docstring, template_string,
- output_function='out', debug=epydoc.DEBUG):
- """
- Given a template string containing inline python source code,
- return a python function that will fill in the template, and
- output the result. The signature for this function is taken from
- the first line of C{docstring}. Output is generated by making
- repeated calls to the output function with the given name (which
- is typically one of the function's parameters).
- The templating language used by this function passes through all
- text as-is, with three exceptions:
- - If every line in the template string is indented by at least
- M{x} spaces, then the first M{x} spaces are stripped from each
- line.
- - Any line that begins with '>>>' (with no indentation)
- should contain python code, and will be inserted as-is into
- the template-filling function. If the line begins a control
- block (such as 'if' or 'for'), then the control block will
- be closed by the first '>>>'-marked line whose indentation is
- less than or equal to the line's own indentation (including
- lines that only contain comments.)
- - In any other line, any expression between two '$' signs will
- be evaluated and inserted into the line (using C{str()} to
- convert the result to a string).
- Here is a simple example:
- >>> TEMPLATE = '''
- ... <book>
- ... <title>$book.title$</title>
- ... <pages>$book.count_pages()$</pages>
- ... >>> for chapter in book.chapters:
- ... <chaptername>$chapter.name$</chaptername>
- ... >>> #endfor
- ... </book>
- >>> write_book = compile_template('write_book(out, book)', TEMPLATE)
- @newfield acknowledgements: Acknowledgements
- @acknowledgements: The syntax used by C{compile_template} is
- loosely based on Cheetah.
- """
- # Extract signature from the docstring:
- signature = docstring.lstrip().split('\n',1)[0].strip()
- func_name = signature.split('(',1)[0].strip()
- # Regexp to search for inline substitutions:
- INLINE = re.compile(r'\$([^\$]+)\$')
- # Regexp to search for python statements in the template:
- COMMAND = re.compile(r'(^>>>.*)\n?', re.MULTILINE)
- # Strip indentation from the template.
- template_string = strip_indent(template_string)
- # If we're debugging, then we'll store the generated function,
- # so we can print it along with any tracebacks that depend on it.
- if debug:
- signature = re.sub(r'\)\s*$', ', __debug=__debug)', signature)
- # Funciton declaration line
- pysrc_lines = ['def %s:' % signature]
- indents = [-1]
- if debug:
- pysrc_lines.append(' try:')
- indents.append(-1)
- commands = COMMAND.split(template_string.strip()+'\n')
- for i, command in enumerate(commands):
- if command == '': continue
- # String literal segment:
- if i%2 == 0:
- pieces = INLINE.split(command)
- for j, piece in enumerate(pieces):
- if j%2 == 0:
- # String piece
- pysrc_lines.append(' '*len(indents)+
- '%s(%r)' % (output_function, piece))
- else:
- # Variable piece
- pysrc_lines.append(' '*len(indents)+
- '%s(unicode(%s))' % (output_function, piece))
- # Python command:
- else:
- srcline = command[3:].lstrip()
- # Update indentation
- indent = len(command)-len(srcline)
- while indent <= indents[-1]: indents.pop()
- # Add on the line.
- srcline = srcline.rstrip()
- pysrc_lines.append(' '*len(indents)+srcline)
- if srcline.endswith(':'):
- indents.append(indent)
-
- if debug:
- pysrc_lines.append(' except Exception,e:')
- pysrc_lines.append(' pysrc, func_name = __debug ')
- pysrc_lines.append(' lineno = sys.exc_info()[2].tb_lineno')
- pysrc_lines.append(' print ("Exception in template %s() on "')
- pysrc_lines.append(' "line %d:" % (func_name, lineno))')
- pysrc_lines.append(' print pysrc[lineno-1]')
- pysrc_lines.append(' raise')
-
- pysrc = '\n'.join(pysrc_lines)+'\n'
- #log.debug(pysrc)
- if debug: localdict = {'__debug': (pysrc_lines, func_name)}
- else: localdict = {}
- try: exec pysrc in globals(), localdict
- except SyntaxError:
- log.error('Error in script:\n' + pysrc + '\n')
- raise
- template_func = localdict[func_name]
- template_func.__doc__ = docstring
- return template_func
-
- def strip_indent(s):
- """
- Given a multiline string C{s}, find the minimum indentation for
- all non-blank lines, and return a new string formed by stripping
- that amount of indentation from all lines in C{s}.
- """
- # Strip indentation from the template.
- minindent = sys.maxint
- lines = s.split('\n')
- for line in lines:
- stripline = line.lstrip()
- if stripline:
- minindent = min(minindent, len(line)-len(stripline))
- return '\n'.join([l[minindent:] for l in lines])
- ######################################################################
- ## HTML Writer
- ######################################################################
- class HTMLWriter:
- #////////////////////////////////////////////////////////////
- # Table of Contents
- #////////////////////////////////////////////////////////////
- #
- # 1. Interface Methods
- #
- # 2. Page Generation -- write complete web page files
- # 2.1. Module Pages
- # 2.2. Class Pages
- # 2.3. Trees Page
- # 2.4. Indices Page
- # 2.5. Help Page
- # 2.6. Frames-based table of contents pages
- # 2.7. Homepage (index.html)
- # 2.8. CSS Stylesheet
- # 2.9. Javascript file
- # 2.10. Graphs
- # 2.11. Images
- #
- # 3. Page Element Generation -- write pieces of a web page file
- # 3.1. Page Header
- # 3.2. Page Footer
- # 3.3. Navigation Bar
- # 3.4. Breadcrumbs
- # 3.5. Summary Tables
- #
- # 4. Helper functions
- def __init__(self, docindex, **kwargs):
- """
- Construct a new HTML writer, using the given documentation
- index.
-
- @param docindex: The documentation index.
-
- @type prj_name: C{string}
- @keyword prj_name: The name of the project. Defaults to
- none.
- @type prj_url: C{string}
- @keyword prj_url: The target for the project hopeage link on
- the navigation bar. If C{prj_url} is not specified,
- then no hyperlink is created.
- @type prj_link: C{string}
- @keyword prj_link: The label for the project link on the
- navigation bar. This link can contain arbitrary HTML
- code (e.g. images). By default, a label is constructed
- from C{prj_name}.
- @type top_page: C{string}
- @keyword top_page: The top page for the documentation. This
- is the default page shown main frame, when frames are
- enabled. C{top} can be a URL, the name of a
- module, the name of a class, or one of the special
- strings C{"trees.html"}, C{"indices.html"}, or
- C{"help.html"}. By default, the top-level package or
- module is used, if there is one; otherwise, C{"trees"}
- is used.
- @type css: C{string}
- @keyword css: The CSS stylesheet file. If C{css} is a file
- name, then the specified file's conents will be used.
- Otherwise, if C{css} is the name of a CSS stylesheet in
- L{epydoc.docwriter.html_css}, then that stylesheet will
- be used. Otherwise, an error is reported. If no stylesheet
- is specified, then the default stylesheet is used.
- @type help_file: C{string}
- @keyword help_file: The name of the help file. If no help file is
- specified, then the default help file will be used.
- @type show_private: C{boolean}
- @keyword show_private: Whether to create documentation for
- private objects. By default, private objects are documented.
- @type show_frames: C{boolean})
- @keyword show_frames: Whether to create a frames-based table of
- contents. By default, it is produced.
- @type show_imports: C{boolean}
- @keyword show_imports: Whether or not to display lists of
- imported functions and classes. By default, they are
- not shown.
- @type variable_maxlines: C{int}
- @keyword variable_maxlines: The maximum number of lines that
- should be displayed for the value of a variable in the
- variable details section. By default, 8 lines are
- displayed.
- @type variable_linelength: C{int}
- @keyword variable_linelength: The maximum line length used for
- displaying the values of variables in the variable
- details sections. If a line is longer than this length,
- then it will be wrapped to the next line. The default
- line length is 70 characters.
- @type variable_summary_linelength: C{int}
- @keyword variable_summary_linelength: The maximum line length
- used for displaying the values of variables in the summary
- section. If a line is longer than this length, then it
- will be truncated. The default is 40 characters.
- @type variable_tooltip_linelength: C{int}
- @keyword variable_tooltip_linelength: The maximum line length
- used for tooltips for the values of variables. If a
- line is longer than this length, then it will be
- truncated. The default is 600 characters.
- @type property_function_linelength: C{int}
- @keyword property_function_linelength: The maximum line length
- used to dispaly property functions (C{fget}, C{fset}, and
- C{fdel}) that contain something other than a function
- object. The default length is 40 characters.
- @type inheritance: C{string}
- @keyword inheritance: How inherited objects should be displayed.
- If C{inheritance='grouped'}, then inherited objects are
- gathered into groups; if C{inheritance='listed'}, then
- inherited objects are listed in a short list at the
- end of their group; if C{inheritance='included'}, then
- inherited objects are mixed in with non-inherited
- objects. The default is 'grouped'.
- @type include_source_code: C{boolean}
- @keyword include_source_code: If true, then generate colorized
- source code files for each python module.
- @type include_log: C{boolean}
- @keyword include_log: If true, the the footer will include an
- href to the page 'epydoc-log.html'.
- @type src_code_tab_width: C{int}
- @keyword src_code_tab_width: Number of spaces to replace each tab
- with in source code listings.
- """
- self.docindex = docindex
- # Process keyword arguments.
- self._show_private = kwargs.get('show_private', 1)
- """Should private docs be included?"""
-
- self._prj_name = kwargs.get('prj_name', None)
- """The project's name (for the project link in the navbar)"""
-
- self._prj_url = kwargs.get('prj_url', None)
- """URL for the project link in the navbar"""
-
- self._prj_link = kwargs.get('prj_link', None)
- """HTML code for the project link in the navbar"""
-
- self._top_page = kwargs.get('top_page', None)
- """The 'main' page"""
- self._css = kwargs.get('css')
- """CSS stylesheet to use"""
-
- self._helpfile = kwargs.get('help_file', None)
- """Filename of file to extract help contents from"""
-
- self._frames_index = kwargs.get('show_frames', 1)
- """Should a frames index be created?"""
-
- self._show_imports = kwargs.get('show_imports', False)
- """Should imports be listed?"""
-
- self._propfunc_linelen = kwargs.get('property_function_linelength', 40)
- """[XXX] Not used!"""
-
- self._variable_maxlines = kwargs.get('variable_maxlines', 8)
- """Max lines for variable values"""
-
- self._variable_linelen = kwargs.get('variable_linelength', 70)
- """Max line length for variable values"""
-
- self._variable_summary_linelen = \
- kwargs.get('variable_summary_linelength', 65)
- """Max length for variable value summaries"""
-
- self._variable_tooltip_linelen = \
- kwargs.get('variable_tooltip_linelength', 600)
- """Max length for variable tooltips"""
-
- self._inheritance = kwargs.get('inheritance', 'listed')
- """How should inheritance be displayed? 'listed', 'included',
- or 'grouped'"""
- self._incl_sourcecode = kwargs.get('include_source_code', True)
- """Should pages be generated for source code of modules?"""
- self._mark_docstrings = kwargs.get('mark_docstrings', False)
- """Wrap <span class='docstring'>...</span> around docstrings?"""
- self._graph_types = kwargs.get('graphs', ()) or ()
- """Graphs that we should include in our output."""
- self._include_log = kwargs.get('include_log', False)
- """Are we generating an HTML log page?"""
- self._src_code_tab_width = kwargs.get('src_code_tab_width', 8)
- """Number of spaces to replace each tab with in source code
- listings."""
-
- self._callgraph_cache = {}
- """Map the callgraph L{uid<DotGraph.uid>} to their HTML
- representation."""
- self._redundant_details = kwargs.get('redundant_details', False)
- """If true, then include objects in the details list even if all
- info about them is already provided by the summary table."""
- # For use with select_variables():
- if self._show_private:
- self._public_filter = None
- else:
- self._public_filter = True
-
- # Make sure inheritance has a sane value.
- if self._inheritance not in ('listed', 'included', 'grouped'):
- raise ValueError, 'Bad value for inheritance'
- # Create the project homepage link, if it was not specified.
- if (self._prj_name or self._prj_url) and not self._prj_link:
- self._prj_link = plaintext_to_html(self._prj_name or
- 'Project Homepage')
- # Add a hyperlink to _prj_url, if _prj_link doesn't already
- # contain any hyperlinks.
- if (self._prj_link and self._prj_url and
- not re.search(r'<a[^>]*\shref', self._prj_link)):
- self._prj_link = ('<a class="navbar" target="_top" href="'+
- self._prj_url+'">'+self._prj_link+'</a>')
- # Precompute lists & sets of APIDoc objects that we're
- # interested in.
- self.valdocs = valdocs = sorted(docindex.reachable_valdocs(
- imports=False, packages=False, bases=False, submodules=False,
- subclasses=False, private=self._show_private))
- self.module_list = [d for d in valdocs if isinstance(d, ModuleDoc)]
- """The list of L{ModuleDoc}s for the documented modules."""
- self.module_set = set(self.module_list)
- """The set of L{ModuleDoc}s for the documented modules."""
- self.class_list = [d for d in valdocs if isinstance(d, ClassDoc)]
- """The list of L{ClassDoc}s for the documented classes."""
- self.class_set = set(self.class_list)
- """The set of L{ClassDoc}s for the documented classes."""
- self.routine_list = [d for d in valdocs if isinstance(d, RoutineDoc)]
- """The list of L{RoutineDoc}s for the documented routines."""
- self.indexed_docs = []
- """The list of L{APIDoc}s for variables and values that should
- be included in the index."""
- # URL for 'trees' page
- if self.module_list: self._trees_url = 'module-tree.html'
- else: self._trees_url = 'class-tree.html'
- # Construct the value for self.indexed_docs.
- self.indexed_docs += [d for d in valdocs
- if not isinstance(d, GenericValueDoc)]
- for doc in valdocs:
- if isinstance(doc, NamespaceDoc):
- # add any vars with generic values; but don't include
- # inherited vars.
- self.indexed_docs += [d for d in doc.variables.values() if
- isinstance(d.value, GenericValueDoc)
- and d.container == doc]
- self.indexed_docs.sort()
- # Figure out the url for the top page.
- self._top_page_url = self._find_top_page(self._top_page)
- # Decide whether or not to split the identifier index.
- self._split_ident_index = (len(self.indexed_docs) >=
- self.SPLIT_IDENT_INDEX_SIZE)
-
- # Figure out how many output files there will be (for progress
- # reporting).
- self.modules_with_sourcecode = set()
- for doc in self.module_list:
- if isinstance(doc, ModuleDoc) and is_src_filename(doc.filename):
- self.modules_with_sourcecode.add(doc)
- self._num_files = (len(self.class_list) + len(self.module_list) +
- 10 + len(self.METADATA_INDICES))
- if self._frames_index:
- self._num_files += len(self.module_list) + 3
- if self._incl_sourcecode:
- self._num_files += len(self.modules_with_sourcecode)
- if self._split_ident_index:
- self._num_files += len(self.LETTERS)
-
- def _find_top_page(self, pagename):
- """
- Find the top page for the API documentation. This page is
- used as the default page shown in the main frame, when frames
- are used. When frames are not used, this page is copied to
- C{index.html}.
- @param pagename: The name of the page, as specified by the
- keyword argument C{top} to the constructor.
- @type pagename: C{string}
- @return: The URL of the top page.
- @rtype: C{string}
- """
- # If a page name was specified, then we need to figure out
- # what it points to.
- if pagename:
- # If it's a URL, then use it directly.
- if pagename.lower().startswith('http:'):
- return pagename
- # If it's an object, then use that object's page.
- try:
- doc = self.docindex.get_valdoc(pagename)
- return self.url(doc)
- except:
- pass
- # Otherwise, give up.
- log.warning('Could not find top page %r; using %s '
- 'instead' % (pagename, self._trees_url))
- return self._trees_url
- # If no page name was specified, then try to choose one
- # automatically.
- else:
- root = [val_doc for val_doc in self.docindex.root
- if isinstance(val_doc, (ClassDoc, ModuleDoc))]
- if len(root) == 0:
- # No docs?? Try the trees page.
- return self._trees_url
- elif len(root) == 1:
- # One item in the root; use that.
- return self.url(root[0])
- else:
- # Multiple root items; if they're all in one package,
- # then use that. Otherwise, use self._trees_url
- root = sorted(root, key=lambda v:len(v.canonical_name))
- top = root[0]
- for doc in root[1:]:
- if not top.canonical_name.dominates(doc.canonical_name):
- return self._trees_url
- else:
- return self.url(top)
-
- #////////////////////////////////////////////////////////////
- #{ 1. Interface Methods
- #////////////////////////////////////////////////////////////
- def write(self, directory=None):
- """
- Write the documentation to the given directory.
- @type directory: C{string}
- @param directory: The directory to which output should be
- written. If no directory is specified, output will be
- written to the current directory. If the directory does
- not exist, it will be created.
- @rtype: C{None}
- @raise OSError: If C{directory} cannot be created.
- @raise OSError: If any file cannot be created or written to.
- """
- # For progress reporting:
- self._files_written = 0.
-
- # Set the default values for ValueDoc formatted representations.
- orig_valdoc_defaults = (ValueDoc.SUMMARY_REPR_LINELEN,
- ValueDoc.REPR_LINELEN,
- ValueDoc.REPR_MAXLINES)
- ValueDoc.SUMMARY_REPR_LINELEN = self._variable_summary_linelen
- ValueDoc.REPR_LINELEN = self._variable_linelen
- ValueDoc.REPR_MAXLINES = self._variable_maxlines
- # Use an image for the crarr symbol.
- from epydoc.markup.epytext import ParsedEpytextDocstring
- orig_crarr_html = ParsedEpytextDocstring.SYMBOL_TO_HTML['crarr']
- ParsedEpytextDocstring.SYMBOL_TO_HTML['crarr'] = (
- r'<span class="variable-linewrap">'
- r'<img src="crarr.png" alt="\" /></span>')
- # Keep track of failed xrefs, and report them at the end.
- self._failed_xrefs = {}
- # Create destination directories, if necessary
- if not directory: directory = os.curdir
- self._mkdir(directory)
- self._directory = directory
- # Write the CSS file.
- self._files_written += 1
- log.progress(self._files_written/self._num_files, 'epydoc.css')
- self.write_css(directory, self._css)
- # Write the Javascript file.
- self._files_written += 1
- log.progress(self._files_written/self._num_files, 'epydoc.js')
- self.write_javascript(directory)
- # Write images
- self.write_images(directory)
- # Build the indices.
- indices = {'ident': self.build_identifier_index(),
- 'term': self.build_term_index()}
- for (name, label, label2) in self.METADATA_INDICES:
- indices[name] = self.build_metadata_index(name)
- # Write the identifier index. If requested, split it into
- # separate pages for each letter.
- ident_by_letter = self._group_by_letter(indices['ident'])
- if not self._split_ident_index:
- self._write(self.write_link_index, directory,
- 'identifier-index.html', indices,
- 'Identifier Index', 'identifier-index.html',
- ident_by_letter)
- else:
- # Write a page for each section.
- for letter in self.LETTERS:
- filename = 'identifier-index-%s.html' % letter
- self._write(self.write_link_index, directory, filename,
- indices, 'Identifier Index', filename,
- ident_by_letter, [letter],
- 'identifier-index-%s.html')
- # Use the first non-empty section as the main index page.
- for letter in self.LETTERS:
- if letter in ident_by_letter:
- filename = 'identifier-index.html'
- self._write(self.write_link_index, directory, filename,
- indices, 'Identifier Index', filename,
- ident_by_letter, [letter],
- 'identifier-index-%s.html')
- break
- # Write the term index.
- if indices['term']:
- term_by_letter = self._group_by_letter(indices['term'])
- self._write(self.write_link_index, directory, 'term-index.html',
- indices, 'Term Definition Index',
- 'term-index.html', term_by_letter)
- else:
- self._files_written += 1 # (skipped)
- # Write the metadata indices.
- for (name, label, label2) in self.METADATA_INDICES:
- if indices[name]:
- self._write(self.write_metadata_index, directory,
- '%s-index.html' % name, indices, name,
- label, label2)
- else:
- self._files_written += 1 # (skipped)
- # Write the trees file (package & class hierarchies)
- if self.module_list:
- self._write(self.write_module_tree, directory, 'module-tree.html')
- else:
- self._files_written += 1 # (skipped)
- if self.class_list:
- self._write(self.write_class_tree, directory, 'class-tree.html')
- else:
- self._files_written += 1 # (skipped)
-
- # Write the help file.
- self._write(self.write_help, directory,'help.html')
-
- # Write the frames-based table of contents.
- if self._frames_index:
- self._write(self.write_frames_index, directory, 'frames.html')
- self._write(self.write_toc, directory, 'toc.html')
- self._write(self.write_project_toc, directory, 'toc-everything.html')
- for doc in self.module_list:
- filename = 'toc-%s' % urllib.unquote(self.url(doc))
- self._write(self.write_module_toc, directory, filename, doc)
- # Write the object documentation.
- for doc in self.module_list:
- filename = urllib.unquote(self.url(doc))
- self._write(self.write_module, directory, filename, doc)
- for doc in self.class_list:
- filename = urllib.unquote(self.url(doc))
- self._write(self.write_class, directory, filename, doc)
- # Write source code files.
- if self._incl_sourcecode:
- # Build a map from short names to APIDocs, used when
- # linking names in the source code.
- name_to_docs = {}
- for api_doc in self.indexed_docs:
- if (api_doc.canonical_name is not None and
- self.url(api_doc) is not None):
- name = api_doc.canonical_name[-1]
- name_to_docs.setdefault(name, []).append(api_doc)
- # Sort each entry of the name_to_docs list.
- for doc_list in name_to_docs.values():
- doc_list.sort()
- # Write the source code for each module.
- for doc in self.modules_with_sourcecode:
- filename = urllib.unquote(self.pysrc_url(doc))
- self._write(self.write_sourcecode, directory, filename, doc,
- name_to_docs)
- # Write the auto-redirect page.
- self._write(self.write_redirect_page, directory, 'redirect.html')
- # Write the mapping object name -> URL
- self._write(self.write_api_list, directory, 'api-objects.txt')
-
- # Write the index.html files.
- # (this must be done last, since it might copy another file)
- self._files_written += 1
- log.progress(self._files_written/self._num_files, 'index.html')
- self.write_homepage(directory)
- # Don't report references to builtins as missing
- for k in self._failed_xrefs.keys(): # have a copy of keys
- if hasattr(__builtin__, k):
- del self._failed_xrefs[k]
- # Report any failed crossreferences
- if self._failed_xrefs:
- estr = 'Failed identifier crossreference targets:\n'
- failed_identifiers = self._failed_xrefs.keys()
- failed_identifiers.sort()
- for identifier in failed_identifiers:
- names = self._failed_xrefs[identifier].keys()
- names.sort()
- estr += '- %s' % identifier
- estr += '\n'
- for name in names:
- estr += ' (from %s)\n' % name
- log.docstring_warning(estr)
- # [xx] testing:
- if self._num_files != int(self._files_written):
- log.debug("Expected to write %d files, but actually "
- "wrote %d files" %
- (self._num_files, int(self._files_written)))
- # Restore defaults that we changed.
- (ValueDoc.SUMMARY_REPR_LINELEN, ValueDoc.REPR_LINELEN,
- ValueDoc.REPR_MAXLINES) = orig_valdoc_defaults
- ParsedEpytextDocstring.SYMBOL_TO_HTML['crarr'] = orig_crarr_html
- def _write(self, write_func, directory, filename, *args):
- # Display our progress.
- self._files_written += 1
- log.progress(self._files_written/self._num_files, filename)
-
- path = os.path.join(directory, filename)
- f = codecs.open(path, 'w', 'ascii', errors='xmlcharrefreplace')
- write_func(f.write, *args)
- f.close()
- def _mkdir(self, directory):
- """
- If the given directory does not exist, then attempt to create it.
- @rtype: C{None}
- """
- if not os.path.isdir(directory):
- if os.path.exists(directory):
- raise OSError('%r is not a directory' % directory)
- os.mkdir(directory)
-
- #////////////////////////////////////////////////////////////
- #{ 2.1. Module Pages
- #////////////////////////////////////////////////////////////
- def write_module(self, out, doc):
- """
- Write an HTML page containing the API documentation for the
- given module to C{out}.
-
- @param doc: A L{ModuleDoc} containing the API documentation
- for the module that should be described.
- """
- longname = doc.canonical_name
- shortname = doc.canonical_name[-1]
- # Write the page header (incl. navigation bar & breadcrumbs)
- self.write_header(out, str(longname))
- self.write_navbar(out, doc)
- self.write_breadcrumbs(out, doc, self.url(doc))
- # Write the name of the module we're describing.
- if doc.is_package is True: typ = 'Package'
- else: typ = 'Module'
- if longname[0].startswith('script-'):
- shortname = str(longname)[7:]
- typ = 'Script'
- out('<!-- ==================== %s ' % typ.upper() +
- 'DESCRIPTION ==================== -->\n')
- out('<h1 class="epydoc">%s %s</h1>' % (typ, shortname))
- out('<p class="nomargin-top">%s</p>\n' % self.pysrc_link(doc))
-
- # If the module has a description, then list it.
- if doc.descr not in (None, UNKNOWN):
- out(self.descr(doc, 2)+'\n\n')
- # Write any standarad metadata (todo, author, etc.)
- if doc.metadata is not UNKNOWN and doc.metadata:
- out('<hr />\n')
- self.write_standard_fields(out, doc)
- # If it's a package, then list the modules it contains.
- if doc.is_package is True:
- self.write_module_list(out, doc)
- # Write summary tables describing the variables that the
- # module defines.
- self.write_summary_table(out, "Classes", doc, "class")
- self.write_summary_table(out, "Functions", doc, "function")
- self.write_summary_table(out, "Variables", doc, "other")
- # Write a list of all imported objects.
- if self._show_imports:
- self.write_imports(out, doc)
- # Write detailed descriptions of functions & variables defined
- # in this module.
- self.write_details_list(out, "Function Details", doc, "function")
- self.write_details_list(out, "Variables Details", doc, "other")
- # Write the page footer (including navigation bar)
- self.write_navbar(out, doc)
- self.write_footer(out)
- #////////////////////////////////////////////////////////////
- #{ 2.??. Source Code Pages
- #////////////////////////////////////////////////////////////
- def write_sourcecode(self, out, doc, name_to_docs):
- #t0 = time.time()
-
- filename = doc.filename
- name = str(doc.canonical_name)
-
- # Header
- self.write_header(out, name)
- self.write_navbar(out, doc)
- self.write_breadcrumbs(out, doc, self.pysrc_url(doc))
- # Source code listing
- out('<h1 class="epydoc">Source Code for %s</h1>\n' %
- self.href(doc, label='%s %s' % (self.doc_kind(doc), name)))
- out('<pre class="py-src">\n')
- out(PythonSourceColorizer(filename, name, self.docindex,
- self.url, name_to_docs,
- self._src_code_tab_width).colorize())
- out('</pre>\n<br />\n')
- # Footer
- self.write_navbar(out, doc)
- self.write_footer(out)
-
- #log.debug('[%6.2f sec] Wrote pysrc for %s' %
- # (time.time()-t0, name))
- #////////////////////////////////////////////////////////////
- #{ 2.2. Class Pages
- #////////////////////////////////////////////////////////////
- def write_class(self, out, doc):
- """
- Write an HTML page containing the API documentation for the
- given class to C{out}.
-
- @param doc: A L{ClassDoc} containing the API documentation
- for the class that should be described.
- """
- longname = doc.canonical_name
- shortname = doc.canonical_name[-1]
- # Write the page header (incl. navigation bar & breadcrumbs)
- self.write_header(out, str(longname))
- self.write_navbar(out, doc)
- self.write_breadcrumbs(out, doc, self.url(doc))
- # Write the name of the class we're describing.
- if doc.is_type(): typ = 'Type'
- elif doc.is_exception(): typ = 'Exception'
- else: typ = 'Class'
- out('<!-- ==================== %s ' % typ.upper() +
- 'DESCRIPTION ==================== -->\n')
- out('<h1 class="epydoc">%s %s</h1>' % (typ, shortname))
- out('<p class="nomargin-top">%s</p>\n' % self.pysrc_link(doc))
- if ((doc.bases not in (UNKNOWN, None) and len(doc.bases) > 0) or
- (doc.subclasses not in (UNKNOWN,None) and len(doc.subclasses)>0)):
- # Display bases graphically, if requested.
- if 'umlclasstree' in self._graph_types:
- self.write_class_tree_graph(out, doc, uml_class_tree_graph)
- elif 'classtree' in self._graph_types:
- self.write_class_tree_graph(out, doc, class_tree_graph)
-
- # Otherwise, use ascii-art.
- else:
- # Write the base class tree.
- if doc.bases not in (UNKNOWN, None) and len(doc.bases) > 0:
- out('<pre class="base-tree">\n%s</pre>\n\n' %
- self.base_tree(doc))
- # Write the known subclasses
- if (doc.subclasses not in (UNKNOWN, None) and
- len(doc.subclasses) > 0):
- out('<dl><dt>Known Subclasses:</dt>\n<dd>\n ')
- out(' <ul class="subclass-list">\n')
- for i, subclass in enumerate(doc.subclasses):
- href = self.href(subclass, context=doc)
- if self._val_is_public(subclass): css = ''
- else: css = ' class="private"'
- if i > 0: href = ', '+href
- out('<li%s>%s</li>' % (css, href))
- out(' </ul>\n')
- out('</dd></dl>\n\n')
- out('<hr />\n')
-
- # If the class has a description, then list it.
- if doc.descr not in (None, UNKNOWN):
- out(self.descr(doc, 2)+'\n\n')
- # Write any standarad metadata (todo, author, etc.)
- if doc.metadata is not UNKNOWN and doc.metadata:
- out('<hr />\n')
- self.write_standard_fields(out, doc)
- # Write summary tables describing the variables that the
- # class defines.
- self.write_summary_table(out, "Nested Classes", doc, "class")
- self.write_summary_table(out, "Instance Methods", doc,
- "instancemethod")
- self.write_summary_table(out, "Class Methods", doc, "classmethod")
- self.write_summary_table(out, "Static Methods", doc, "staticmethod")
- self.write_summary_table(out, "Class Variables", doc,
- "classvariable")
- self.write_summary_table(out, "Instance Variables", doc,
- "instancevariable")
- self.write_summary_table(out, "Properties", doc, "property")
- # Write a list of all imported objects.
- if self._show_imports:
- self.write_imports(out, doc)
- # Write detailed descriptions of functions & variables defined
- # in this class.
- # [xx] why group methods into one section but split vars into two?
- # seems like we should either group in both cases or split in both
- # cases.
- self.write_details_list(out, "Method Details", doc, "method")
- self.write_details_list(out, "Class Variable Details", doc,
- "classvariable")
- self.write_details_list(out, "Instance Variable Details", doc,
- "instancevariable")
- self.write_details_list(out, "Property Details", doc, "property")
- # Write the page footer (including navigation bar)
- self.write_navbar(out, doc)
- self.write_footer(out)
- def write_class_tree_graph(self, out, doc, graphmaker):
- """
- Write HTML code for a class tree graph of C{doc} (a classdoc),
- using C{graphmaker} to draw the actual graph. C{graphmaker}
- should be L{class_tree_graph()}, or L{uml_class_tree_graph()},
- or any other function with a compatible signature.
- If the given class has any private sublcasses (including
- recursive subclasses), then two graph images will be generated
- -- one to display when private values are shown, and the other
- to display when private values are hidden.
- """
- linker = _HTMLDocstringLinker(self, doc)
- private_subcls = self._private_subclasses(doc)
- if private_subcls:
- out('<center>\n'
- ' <div class="private">%s</div>\n'
- ' <div class="public" style="display:none">%s</div>\n'
- '</center>\n' %
- (self.render_graph(graphmaker(doc, linker, doc)),
- self.render_graph(graphmaker(doc, linker, doc,
- exclude=private_subcls))))
- else:
- out('<center>\n%s\n</center>\n' %
- self.render_graph(graphmaker(doc, linker, doc)))
- #////////////////////////////////////////////////////////////
- #{ 2.3. Trees pages
- #////////////////////////////////////////////////////////////
- def write_module_tree(self, out):
- # Header material
- self.write_treepage_header(out, 'Module Hierarchy', 'module-tree.html')
- out('<h1 class="epydoc">Module Hierarchy</h1>\n')
- # Write entries for all top-level modules/packages.
- out('<ul class="nomargin-top">\n')
- for doc in self.module_list:
- if (doc.package in (None, UNKNOWN) or
- doc.package not in self.module_set):
- self.write_module_tree_item(out, doc)
- out('</ul>\n')
- # Footer material
- self.write_navbar(out, 'trees')
- self.write_footer(out)
- def write_class_tree(self, out):
- """
- Write HTML code for a nested list showing the base/subclass
- relationships between all documented classes. Each element of
- the top-level list is a class with no (documented) bases; and
- under each class is listed all of its subclasses. Note that
- in the case of multiple inheritance, a class may appear
- multiple times.
-
- @todo: For multiple inheritance, don't repeat subclasses the
- second time a class is mentioned; instead, link to the
- first mention.
- """
- # [XX] backref for multiple inheritance?
- # Header material
- self.write_treepage_header(out, 'Class Hierarchy', 'class-tree.html')
- out('<h1 class="epydoc">Class Hierarchy</h1>\n')
- # Build a set containing all classes that we should list.
- # This includes everything in class_list, plus any of those
- # class' bases, but not undocumented subclasses.
- class_set = self.class_set.copy()
- for doc in self.class_list:
- if doc.bases != UNKNOWN:
- for base in doc.bases:
- if base not in class_set:
- if isinstance(base, ClassDoc):
- class_set.update(base.mro())
- else:
- # [XX] need to deal with this -- how?
- pass
- #class_set.add(base)
-
- out('<ul class="nomargin-top">\n')
- for doc in sorted(class_set, key=lambda c:c.canonical_name[-1]):
- if doc.bases != UNKNOWN and len(doc.bases)==0:
- self.write_class_tree_item(out, doc, class_set)
- out('</ul>\n')
-
- # Footer material
- self.write_navbar(out, 'trees')
- self.write_footer(out)
- def write_treepage_header(self, out, title, url):
- # Header material.
- self.write_header(out, title)
- self.write_navbar(out, 'trees')
- self.write_breadcrumbs(out, 'trees', url)
- if self.class_list and self.module_list:
- out('<center><b>\n')
- out(' [ <a href="module-tree.html">Module Hierarchy</a>\n')
- out(' | <a href="class-tree.html">Class Hierarchy</a> ]\n')
- out('</b></center><br />\n')
- #////////////////////////////////////////////////////////////
- #{ 2.4. Index pages
- #////////////////////////////////////////////////////////////
- SPLIT_IDENT_INDEX_SIZE = 3000
- """If the identifier index has more than this number of entries,
- then it will be split into separate pages, one for each
- alphabetical section."""
- LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ_'
- """The alphabetical sections that are used for link index pages."""
-
- def write_link_index(self, out, indices, title, url, index_by_section,
- sections=LETTERS, section_url='#%s'):
-
- # Header
- self.write_indexpage_header(out, indices, title, url)
- # Index title & links to alphabetical sections.
- out('<table border="0" width="100%">\n'
- '<tr valign="bottom"><td>\n')
- out('<h1 class="epydoc">%s</h1>\n</td><td>\n[\n' % title)
- for sec in self.LETTERS:
- if sec in index_by_section:
- out(' <a href="%s">%s</a>\n' % (section_url % sec, sec))
- else:
- out(' %s\n' % sec)
- out(']\n')
- out('</td></table>\n')
- # Alphabetical sections.
- sections = [s for s in sections if s in index_by_section]
- if sections:
- out('<table border="0" width="100%">\n')
- for section in sorted(sections):
- out('<tr valign="top"><td valign="top" width="1%">')
- out('<h2 class="epydoc"><a name="%s">%s</a></h2></td>\n' %
- (section, section))
- out('<td valign="top">\n')
- self.write_index_section(out, index_by_section[section], True)
- out('</td></tr>\n')
- out('</table>\n<br />')
- # Footer material.
- out('<br />')
- self.write_navbar(out, 'indices')
- self.write_footer(out)
-
-
- def write_metadata_index(self, out, indices, field, title, typ):
- """
- Write an HTML page containing a metadata index.
- """
- index = indices[field]
-
- # Header material.
- self.write_indexpage_header(out, indices, title,
- '%s-index.html' % field)
- # Page title.
- out('<h1 class="epydoc"><a name="%s">%s</a></h1>\n<br />\n' %
- (field, title))
- # Index (one section per arg)
- for arg in sorted(index):
- # Write a section title.
- if arg is not None:
- if len([1 for (doc, descrs) in index[arg] if
- not self._doc_or_ancestor_is_private(doc)]) == 0:
- out('<div class="private">')
- else:
- out('<div>')
- self.write_table_header(out, 'metadata-index', arg)
- out('</table>')
- # List every descr for this arg.
- for (doc, descrs) in index[arg]:
- if self._doc_or_ancestor_is_private(doc):
- out('<div class="private">\n')
- else:
- out('<div>\n')
- out('<table width="100%" class="metadata-index" '
- 'bgcolor="#e0e0e0"><tr><td class="metadata-index">')
- out('<b>%s in %s</b>' %
- (typ, self.href(doc, label=doc.canonical_name)))
- out(' <ul class="nomargin">\n')
- for descr in descrs:
- out(' <li>%s</li>\n' %
- self.docstring_to_html(descr,doc,4))
- out(' </ul>\n')
- out('</table></div>\n')
- # Footer material.
- out('<br />')
- self.write_navbar(out, 'indices')
- self.write_footer(out)
- def write_indexpage_header(self, out, indices, title, url):
- """
- A helper for the index page generation functions, which
- generates a header that can be used to navigate between the
- different indices.
- """
- self.write_header(out, title)
- self.write_navbar(out, 'indices')
- self.write_breadcrumbs(out, 'indices', url)
- if (indices['term'] or
- [1 for (name,l,l2) in self.METADATA_INDICES if indices[name]]):
- out('<center><b>[\n')
- out(' <a href="identifier-index.html">Identifiers</a>\n')
- if indices['term']:
- out('| <a href="term-index.html">Term Definitions</a>\n')
- for (name, label, label2) in self.METADATA_INDICES:
- if indices[name]:
- out('| <a href="%s-index.html">%s</a>\n' %
- (name, label2))
- out(']</b></center><br />\n')
- def write_index_section(self, out, items, add_blankline=False):
- out('<table class="link-index" width="100%" border="1">\n')
- num_rows = (len(items)+2)/3
- for row in range(num_rows):
- out('<tr>\n')
- for col in range(3):
- out('<td width="33%" class="link-index">')
- i = col*num_rows+row
- if i < len(items):
- name, url, container = items[col*num_rows+row]
- out('<a href="%s">%s</a>' % (url, name))
- if container is not None:
- out('<br />\n')
- if isinstance(container, ModuleDoc):
- label = container.canonical_name
- else:
- label = container.canonical_name[-1]
- out('<span class="index-where">(in %s)'
- '</span>' % self.href(container, label))
- else:
- out(' ')
- out('</td>\n')
- out('</tr>\n')
- if add_blankline and num_rows == 1:
- blank_cell = '<td class="link-index"> </td>'
- out('<tr>'+3*blank_cell+'</tr>\n')
- out('</table>\n')
- #////////////////////////////////////////////////////////////
- #{ 2.5. Help Page
- #////////////////////////////////////////////////////////////
- def write_help(self, out):
- """
- Write an HTML help file to the given stream. If
- C{self._helpfile} contains a help file, then use it;
- otherwise, use the default helpfile from
- L{epydoc.docwriter.html_help}.
- """
- # todo: optionally parse .rst etc help files?
-
- # Get the contents of the help file.
- if self._helpfile:
- if os.path.exists(self._helpfile):
- try: help = open(self._helpfile).read()
- except: raise IOError("Can't open help file: %r" %
- self._helpfile)
- else:
- raise IOError("Can't find help file: %r" % self._helpfile)
- else:
- if self._prj_name: thisprj = self._prj_name
- else: thisprj = 'this project'
- help = HTML_HELP % {'this_project':thisprj}
- # Insert the help contents into a webpage.
- self.write_header(out, 'Help')
- self.write_navbar(out, 'help')
- self.write_breadcrumbs(out, 'help', 'help.html')
- out(help)
- self.write_navbar(out, 'help')
- self.write_footer(out)
- #////////////////////////////////////////////////////////////
- #{ 2.6. Frames-based Table of Contents
- #////////////////////////////////////////////////////////////
-
- write_frames_index = compile_template(
- """
- write_frames_index(self, out)
- Write the frames index file for the frames-based table of
- contents to the given streams.
- """,
- # /------------------------- Template -------------------------\
- '''
- <?xml version="1.0" encoding="iso-8859-1"?>
- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
- "DTD/xhtml1-frameset.dtd">
- <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
- <head>
- <title> $self._prj_name or "API Documentation"$ </title>
- </head>
- <frameset cols="20%,80%">
- <frameset rows="30%,70%">
- <frame src="toc.html" name="moduleListFrame"
- id="moduleListFrame" />
- <frame src="toc-everything.html" name="moduleFrame"
- id="moduleFrame" />
- </frameset>
- <frame src="$self._top_page_url$" name="mainFrame" id="mainFrame" />
- </frameset>
- </html>
- ''')
- # \------------------------------------------------------------/
-
- write_toc = compile_template(
- """
- write_toc(self, out)
- """,
- # /------------------------- Template -------------------------\
- '''
- >>> self.write_header(out, "Table of Contents")
- <h1 class="toc">Table of Contents</h1>
- <hr />
- <a target="moduleFrame" href="toc-everything.html">Everything</a>
- <br />
- >>> self.write_toc_section(out, "Modules", self.module_list)
- <hr />
- >>> if self._show_private:
- $self.PRIVATE_LINK$
- >>> #endif
- >>> self.write_footer(out, short=True)
- ''')
- # \------------------------------------------------------------/
- def write_toc_section(self, out, name, docs, fullname=True):
- if not docs: return
- # Assign names to each item, and sort by name.
- if fullname:
- docs = [(str(d.canonical_name), d) for d in docs]
- else:
- docs = [(str(d.canonical_name[-1]), d) for d in docs]
- docs.sort()
- out(' <h2 class="toc">%s</h2>\n' % name)
- for label, doc in docs:
- doc_url = self.url(doc)
- toc_url = 'toc-%s' % doc_url
- is_private = self._doc_or_ancestor_is_private(doc)
- if is_private:
- if not self._show_private: continue
- out(' <div class="private">\n')
-
- if isinstance(doc, ModuleDoc):
- out(' <a target="moduleFrame" href="%s"\n'
- ' onclick="setFrame(\'%s\',\'%s\');"'
- ' >%s</a><br />' % (toc_url, toc_url, doc_url, label))
- else:
- out(' <a target="mainFrame" href="%s"\n'
- ' >%s</a><br />' % (doc_url, label))
- if is_private:
- out(' </div>\n')
- def write_project_toc(self, out):
- self.write_header(out, "Everything")
- out('<h1 class="toc">Everything</h1>\n')
- out('<hr />\n')
- # List the classes.
- self.write_toc_section(out, "All Classes", self.class_list)
- # List the functions.
- funcs = [d for d in self.routine_list
- if not isinstance(self.docindex.container(d),
- (ClassDoc, types.NoneType))]
- self.write_toc_section(out, "All Functions", funcs)
- # List the variables.
- vars = []
- for doc in self.module_list:
- vars += doc.select_variables(value_type='other',
- imported=False,
- public=self._public_filter)
- self.write_toc_section(out, "All Variables", vars)
- # Footer material.
- out('<hr />\n')
- if self._show_private:
- out(self.PRIVATE_LINK+'\n')
- self.write_footer(out, short=True)
- def write_module_toc(self, out, doc):
- """
- Write an HTML page containing the table of contents page for
- the given module to the given streams. This page lists the
- modules, classes, exceptions, functions, and variables defined
- by the module.
- """
- name = doc.canonical_name[-1]
- self.write_header(out, name)
- out('<h1 class="toc">Module %s</h1>\n' % name)
- out('<hr />\n')
- # List the classes.
- classes = doc.select_variables(value_type='class', imported=False,
- public=self._public_filter)
- self.write_toc_section(out, "Classes", classes, fullname=False)
- # List the functions.
- funcs = doc.select_variables(value_type='function', imported=False,
- public=self._public_filter)
- self.write_toc_section(out, "Functions", funcs, fullname=False)
- # List the variables.
- variables = doc.select_variables(value_type='other', imported=False,
- public=self._public_filter)
- self.write_toc_section(out, "Variables", variables, fullname=False)
-
- # Footer material.
- out('<hr />\n')
- if self._show_private:
- out(self.PRIVATE_LINK+'\n')
- self.write_footer(out, short=True)
- #////////////////////////////////////////////////////////////
- #{ 2.7. Project homepage (index.html)
- #////////////////////////////////////////////////////////////
- def write_homepage(self, directory):
- """
- Write an C{index.html} file in the given directory. The
- contents of this file are copied or linked from an existing
- page, so this method must be called after all pages have been
- written. The page used is determined by L{_frames_index} and
- L{_top_page}:
- - If L{_frames_index} is true, then C{frames.html} is
- copied.
- - Otherwise, the page specified by L{_top_page} is
- copied.
- """
- filename = os.path.join(directory, 'index.html')
- if self._frames_index: top = 'frames.html'
- else: top = self._top_page_url
- # Copy the non-frames index file from top, if it's internal.
- if top[:5] != 'http:' and '/' not in top:
- try:
- # Read top into `s`.
- topfile = os.path.join(directory, top)
- s = open(topfile, 'r').read()
- # Write the output file.
- open(filename, 'w').write(s)
- return
- except:
- log.error('Warning: error copying index; '
- 'using a redirect page')
- # Use a redirect if top is external, or if we faild to copy.
- name = self._prj_name or 'this project'
- f = open(filename, 'w')
- self.write_redirect_index(f.write, top, name)
- f.close()
- write_redirect_index = compile_template(
- """
- write_redirect_index(self, out, top, name)
- """,
- # /------------------------- Template -------------------------\
- '''
- <?xml version="1.0" encoding="iso-8859-1"?>
- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
- "DTD/xhtml1-strict.dtd">
- <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
- <head>
- <title> Redirect </title>
- <meta http-equiv="refresh" content="1;url=$top$" />
- <link rel="stylesheet" href="epydoc.css" type="text/css"></link>
- </head>
- <body>
- <p>Redirecting to the API documentation for
- <a href="$top$">$self._prj_name or "this project"$</a>...</p>
- </body>
- </html>
- ''')
- # \------------------------------------------------------------/
- #////////////////////////////////////////////////////////////
- #{ 2.8. Stylesheet (epydoc.css)
- #////////////////////////////////////////////////////////////
- def write_css(self, directory, cssname):
- """
- Write the CSS stylesheet in the given directory. If
- C{cssname} contains a stylesheet file or name (from
- L{epydoc.docwriter.html_css}), then use that stylesheet;
- otherwise, use the default stylesheet.
- @rtype: C{None}
- """
- filename = os.path.join(directory, 'epydoc.css')
-
- # Get the contents for the stylesheet file.
- if cssname is None:
- css = STYLESHEETS['default'][0]
- else:
- if os.path.exists(cssname):
- try: css = open(cssname).read()
- except: raise IOError("Can't open CSS file: %r" % cssname)
- elif cssname in STYLESHEETS:
- css = STYLESHEETS[cssname][0]
- else:
- raise IOError("Can't find CSS file: %r" % cssname)
- # Write the stylesheet.
- cssfile = open(filename, 'w')
- cssfile.write(css)
- cssfile.close()
- #////////////////////////////////////////////////////////////
- #{ 2.9. Javascript (epydoc.js)
- #////////////////////////////////////////////////////////////
- def write_javascript(self, directory):
- jsfile = open(os.path.join(directory, 'epydoc.js'), 'w')
- print >> jsfile, self.TOGGLE_PRIVATE_JS
- print >> jsfile, self.SHOW_PRIVATE_JS
- print >> jsfile, self.GET_COOKIE_JS
- print >> jsfile, self.SET_FRAME_JS
- print >> jsfile, self.HIDE_PRIVATE_JS
- print >> jsfile, self.TOGGLE_CALLGRAPH_JS
- print >> jsfile, html_colorize.PYSRC_JAVASCRIPTS
- print >> jsfile, self.GET_ANCHOR_JS
- print >> jsfile, self.REDIRECT_URL_JS
- jsfile.close()
- #: A javascript that is used to show or hide the API documentation
- #: for private objects. In order for this to work correctly, all
- #: documentation for private objects should be enclosed in
- #: C{<div class="private">...</div>} elements.
- TOGGLE_PRIVATE_JS = '''
- function toggle_private() {
- // Search for any private/public links on this page. Store
- // their old text in "cmd," so we will know what action to
- // take; and change their text to the opposite action.
- var cmd = "?";
- var elts = document.getElementsByTagName("a");
- for(var i=0; i<elts.length; i++) {
- if (elts[i].className == "privatelink") {
- cmd = elts[i].innerHTML;
- elts[i].innerHTML = ((cmd && cmd.substr(0,4)=="show")?
- "hide private":"show private");
- }
- }
- // Update all DIVs containing private objects.
- var elts = document.getElementsByTagName("div");
- for(var i=0; i<elts.length; i++) {
- if (elts[i].className == "private") {
- elts[i].style.display = ((cmd && cmd.substr(0,4)=="hide")?"none":"block");
- }
- else if (elts[i].className == "public") {
- elts[i].style.display = ((cmd && cmd.substr(0,4)=="hide")?"block":"none");
- }
- }
- // Update all table rows containing private objects. Note, we
- // use "" instead of "block" becaue IE & firefox disagree on what
- // this should be (block vs table-row), and "" just gives the
- // default for both browsers.
- var elts = document.getElementsByTagName("tr");
- for(var i=0; i<elts.length; i++) {
- if (elts[i].className == "private") {
- elts[i].style.display = ((cmd && cmd.substr(0,4)=="hide")?"none":"");
- }
- }
- // Update all list items containing private objects.
- var elts = document.getElementsByTagName("li");
- for(var i=0; i<elts.length; i++) {
- if (elts[i].className == "private") {
- elts[i].style.display = ((cmd && cmd.substr(0,4)=="hide")?
- "none":"");
- }
- }
- // Update all list items containing private objects.
- var elts = document.getElementsByTagName("ul");
- for(var i=0; i<elts.length; i++) {
- if (elts[i].className == "private") {
- elts[i].style.display = ((cmd && cmd.substr(0,4)=="hide")?"none":"block");
- }
- }
- // Set a cookie to remember the current option.
- document.cookie = "EpydocPrivate="+cmd;
- }
- '''.strip()
- #: A javascript that is used to read the value of a cookie. This
- #: is used to remember whether private variables should be shown or
- #: hidden.
- GET_COOKIE_JS = '''
- function getCookie(name) {
- var dc = document.cookie;
- var prefix = name + "=";
- var begin = dc.indexOf("; " + prefix);
- if (begin == -1) {
- begin = dc.indexOf(prefix);
- if (begin != 0) return null;
- } else
- { begin += 2; }
- var end = document.cookie.indexOf(";", begin);
- if (end == -1)
- { end = dc.length; }
- return unescape(dc.substring(begin + prefix.length, end));
- }
- '''.strip()
- #: A javascript that is used to set the contents of two frames at
- #: once. This is used by the project table-of-contents frame to
- #: set both the module table-of-contents frame and the main frame
- #: when the user clicks on a module.
- SET_FRAME_JS = '''
- function setFrame(url1, url2) {
- parent.frames[1].location.href = url1;
- parent.frames[2].location.href = url2;
- }
- '''.strip()
- #: A javascript that is used to hide private variables, unless
- #: either: (a) the cookie says not to; or (b) we appear to be
- #: linking to a private variable.
- HIDE_PRIVATE_JS = '''
- function checkCookie() {
- var cmd=getCookie("EpydocPrivate");
- if (cmd && cmd.substr(0,4)!="show" && location.href.indexOf("#_") < 0)
- toggle_private();
- }
- '''.strip()
- TOGGLE_CALLGRAPH_JS = '''
- function toggleCallGraph(id) {
- var elt = document.getElementById(id);
- if (elt.style.display == "none")
- elt.style.display = "block";
- else
- elt.style.display = "none";
- }
- '''.strip()
- SHOW_PRIVATE_JS = '''
- function show_private() {
- var elts = document.getElementsByTagName("a");
- for(var i=0; i<elts.length; i++) {
- if (elts[i].className == "privatelink") {
- cmd = elts[i].innerHTML;
- if (cmd && cmd.substr(0,4)=="show")
- toggle_private();
- }
- }
- }
- '''.strip()
- GET_ANCHOR_JS = '''
- function get_anchor() {
- var href = location.href;
- var start = href.indexOf("#")+1;
- if ((start != 0) && (start != href.length))
- return href.substring(start, href.length);
- }
- '''.strip()
- #: A javascript that is used to implement the auto-redirect page.
- #: When the user visits <redirect.html#dotted.name>, they will
- #: automatically get redirected to the page for the object with
- #: the given fully-qualified dotted name. E.g., for epydoc,
- #: <redirect.html#epydoc.apidoc.UNKNOWN> redirects the user to
- #: <epydoc.apidoc-module.html#UNKNOWN>.
- REDIRECT_URL_JS = '''
- function redirect_url(dottedName) {
- // Scan through each element of the "pages" list, and check
- // if "name" matches with any of them.
- for (var i=0; i<pages.length; i++) {
- // Each page has the form "<pagename>-m" or "<pagename>-c";
- // extract the <pagename> portion & compare it to dottedName.
- var pagename = pages[i].substring(0, pages[i].length-2);
- if (pagename == dottedName.substring(0,pagename.length)) {
- // We\'ve found a page that matches `dottedName`;
- // construct its URL, using leftover `dottedName`
- // content to form an anchor.
- var pagetype = pages[i].charAt(pages[i].length-1);
- var url = pagename + ((pagetype=="m")?"-module.html":
- "-class.html");
- if (dottedName.length > pagename.length)
- url += "#" + dottedName.substring(pagename.length+1,
- dottedName.length);
- return url;
- }
- }
- }
- '''.strip()
-
- #////////////////////////////////////////////////////////////
- #{ 2.10. Graphs
- #////////////////////////////////////////////////////////////
- def render_graph(self, graph):
- if graph is None: return ''
- graph.caption = graph.title = None
- image_url = '%s.gif' % graph.uid
- image_file = os.path.join(self._directory, image_url)
- return graph.to_html(image_file, image_url)
-
- RE_CALLGRAPH_ID = re.compile(r"""["'](.+-div)['"]""")
-
- def render_callgraph(self, callgraph, token=""):
- """Render the HTML chunk of a callgraph.
- If C{callgraph} is a string, use the L{_callgraph_cache} to return
- a pre-rendered HTML chunk. This mostly avoids to run C{dot} twice for
- the same callgraph. Else, run the graph and store its HTML output in
- the cache.
- @param callgraph: The graph to render or its L{uid<DotGraph.uid>}.
- @type callgraph: L{DotGraph} or C{str}
- @param token: A string that can be used to make the C{<div>} id
- unambiguous, if the callgraph is used more than once in a page.
- @type token: C{str}
- @return: The HTML representation of the graph.
- @rtype: C{str}
- """
- if callgraph is None: return ""
-
- if isinstance(callgraph, basestring):
- uid = callgraph
- rv = self._callgraph_cache.get(callgraph, "")
- else:
- uid = callgraph.uid
- graph_html = self.render_graph(callgraph)
- if graph_html == '':
- rv = ""
- else:
- rv = ('<div style="display:none" id="%%s-div"><center>\n'
- '<table border="0" cellpadding="0" cellspacing="0">\n'
- ' <tr><td>%s</td></tr>\n'
- ' <tr><th>Call Graph</th></tr>\n'
- '</table><br />\n</center></div>\n' % graph_html)
- # Store in the cache the complete HTML chunk without the
- # div id, which may be made unambiguous by the token
- self._callgraph_cache[uid] = rv
- # Mangle with the graph
- if rv: rv = rv % (uid + token)
- return rv
- def callgraph_link(self, callgraph, token=""):
- """Render the HTML chunk of a callgraph link.
- The link can toggles the visibility of the callgraph rendered using
- L{render_callgraph} with matching parameters.
- @param callgraph: The graph to render or its L{uid<DotGraph.uid>}.
- @type callgraph: L{DotGraph} or C{str}
- @param token: A string that can be used to make the C{<div>} id
- unambiguous, if the callgraph is used more than once in a page.
- @type token: C{str}
- @return: The HTML representation of the graph link.
- @rtype: C{str}
- """
- # Use class=codelink, to match style w/ the source code link.
- if callgraph is None: return ''
- if isinstance(callgraph, basestring):
- uid = callgraph
- else:
- uid = callgraph.uid
- return ('<br /><span class="codelink"><a href="javascript:void(0);" '
- 'onclick="toggleCallGraph(\'%s-div\');return false;">'
- 'call graph</a></span> ' % (uid + token))
- #////////////////////////////////////////////////////////////
- #{ 2.11. Images
- #////////////////////////////////////////////////////////////
- IMAGES = {'crarr.png': # Carriage-return arrow, used for LINEWRAP.
- 'iVBORw0KGgoAAAANSUhEUgAAABEAAAAKCAMAAABlokWQAAAALHRFWHRD'
- 'cmVhdGlvbiBUaW1lAFR1\nZSAyMiBBdWcgMjAwNiAwMDo0MzoxMCAtMD'
- 'UwMGAMEFgAAAAHdElNRQfWCBYFASkQ033WAAAACXBI\nWXMAAB7CAAAe'
- 'wgFu0HU+AAAABGdBTUEAALGPC/xhBQAAAEVQTFRF////zcOw18/AgGY0'
- 'c1cg4dvQ\ninJEYEAAYkME3NXI6eTcloFYe2Asr5+AbE4Uh29A9fPwqp'
- 'l4ZEUI8O3onopk0Ma0lH5U1nfFdgAA\nAAF0Uk5TAEDm2GYAAABNSURB'
- 'VHjaY2BAAbzsvDAmK5oIlxgfioiwCAe7KJKIgKAQOzsLLwTwA0VY\n+d'
- 'iRAT8T0AxuIIMHqoaXCWIPGzsHJ6orGJiYWRjQASOcBQAocgMSPKMTIg'
- 'AAAABJRU5ErkJggg==\n',
- }
- def write_images(self, directory):
- for (name, data) in self.IMAGES.items():
- f = open(os.path.join(directory, name), 'wb')
- f.write(base64.decodestring(data))
- f.close()
- #////////////////////////////////////////////////////////////
- #{ 3.1. Page Header
- #////////////////////////////////////////////////////////////
- write_header = compile_template(
- """
- write_header(self, out, title)
- Generate HTML code for the standard page header, and write it
- to C{out}. C{title} is a string containing the page title.
- It should be appropriately escaped/encoded.
- """,
- # /------------------------- Template -------------------------\
- '''
- <?xml version="1.0" encoding="ascii"?>
- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "DTD/xhtml1-transitional.dtd">
- <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
- <head>
- <title>$title$</title>
- <link rel="stylesheet" href="epydoc.css" type="text/css" />
- <script type="text/javascript" src="epydoc.js"></script>
- </head>
-
- <body bgcolor="white" text="black" link="blue" vlink="#204080"
- alink="#204080">
- ''')
- # \------------------------------------------------------------/
- #////////////////////////////////////////////////////////////
- #{ 3.2. Page Footer
- #////////////////////////////////////////////////////////////
- write_footer = compile_template(
- """
- write_footer(self, out, short=False)
- Generate HTML code for the standard page footer, and write it
- to C{out}.
- """,
- # /------------------------- Template -------------------------\
- '''
- >>> if not short:
- <table border="0" cellpadding="0" cellspacing="0" width="100%%">
- <tr>
- <td align="left" class="footer">
- >>> if self._include_log:
- <a href="epydoc-log.html">Generated by Epydoc
- $epydoc.__version__$ on $time.asctime()$</a>
- >>> else:
- Generated by Epydoc $epydoc.__version__$ on $time.asctime()$
- >>> #endif
- </td>
- <td align="right" class="footer">
- <a target="mainFrame" href="http://epydoc.sourceforge.net"
- >http://epydoc.sourceforge.net</a>
- </td>
- </tr>
- </table>
- >>> #endif
- <script type="text/javascript">
- <!--
- // Private objects are initially displayed (because if
- // javascript is turned off then we want them to be
- // visible); but by default, we want to hide them. So hide
- // them unless we have a cookie that says to show them.
- checkCookie();
- // -->
- </script>
- </body>
- </html>
- ''')
- # \------------------------------------------------------------/
- #////////////////////////////////////////////////////////////
- #{ 3.3. Navigation Bar
- #////////////////////////////////////////////////////////////
- write_navbar = compile_template(
- """
- write_navbar(self, out, context)
- Generate HTML code for the navigation bar, and write it to
- C{out}. The navigation bar typically looks like::
- [ Home Trees Index Help Project ]
- @param context: A value indicating what page we're generating
- a navigation bar for. If we're generating an API
- documentation page for an object, then C{context} is a
- L{ValueDoc} containing the documentation for that object;
- otherwise, C{context} is a string name for the page. The
- following string names are recognized: C{'tree'}, C{'index'},
- and C{'help'}.
- """,
- # /------------------------- Template -------------------------\
- '''
- <!-- ==================== NAVIGATION BAR ==================== -->
- <table class="navbar" border="0" width="100%" cellpadding="0"
- bgcolor="#a0c0ff" cellspacing="0">
- <tr valign="middle">
- >>> if self._top_page_url not in (self._trees_url, "identifier-index.html", "help.html"):
- <!-- Home link -->
- >>> if (isinstance(context, ValueDoc) and
- >>> self._top_page_url == self.url(context.canonical_name)):
- <th bgcolor="#70b0f0" class="navbar-select"
- > Home </th>
- >>> else:
- <th> <a
- href="$self._top_page_url$">Home</a> </th>
- >>> #endif
-
- <!-- Tree link -->
- >>> if context == "trees":
- <th bgcolor="#70b0f0" class="navbar-select"
- > Trees </th>
- >>> else:
- <th> <a
- href="$self._trees_url$">Trees</a> </th>
- >>> #endif
-
- <!-- Index link -->
- >>> if context == "indices":
- <th bgcolor="#70b0f0" class="navbar-select"
- > Indices </th>
- >>> else:
- <th> <a
- href="identifier-index.html">Indices</a> </th>
- >>> #endif
-
- <!-- Help link -->
- >>> if context == "help":
- <th bgcolor="#70b0f0" class="navbar-select"
- > Help </th>
- >>> else:
- <th> <a
- href="help.html">Help</a> </th>
- >>> #endif
-
- >>> if self._prj_link:
- <!-- Project homepage -->
- <th class="navbar" align="right" width="100%">
- <table border="0" cellpadding="0" cellspacing="0">
- <tr><th class="navbar" align="center"
- >$self._prj_link.strip()$</th>
- </tr></table></th>
- >>> else:
- <th class="navbar" width="100%"></th>
- >>> #endif
- </tr>
- </table>
- ''')
- # \------------------------------------------------------------/
- #////////////////////////////////////////////////////////////
- #{ 3.4. Breadcrumbs
- #////////////////////////////////////////////////////////////
- write_breadcrumbs = compile_template(
- """
- write_breadcrumbs(self, out, context, context_url)
- Generate HTML for the breadcrumbs line, and write it to
- C{out}. The breadcrumbs line is an invisible table with a
- list of pointers to the current object's ancestors on the
- left; and the show/hide private selector and the
- frames/noframes selector on the right.
- @param context: The API documentation for the object whose
- breadcrumbs we should generate.
- @type context: L{ValueDoc}
- """,
- # /------------------------- Template -------------------------\
- '''
- <table width="100%" cellpadding="0" cellspacing="0">
- <tr valign="top">
- >>> if isinstance(context, APIDoc):
- <td width="100%">
- <span class="breadcrumbs">
- >>> crumbs = self.breadcrumbs(context)
- >>> for crumb in crumbs[:-1]:
- $crumb$ ::
- >>> #endfor
- $crumbs[-1]$
- </span>
- </td>
- >>> else:
- <td width="100%"> </td>
- >>> #endif
- <td>
- <table cellpadding="0" cellspacing="0">
- <!-- hide/show private -->
- >>> if self._show_private:
- <tr><td align="right">$self.PRIVATE_LINK$</td></tr>
- >>> #endif
- >>> if self._frames_index:
- <tr><td align="right"><span class="options"
- >[<a href="frames.html" target="_top">frames</a
- >] | <a href="$context_url$"
- target="_top">no frames</a>]</span></td></tr>
- >>> #endif
- </table>
- </td>
- </tr>
- </table>
- ''')
- # \------------------------------------------------------------/
- def breadcrumbs(self, doc):
- crumbs = [self._crumb(doc)]
- # Generate the crumbs for uid's ancestors.
- while True:
- container = self.docindex.container(doc)
- assert doc != container, 'object is its own container?'
- if container is None:
- if doc.canonical_name is UNKNOWN:
- return ['??']+crumbs
- elif isinstance(doc, ModuleDoc):
- return ['Package %s' % ident
- for ident in doc.canonical_name[:-1]]+crumbs
- else:
- return list(doc.canonical_name)+crumbs
- else:
- label = self._crumb(container)
- name = container.canonical_name
- crumbs.insert(0, self.href(container, label)) # [xx] code=0??
- doc = container
-
- def _crumb(self, doc):
- if (len(doc.canonical_name)==1 and
- doc.canonical_name[0].startswith('script-')):
- return 'Script %s' % doc.canonical_name[0][7:]
- return '%s %s' % (self.doc_kind(doc), doc.canonical_name[-1])
- #////////////////////////////////////////////////////////////
- #{ 3.5. Summary Tables
- #////////////////////////////////////////////////////////////
- def write_summary_table(self, out, heading, doc, value_type):
- """
- Generate HTML code for a summary table, and write it to
- C{out}. A summary table is a table that includes a one-row
- description for each variable (of a given type) in a module
- or class.
- @param heading: The heading for the summary table; typically,
- this indicates what kind of value the table describes
- (e.g., functions or classes).
- @param doc: A L{ValueDoc} object containing the API
- documentation for the module or class whose variables
- we should summarize.
- @param value_type: A string indicating what type of value
- should be listed in this summary table. This value
- is passed on to C{doc}'s C{select_variables()} method.
- """
- # inh_var_groups is a dictionary used to hold "inheritance
- # pseudo-groups", which are created when inheritance is
- # 'grouped'. It maps each base to a list of vars inherited
- # from that base.
- grouped_inh_vars = {}
- # Divide all public variables of the given type into groups.
- groups = [(plaintext_to_html(group_name),
- doc.select_variables(group=group_name, imported=False,
- value_type=value_type,
- public=self._public_filter))
- for group_name in doc.group_names()]
-
- # Discard any empty groups; and return if they're all empty.
- groups = [(g,vars) for (g,vars) in groups if vars]
- if not groups: return
- # Write a header
- self.write_table_header(out, "summary", heading)
- # Write a section for each group.
- for name, var_docs in groups:
- self.write_summary_group(out, doc, name,
- var_docs, grouped_inh_vars)
- # Write a section for each inheritance pseudo-group (used if
- # inheritance=='grouped')
- if grouped_inh_vars:
- for base in doc.mro():
- if base in grouped_inh_vars:
- hdr = 'Inherited from %s' % self.href(base, context=doc)
- tr_class = ''
- if len([v for v in grouped_inh_vars[base]
- if v.is_public]) == 0:
- tr_class = ' class="private"'
- self.write_group_header(out, hdr, tr_class)
- for var_doc in grouped_inh_vars[base]:
- self.write_summary_line(out, var_doc, doc)
- # Write a footer for the table.
- out(self.TABLE_FOOTER)
- def write_summary_group(self, out, doc, name, var_docs, grouped_inh_vars):
- # Split up the var_docs list, according to the way each var
- # should be displayed:
- # - listed_inh_vars -- for listed inherited variables.
- # - grouped_inh_vars -- for grouped inherited variables.
- # - normal_vars -- for all other variables.
- listed_inh_vars = {}
- normal_vars = []
- for var_doc in var_docs:
- if var_doc.container != doc:
- base = var_doc.container
- if not isinstance(base, ClassDoc):
- # This *should* never happen:
- log.warning("%s's container is not a class!" % var_doc)
- normal_vars.append(var_doc)
- elif (base not in self.class_set or
- self._inheritance == 'listed'):
- listed_inh_vars.setdefault(base,[]).append(var_doc)
- elif self._inheritance == 'grouped':
- grouped_inh_vars.setdefault(base,[]).append(var_doc)
- else:
- normal_vars.append(var_doc)
- else:
- normal_vars.append(var_doc)
-
- # Write a header for the group.
- if name != '':
- tr_class = ''
- if len([v for v in var_docs if v.is_public]) == 0:
- tr_class = ' class="private"'
- self.write_group_header(out, name, tr_class)
- # Write a line for each normal var:
- for var_doc in normal_vars:
- self.write_summary_line(out, var_doc, doc)
- # Write a subsection for inherited vars:
- if listed_inh_vars:
- self.write_inheritance_list(out, doc, listed_inh_vars)
- def write_inheritance_list(self, out, doc, listed_inh_vars):
- out(' <tr>\n <td colspan="2" class="summary">\n')
- for base in doc.mro():
- if base not in listed_inh_vars: continue
- public_vars = [v for v in listed_inh_vars[base]
- if v.is_public]
- private_vars = [v for v in listed_inh_vars[base]
- if not v.is_public]
- if public_vars:
- out(' <p class="indent-wrapped-lines">'
- '<b>Inherited from <code>%s</code></b>:\n' %
- self.href(base, context=doc))
- self.write_var_list(out, public_vars)
- out(' </p>\n')
- if private_vars and self._show_private:
- out(' <div class="private">')
- out(' <p class="indent-wrapped-lines">'
- '<b>Inherited from <code>%s</code></b> (private):\n' %
- self.href(base, context=doc))
- self.write_var_list(out, private_vars)
- out(' </p></div>\n')
- out(' </td>\n </tr>\n')
-
- def write_var_list(self, out, vardocs):
- out(' ')
- out(',\n '.join(['<code>%s</code>' % self.href(v,v.name)
- for v in vardocs])+'\n')
- def write_summary_line(self, out, var_doc, container):
- """
- Generate HTML code for a single line of a summary table, and
- write it to C{out}. See L{write_summary_table} for more
- information.
-
- @param var_doc: The API documentation for the variable that
- should be described by this line of the summary table.
- @param container: The API documentation for the class or
- module whose summary table we're writing.
- """
- pysrc_link = None
- callgraph = None
- # If it's a private variable, then mark its <tr>.
- if var_doc.is_public: tr_class = ''
- else: tr_class = ' class="private"'
- # Decide an anchor or a link is to be generated.
- link_name = self._redundant_details or var_doc.is_detailed()
- anchor = not link_name
- # Construct the HTML code for the type (cell 1) & description
- # (cell 2).
- if isinstance(var_doc.value, RoutineDoc):
- typ = self.return_type(var_doc, indent=6)
- description = self.function_signature(var_doc, is_summary=True,
- link_name=link_name, anchor=anchor)
- pysrc_link = self.pysrc_link(var_doc.value)
- # Perpare the call-graph, if requested
- if 'callgraph' in self._graph_types:
- linker = _HTMLDocstringLinker(self, var_doc.value)
- callgraph = call_graph([var_doc.value], self.docindex,
- linker, var_doc, add_callers=True,
- add_callees=True)
- if callgraph and callgraph.nodes:
- var_doc.value.callgraph_uid = callgraph.uid
- else:
- callgraph = None
- else:
- typ = self.type_descr(var_doc, indent=6)
- description = self.summary_name(var_doc,
- link_name=link_name, anchor=anchor)
- if isinstance(var_doc.value, GenericValueDoc):
- # The summary max length has been chosen setting
- # L{ValueDoc.SUMMARY_REPR_LINELEN} in the constructor
- max_len=self._variable_summary_linelen-3-len(var_doc.name)
- val_repr = var_doc.value.summary_pyval_repr(max_len)
- tooltip = self.variable_tooltip(var_doc)
- description += (' = <code%s>%s</code>' %
- (tooltip, val_repr.to_html(None)))
- # Add the summary to the description (if there is one).
- summary = self.summary(var_doc, indent=6)
- if summary: description += '<br />\n %s' % summary
-
- # If it's inherited, then add a note to the description.
- if var_doc.container != container and self._inheritance=="included":
- description += ("\n <em>(Inherited from " +
- self.href(var_doc.container) + ")</em>")
- # Write the summary line.
- self._write_summary_line(out, typ, description, tr_class, pysrc_link,
- callgraph)
- _write_summary_line = compile_template(
- "_write_summary_line(self, out, typ, description, tr_class, "
- "pysrc_link, callgraph)",
- # /------------------------- Template -------------------------\
- '''
- <tr$tr_class$>
- <td width="15%" align="right" valign="top" class="summary">
- <span class="summary-type">$typ or " "$</span>
- </td><td class="summary">
- >>> if pysrc_link is not None or callgraph is not None:
- <table width="100%" cellpadding="0" cellspacing="0" border="0">
- <tr>
- <td>$description$</td>
- <td align="right" valign="top">
- $pysrc_link$
- $self.callgraph_link(callgraph, token='-summary')$
- </td>
- </tr>
- </table>
- $self.render_callgraph(callgraph, token='-summary')$
- >>> #endif
- >>> if pysrc_link is None and callgraph is None:
- $description$
- >>> #endif
- </td>
- </tr>
- ''')
- # \------------------------------------------------------------/
- #////////////////////////////////////////////////////////////
- #{ 3.6. Details Lists
- #////////////////////////////////////////////////////////////
- def write_details_list(self, out, heading, doc, value_type):
- # Get a list of the VarDocs we should describe.
- if self._redundant_details:
- detailed = None
- else:
- detailed = True
- if isinstance(doc, ClassDoc):
- var_docs = doc.select_variables(value_type=value_type,
- imported=False, inherited=False,
- public=self._public_filter,
- detailed=detailed)
- else:
- var_docs = doc.select_variables(value_type=value_type,
- imported=False,
- public=self._public_filter,
- detailed=detailed)
- if not var_docs: return
- # Write a header
- self.write_table_header(out, "details", heading)
- out(self.TABLE_FOOTER)
- for var_doc in var_docs:
- self.write_details_entry(out, var_doc)
- out('<br />\n')
- def write_details_entry(self, out, var_doc):
- descr = self.descr(var_doc, indent=2) or ''
- if var_doc.is_public: div_class = ''
- else: div_class = ' class="private"'
- # Functions
- if isinstance(var_doc.value, RoutineDoc):
- rtype = self.return_type(var_doc, indent=10)
- rdescr = self.return_descr(var_doc, indent=10)
- arg_descrs = []
- args = set()
- # Find the description for each arg. (Leave them in the
- # same order that they're listed in the docstring.)
- for (arg_names, arg_descr) in var_doc.value.arg_descrs:
- args.update(arg_names)
- lhs = ', '.join([self.arg_name_to_html(var_doc.value, n)
- for n in arg_names])
- rhs = self.docstring_to_html(arg_descr, var_doc.value, 10)
- arg_descrs.append( (lhs, rhs) )
- # Check for arguments for which we have @type but not @param;
- # and add them to the arg_descrs list.
- for arg in var_doc.value.arg_types:
- if arg not in args:
- argname = self.arg_name_to_html(var_doc.value, arg)
- arg_descrs.append( (argname,'') )
- self.write_function_details_entry(out, var_doc, descr,
- var_doc.value.callgraph_uid,
- rtype, rdescr, arg_descrs,
- div_class)
- # Properties
- elif isinstance(var_doc.value, PropertyDoc):
- prop_doc = var_doc.value
- accessors = [ (name,
- self.property_accessor_to_html(val_doc, prop_doc),
- self.summary(val_doc))
- for (name, val_doc) in
- [('Get', prop_doc.fget), ('Set', prop_doc.fset),
- ('Delete', prop_doc.fdel)]
- if val_doc not in (None, UNKNOWN)
- and val_doc.pyval is not None ]
- self.write_property_details_entry(out, var_doc, descr,
- accessors, div_class)
-
- # Variables
- else:
- self.write_variable_details_entry(out, var_doc, descr, div_class)
- def labelled_list_item(self, lhs, rhs):
- # If the RHS starts with a paragraph, then move the
- # paragraph-start tag to the beginning of the lhs instead (so
- # there won't be a line break after the '-').
- m = re.match(r'^<p( [^>]+)?>', rhs)
- if m:
- lhs = m.group() + lhs
- rhs = rhs[m.end():]
- if rhs:
- return '<li>%s - %s</li>' % (lhs, rhs)
- else:
- return '<li>%s</li>' % (lhs,)
- def property_accessor_to_html(self, val_doc, context=None):
- if val_doc not in (None, UNKNOWN):
- if isinstance(val_doc, RoutineDoc):
- return self.function_signature(val_doc, is_summary=True,
- link_name=True, context=context)
- elif isinstance(val_doc, GenericValueDoc):
- return self.pprint_value(val_doc)
- else:
- return self.href(val_doc, context=context)
- else:
- return '??'
-
- def arg_name_to_html(self, func_doc, arg_name):
- """
- A helper function used to format an argument name, for use in
- the argument description list under a routine's details entry.
- This just wraps strong & code tags around the arg name; and if
- the arg name is associated with a type, then adds it
- parenthetically after the name.
- """
- s = '<strong class="pname"><code>%s</code></strong>' % arg_name
- if arg_name in func_doc.arg_types:
- typ = func_doc.arg_types[arg_name]
- typ_html = self.docstring_to_html(typ, func_doc, 10)
- s += " (%s)" % typ_html
- return s
- write_function_details_entry = compile_template(
- '''
- write_function_details_entry(self, out, var_doc, descr, callgraph, \
- rtype, rdescr, arg_descrs, div_class)
- ''',
- # /------------------------- Template -------------------------\
- '''
- >>> func_doc = var_doc.value
- <a name="$var_doc.name$"></a>
- <div$div_class$>
- >>> self.write_table_header(out, "details")
- <tr><td>
- <table width="100%" cellpadding="0" cellspacing="0" border="0">
- <tr valign="top"><td>
- <h3 class="epydoc">$self.function_signature(var_doc)$
- >>> if var_doc.name in self.SPECIAL_METHODS:
- <br /><em class="fname">($self.SPECIAL_METHODS[var_doc.name]$)</em>
- >>> #endif
- >>> if isinstance(func_doc, ClassMethodDoc):
- <br /><em class="fname">Class Method</em>
- >>> #endif
- >>> if isinstance(func_doc, StaticMethodDoc):
- <br /><em class="fname">Static Method</em>
- >>> #endif
- </h3>
- </td><td align="right" valign="top"
- >$self.pysrc_link(func_doc)$
- $self.callgraph_link(callgraph)$</td>
- </tr></table>
- $self.render_callgraph(callgraph)$
- $descr$
- <dl class="fields">
- >>> # === parameters ===
- >>> if arg_descrs:
- <dt>Parameters:</dt>
- <dd><ul class="nomargin-top">
- >>> for lhs, rhs in arg_descrs:
- $self.labelled_list_item(lhs, rhs)$
- >>> #endfor
- </ul></dd>
- >>> #endif
- >>> # === return type ===
- >>> if rdescr and rtype:
- <dt>Returns: $rtype$</dt>
- <dd>$rdescr$</dd>
- >>> elif rdescr:
- <dt>Returns:</dt>
- <dd>$rdescr$</dd>
- >>> elif rtype:
- <dt>Returns: $rtype$</dt>
- >>> #endif
- >>> # === decorators ===
- >>> if func_doc.decorators not in (None, UNKNOWN):
- >>> # (staticmethod & classmethod are already shown, above)
- >>> decos = filter(lambda deco:
- >>> not ((deco=="staticmethod" and
- >>> isinstance(func_doc, StaticMethodDoc)) or
- >>> (deco=="classmethod" and
- >>> isinstance(func_doc, ClassMethodDoc))),
- >>> func_doc.decorators)
- >>> else:
- >>> decos = None
- >>> #endif
- >>> if decos:
- <dt>Decorators:</dt>
- <dd><ul class="nomargin-top">
- >>> for deco in decos:
- <li><code>@$deco$</code></li>
- >>> #endfor
- </ul></dd>
- >>> #endif
- >>> # === exceptions ===
- >>> if func_doc.exception_descrs not in (None, UNKNOWN, (), []):
- <dt>Raises:</dt>
- <dd><ul class="nomargin-top">
- >>> for name, descr in func_doc.exception_descrs:
- >>> exc_name = self.docindex.find(name, func_doc)
- >>> if exc_name is not None:
- >>> name = self.href(exc_name, label=str(name))
- >>> #endif
- $self.labelled_list_item(
- "<code><strong class=\'fraise\'>" +
- str(name) + "</strong></code>",
- self.docstring_to_html(descr, func_doc, 8))$
- >>> #endfor
- </ul></dd>
- >>> #endif
- >>> # === overrides ===
- >>> if var_doc.overrides not in (None, UNKNOWN):
- <dt>Overrides:
- >>> # Avoid passing GenericValueDoc to href()
- >>> if isinstance(var_doc.overrides.value, RoutineDoc):
- $self.href(var_doc.overrides.value, context=var_doc)$
- >>> else:
- >>> # In this case, a less interesting label is generated.
- $self.href(var_doc.overrides, context=var_doc)$
- >>> #endif
- >>> if (func_doc.docstring in (None, UNKNOWN) and
- >>> var_doc.overrides.value.docstring not in (None, UNKNOWN)):
- <dd><em class="note">(inherited documentation)</em></dd>
- >>> #endif
- </dt>
- >>> #endif
- </dl>
- >>> # === metadata ===
- >>> self.write_standard_fields(out, func_doc)
- </td></tr></table>
- </div>
- ''')
- # \------------------------------------------------------------/
- # Names for the __special__ methods.
- SPECIAL_METHODS ={
- '__init__': 'Constructor',
- '__del__': 'Destructor',
- '__add__': 'Addition operator',
- '__sub__': 'Subtraction operator',
- '__and__': 'And operator',
- '__or__': 'Or operator',
- '__xor__': 'Exclusive-Or operator',
- '__repr__': 'Representation operator',
- '__call__': 'Call operator',
- '__getattr__': 'Qualification operator',
- '__getitem__': 'Indexing operator',
- '__setitem__': 'Index assignment operator',
- '__delitem__': 'Index deletion operator',
- '__delslice__': 'Slice deletion operator',
- '__setslice__': 'Slice assignment operator',
- '__getslice__': 'Slicling operator',
- '__len__': 'Length operator',
- '__cmp__': 'Comparison operator',
- '__eq__': 'Equality operator',
- '__in__': 'Containership operator',
- '__gt__': 'Greater-than operator',
- '__lt__': 'Less-than operator',
- '__ge__': 'Greater-than-or-equals operator',
- '__le__': 'Less-than-or-equals operator',
- '__radd__': 'Right-side addition operator',
- '__hash__': 'Hashing function',
- '__contains__': 'In operator',
- '__nonzero__': 'Boolean test operator',
- '__str__': 'Informal representation operator',
- }
- write_property_details_entry = compile_template(
- '''
- write_property_details_entry(self, out, var_doc, descr, \
- accessors, div_class)
- ''',
- # /------------------------- Template -------------------------\
- '''
- >>> prop_doc = var_doc.value
- <a name="$var_doc.name$"></a>
- <div$div_class$>
- >>> self.write_table_header(out, "details")
- <tr><td>
- <h3 class="epydoc">$var_doc.name$</h3>
- $descr$
- <dl class="fields">
- >>> for (name, val, summary) in accessors:
- <dt>$name$ Method:</dt>
- <dd class="value">$val$
- >>> if summary:
- - $summary$
- >>> #endif
- </dd>
- >>> #endfor
- >>> if prop_doc.type_descr not in (None, UNKNOWN):
- <dt>Type:</dt>
- <dd>$self.type_descr(var_doc, indent=6)$</dd>
- >>> #endif
- </dl>
- >>> self.write_standard_fields(out, prop_doc)
- </td></tr></table>
- </div>
- ''')
- # \------------------------------------------------------------/
-
- write_variable_details_entry = compile_template(
- '''
- write_variable_details_entry(self, out, var_doc, descr, div_class)
- ''',
- # /------------------------- Template -------------------------\
- '''
- <a name="$var_doc.name$"></a>
- <div$div_class$>
- >>> self.write_table_header(out, "details")
- <tr><td>
- <h3 class="epydoc">$var_doc.name$</h3>
- $descr$
- <dl class="fields">
- >>> if var_doc.type_descr not in (None, UNKNOWN):
- <dt>Type:</dt>
- <dd>$self.type_descr(var_doc, indent=6)$</dd>
- >>> #endif
- </dl>
- >>> self.write_standard_fields(out, var_doc)
- >>> if var_doc.value is not UNKNOWN:
- <dl class="fields">
- <dt>Value:</dt>
- <dd>$self.pprint_value(var_doc.value)$</dd>
- </dl>
- >>> #endif
- </td></tr></table>
- </div>
- ''')
- # \------------------------------------------------------------/
- def variable_tooltip(self, var_doc):
- if var_doc.value in (None, UNKNOWN):
- return ''
- s = var_doc.value.pyval_repr().to_plaintext(None)
- if len(s) > self._variable_tooltip_linelen:
- s = s[:self._variable_tooltip_linelen-3]+'...'
- return ' title="%s"' % plaintext_to_html(s)
- def pprint_value(self, val_doc):
- if val_doc is UNKNOWN:
- return '??'
- elif isinstance(val_doc, GenericValueDoc):
- return ('<table><tr><td><pre class="variable">\n' +
- val_doc.pyval_repr().to_html(None) +
- '\n</pre></td></tr></table>\n')
- else:
- return self.href(val_doc)
- #////////////////////////////////////////////////////////////
- #{ Base Tree
- #////////////////////////////////////////////////////////////
- def base_tree(self, doc, width=None, postfix='', context=None):
- """
- @return: The HTML code for a class's base tree. The tree is
- drawn 'upside-down' and right justified, to allow for
- multiple inheritance.
- @rtype: C{string}
- """
- if context is None:
- context = doc.defining_module
- if width == None: width = self.find_tree_width(doc, context)
- if isinstance(doc, ClassDoc) and doc.bases != UNKNOWN:
- bases = doc.bases
- else:
- bases = []
-
- if postfix == '':
- # [XX] use var name instead of canonical name?
- s = (' '*(width-2) + '<strong class="uidshort">'+
- self.contextual_label(doc, context)+'</strong>\n')
- else: s = ''
- for i in range(len(bases)-1, -1, -1):
- base = bases[i]
- label = self.contextual_label(base, context)
- s = (' '*(width-4-len(label)) + self.href(base, label)
- +' --+'+postfix+'\n' +
- ' '*(width-4) +
- ' |'+postfix+'\n' +
- s)
- if i != 0:
- s = (self.base_tree(base, width-4, ' |'+postfix, context)+s)
- else:
- s = (self.base_tree(base, width-4, ' '+postfix, context)+s)
- return s
- def find_tree_width(self, doc, context):
- """
- Helper function for L{base_tree}.
- @return: The width of a base tree, when drawn
- right-justified. This is used by L{base_tree} to
- determine how far to indent lines of the base tree.
- @rtype: C{int}
- """
- if not isinstance(doc, ClassDoc): return 2
- if doc.bases == UNKNOWN: return 2
- width = 2
- for base in doc.bases:
- width = max(width, len(self.contextual_label(base, context))+4,
- self.find_tree_width(base, context)+4)
- return width
- def contextual_label(self, doc, context):
- """
- Return the label for C{doc} to be shown in C{context}.
- """
- if doc.canonical_name is None:
- if doc.parse_repr is not None:
- return doc.parse_repr
- else:
- return '??'
- else:
- if context is UNKNOWN:
- return str(doc.canonical_name)
- else:
- context_name = context.canonical_name
- return str(doc.canonical_name.contextualize(context_name))
-
- #////////////////////////////////////////////////////////////
- #{ Function Signatures
- #////////////////////////////////////////////////////////////
- def function_signature(self, api_doc, is_summary=False,
- link_name=False, anchor=False, context=None):
- """Render a function signature in HTML.
- @param api_doc: The object whose name is to be rendered. If a
- C{VariableDoc}, its C{value} should be a C{RoutineDoc}
- @type api_doc: L{VariableDoc} or L{RoutineDoc}
- @param is_summary: True if the fuction is to be rendered in the summary.
- type css_class: C{bool}
- @param link_name: If True, the name is a link to the object anchor.
- @type link_name: C{bool}
- @param anchor: If True, the name is the object anchor.
- @type anchor: C{bool}
- @param context: If set, represent the function name from this context.
- Only useful when C{api_doc} is a L{RoutineDoc}.
- @type context: L{DottedName}
- @return: The HTML code for the object.
- @rtype: C{str}
- """
- if is_summary: css_class = 'summary-sig'
- else: css_class = 'sig'
-
- # [XX] clean this up!
- if isinstance(api_doc, VariableDoc):
- func_doc = api_doc.value
- # This should never happen, but just in case:
- if api_doc.value in (None, UNKNOWN):
- return (('<span class="%s"><span class="%s-name">%s'+
- '</span>(...)</span>') %
- (css_class, css_class, api_doc.name))
- # Get the function's name.
- name = self.summary_name(api_doc, css_class=css_class+'-name',
- link_name=link_name, anchor=anchor)
- else:
- func_doc = api_doc
- name = self.href(api_doc, css_class=css_class+'-name',
- context=context)
- if func_doc.posargs == UNKNOWN:
- args = ['...']
- else:
- args = [self.func_arg(n, d, css_class) for (n, d)
- in zip(func_doc.posargs, func_doc.posarg_defaults)]
- if func_doc.vararg not in (None, UNKNOWN):
- if func_doc.vararg == '...':
- args.append('<span class="%s-arg">...</span>' % css_class)
- else:
- args.append('<span class="%s-arg">*%s</span>' %
- (css_class, func_doc.vararg))
- if func_doc.kwarg not in (None, UNKNOWN):
- args.append('<span class="%s-arg">**%s</span>' %
- (css_class, func_doc.kwarg))
- return ('<span class="%s">%s(%s)</span>' %
- (css_class, name, ',\n '.join(args)))
- def summary_name(self, api_doc, css_class='summary-name',
- link_name=False, anchor=False):
- """Render an object name in HTML.
- @param api_doc: The object whose name is to be rendered
- @type api_doc: L{APIDoc}
- @param css_class: The CSS class to assign to the rendered name
- type css_class: C{str}
- @param link_name: If True, the name is a link to the object anchor.
- @type link_name: C{bool}
- @param anchor: If True, the name is the object anchor.
- @type anchor: C{bool}
- @return: The HTML code for the object.
- @rtype: C{str}
- """
- if anchor:
- rv = '<a name="%s"></a>' % api_doc.name
- else:
- rv = ''
- if link_name:
- rv += self.href(api_doc, css_class=css_class)
- else:
- rv += '<span class="%s">%s</span>' % (css_class, api_doc.name)
- return rv
- # [xx] tuple args???
- def func_arg(self, name, default, css_class):
- name = self._arg_name(name)
- s = '<span class="%s-arg">%s</span>' % (css_class, name)
- if default is not None:
- s += ('=<span class="%s-default">%s</span>' %
- (css_class, default.summary_pyval_repr().to_html(None)))
- return s
- def _arg_name(self, arg):
- if isinstance(arg, basestring):
- return arg
- elif len(arg) == 1:
- return '(%s,)' % self._arg_name(arg[0])
- else:
- return '(%s)' % (', '.join([self._arg_name(a) for a in arg]))
-
- #////////////////////////////////////////////////////////////
- #{ Import Lists
- #////////////////////////////////////////////////////////////
-
- def write_imports(self, out, doc):
- assert isinstance(doc, NamespaceDoc)
- imports = doc.select_variables(imported=True,
- public=self._public_filter)
- if not imports: return
- out('<p class="indent-wrapped-lines">')
- out('<b>Imports:</b>\n ')
- out(',\n '.join([self._import(v, doc) for v in imports]))
- out('\n</p><br />\n')
- def _import(self, var_doc, context):
- if var_doc.imported_from not in (None, UNKNOWN):
- return self.href(var_doc.imported_from,
- var_doc.name, context=context,
- tooltip='%s' % var_doc.imported_from)
- elif (var_doc.value not in (None, UNKNOWN) and not
- isinstance(var_doc.value, GenericValueDoc)):
- return self.href(var_doc.value,
- var_doc.name, context=context,
- tooltip='%s' % var_doc.value.canonical_name)
- else:
- return plaintext_to_html(var_doc.name)
-
- #////////////////////////////////////////////////////////////
- #{ Function Attributes
- #////////////////////////////////////////////////////////////
-
- #////////////////////////////////////////////////////////////
- #{ Module Trees
- #////////////////////////////////////////////////////////////
- def write_module_list(self, out, doc):
- if len(doc.submodules) == 0: return
- self.write_table_header(out, "summary", "Submodules")
- for group_name in doc.group_names():
- if not doc.submodule_groups[group_name]: continue
- if group_name:
- self.write_group_header(out, group_name)
- out(' <tr><td class="summary">\n'
- ' <ul class="nomargin">\n')
- for submodule in doc.submodule_groups[group_name]:
- self.write_module_tree_item(out, submodule, package=doc)
- out(' </ul></td></tr>\n')
-
- out(self.TABLE_FOOTER+'\n<br />\n')
- def write_module_tree_item(self, out, doc, package=None):
- # If it's a private variable, then mark its <li>.
- var = package and package.variables.get(doc.canonical_name[-1])
- priv = ((var is not None and var.is_public is False) or
- (var is None and doc.canonical_name[-1].startswith('_')))
- out(' <li%s> <strong class="uidlink">%s</strong>'
- % (priv and ' class="private"' or '', self.href(doc)))
- if doc.summary not in (None, UNKNOWN):
- out(': <em class="summary">'+
- self.description(doc.summary, doc, 8)+'</em>')
- if doc.submodules != UNKNOWN and doc.submodules:
- if priv: out('\n <ul class="private">\n')
- else: out('\n <ul>\n')
- for submodule in doc.submodules:
- self.write_module_tree_item(out, submodule, package=doc)
- out(' </ul>\n')
- out(' </li>\n')
- #////////////////////////////////////////////////////////////
- #{ Class trees
- #////////////////////////////////////////////////////////////
- write_class_tree_item = compile_template(
- '''
- write_class_tree_item(self, out, doc, class_set)
- ''',
- # /------------------------- Template -------------------------\
- '''
- >>> if doc.summary in (None, UNKNOWN):
- <li> <strong class="uidlink">$self.href(doc)$</strong>
- >>> else:
- <li> <strong class="uidlink">$self.href(doc)$</strong>:
- <em class="summary">$self.description(doc.summary, doc, 8)$</em>
- >>> # endif
- >>> if doc.subclasses:
- <ul>
- >>> for subclass in sorted(set(doc.subclasses), key=lambda c:c.canonical_name[-1]):
- >>> if subclass in class_set:
- >>> self.write_class_tree_item(out, subclass, class_set)
- >>> #endif
- >>> #endfor
- </ul>
- >>> #endif
- </li>
- ''')
- # \------------------------------------------------------------/
-
- #////////////////////////////////////////////////////////////
- #{ Standard Fields
- #////////////////////////////////////////////////////////////
- def write_standard_fields(self, out, doc):
- """
- Write HTML code containing descriptions of any standard markup
- fields that are defined by the given L{APIDoc} object (such as
- C{@author} and C{@todo} fields).
- @param doc: The L{APIDoc} object containing the API documentation
- for the object whose standard markup fields should be
- described.
- """
- fields = []
- field_values = {}
-
- for (field, arg, descr) in doc.metadata:
- if field not in field_values:
- fields.append(field)
- if field.takes_arg:
- subfields = field_values.setdefault(field,{})
- subfields.setdefault(arg,[]).append(descr)
- else:
- field_values.setdefault(field,[]).append(descr)
- if not fields: return
- out('<div class="fields">')
- for field in fields:
- if field.takes_arg:
- for arg, descrs in field_values[field].items():
- self.write_standard_field(out, doc, field, descrs, arg)
-
- else:
- self.write_standard_field(out, doc, field, field_values[field])
- out('</div>')
- write_standard_field = compile_template(
- """
- write_standard_field(self, out, doc, field, descrs, arg='')
-
- """,
- # /------------------------- Template -------------------------\
- '''
- >>> if arg: arglabel = " (%s)" % arg
- >>> else: arglabel = ""
- >>> if len(descrs) == 1:
- <p><strong>$field.singular+arglabel$:</strong>
- $self.description(descrs[0], doc, 8)$
- </p>
- >>> elif field.short:
- <dl><dt>$field.plural+arglabel$:</dt>
- <dd>
- >>> for descr in descrs[:-1]:
- $self.description(descr, doc, 10)$,
- >>> # end for
- $self.description(descrs[-1], doc, 10)$
- </dd>
- </dl>
- >>> else:
- <strong>$field.plural+arglabel$:</strong>
- <ul class="nomargin-top">
- >>> for descr in descrs:
- <li>
- $self.description(descr, doc, 8)$
- </li>
- >>> # end for
- </ul>
- >>> # end else
- >>> # end for
- ''')
- # \------------------------------------------------------------/
- #////////////////////////////////////////////////////////////
- #{ Index generation
- #////////////////////////////////////////////////////////////
- #: A list of metadata indices that should be generated. Each
- #: entry in this list is a tuple C{(tag, label, short_label)},
- #: where C{tag} is the cannonical tag of a metadata field;
- #: C{label} is a label for the index page; and C{short_label}
- #: is a shorter label, used in the index selector.
- METADATA_INDICES = [('bug', 'Bug List', 'Bugs'),
- ('todo', 'To Do List', 'To Do'),
- ('change', 'Change Log', 'Changes'),
- ('deprecated', 'Deprecation List', 'Deprecations'),
- ('since', 'Introductions List', 'Introductions'),
- ]
-
- def build_identifier_index(self):
- items = []
- for doc in self.indexed_docs:
- name = plaintext_to_html(doc.canonical_name[-1])
- if isinstance(doc, RoutineDoc): name += '()'
- url = self.url(doc)
- if not url: continue
- container = self.docindex.container(doc)
- items.append( (name, url, container) )
- return sorted(items, key=lambda v:v[0].lower())
- def _group_by_letter(self, items):
- """Preserves sort order of the input."""
- index = {}
- for item in items:
- first_letter = item[0][0].upper()
- if not ("A" <= first_letter <= "Z"):
- first_letter = '_'
- index.setdefault(first_letter, []).append(item)
- return index
-
- def build_term_index(self):
- items = []
- for doc in self.indexed_docs:
- url = self.url(doc)
- items += self._terms_from_docstring(url, doc, doc.descr)
- for (field, arg, descr) in doc.metadata:
- items += self._terms_from_docstring(url, doc, descr)
- if hasattr(doc, 'type_descr'):
- items += self._terms_from_docstring(url, doc,
- doc.type_descr)
- if hasattr(doc, 'return_descr'):
- items += self._terms_from_docstring(url, doc,
- doc.return_descr)
- if hasattr(doc, 'return_type'):
- items += self._terms_from_docstring(url, doc,
- doc.return_type)
- return sorted(items, key=lambda v:v[0].lower())
- def _terms_from_docstring(self, base_url, container, parsed_docstring):
- if parsed_docstring in (None, UNKNOWN): return []
- terms = []
- # Strip any existing anchor off:
- base_url = re.sub('#.*', '', '%s' % (base_url,))
- for term in parsed_docstring.index_terms():
- anchor = self._term_index_to_anchor(term)
- url = '%s#%s' % (base_url, anchor)
- terms.append( (term.to_plaintext(None), url, container) )
- return terms
- def build_metadata_index(self, field_name):
- # Build the index.
- index = {}
- for doc in self.indexed_docs:
- if (not self._show_private and
- self._doc_or_ancestor_is_private(doc)):
- continue
- descrs = {}
- if doc.metadata is not UNKNOWN:
- for (field, arg, descr) in doc.metadata:
- if field.tags[0] == field_name:
- descrs.setdefault(arg, []).append(descr)
- for (arg, descr_list) in descrs.iteritems():
- index.setdefault(arg, []).append( (doc, descr_list) )
- return index
- def _term_index_to_anchor(self, term):
- """
- Given the name of an inline index item, construct a URI anchor.
- These anchors are used to create links from the index page to each
- index item.
- """
- # Include "-" so we don't accidentally collide with the name
- # of a python identifier.
- s = re.sub(r'\s\s+', '-', term.to_plaintext(None))
- return "index-"+re.sub("[^a-zA-Z0-9]", "_", s)
- #////////////////////////////////////////////////////////////
- #{ Redirect page
- #////////////////////////////////////////////////////////////
- def write_redirect_page(self, out):
- """
- Build the auto-redirect page, which translates dotted names to
- URLs using javascript. When the user visits
- <redirect.html#dotted.name>, they will automatically get
- redirected to the page for the object with the given
- fully-qualified dotted name. E.g., for epydoc,
- <redirect.html#epydoc.apidoc.UNKNOWN> redirects the user to
- <epydoc.apidoc-module.html#UNKNOWN>.
- """
- # Construct a list of all the module & class pages that we're
- # documenting. The redirect_url javascript will scan through
- # this list, looking for a page name that matches the
- # requested dotted name.
- pages = (['%s-m' % val_doc.canonical_name
- for val_doc in self.module_list] +
- ['%s-c' % val_doc.canonical_name
- for val_doc in self.class_list])
- # Sort the pages from longest to shortest. This ensures that
- # we find e.g. "x.y.z" in the list before "x.y".
- pages = sorted(pages, key=lambda p:-len(p))
- # Write the redirect page.
- self._write_redirect_page(out, pages)
- _write_redirect_page = compile_template(
- '''
- _write_redirect_page(self, out, pages)
- ''',
- # /------------------------- Template -------------------------\
- '''
- <html><head><title>Epydoc Redirect Page</title>
- <meta http-equiv="cache-control" content="no-cache" />
- <meta http-equiv="expires" content="0" />
- <meta http-equiv="pragma" content="no-cache" />
- <script type="text/javascript" src="epydoc.js"></script>
- </head>
- <body>
- <script type="text/javascript">
- <!--
- var pages = $"[%s]" % ", ".join(['"%s"' % v for v in pages])$;
- var dottedName = get_anchor();
- if (dottedName) {
- var target = redirect_url(dottedName);
- if (target) window.location.replace(target);
- }
- // -->
- </script>
- <h3>Epydoc Auto-redirect page</h3>
-
- <p>When javascript is enabled, this page will redirect URLs of
- the form <tt>redirect.html#<i>dotted.name</i></tt> to the
- documentation for the object with the given fully-qualified
- dotted name.</p>
- <p><a id="message"> </a></p>
-
- <script type="text/javascript">
- <!--
- if (dottedName) {
- var msg = document.getElementById("message");
- msg.innerHTML = "No documentation found for <tt>"+
- dottedName+"</tt>";
- }
- // -->
- </script>
- </body>
- </html>
- ''')
- # \------------------------------------------------------------/
- #////////////////////////////////////////////////////////////
- #{ URLs list
- #////////////////////////////////////////////////////////////
- def write_api_list(self, out):
- """
- Write a list of mapping name->url for all the documented objects.
- """
- # Construct a list of all the module & class pages that we're
- # documenting. The redirect_url javascript will scan through
- # this list, looking for a page name that matches the
- # requested dotted name.
- skip = (ModuleDoc, ClassDoc, type(UNKNOWN))
- for val_doc in self.module_list:
- self.write_url_record(out, val_doc)
- for var in val_doc.variables.itervalues():
- if not isinstance(var.value, skip):
- self.write_url_record(out, var)
- for val_doc in self.class_list:
- self.write_url_record(out, val_doc)
- for var in val_doc.variables.itervalues():
- self.write_url_record(out, var)
- def write_url_record(self, out, obj):
- url = self.url(obj)
- if url is not None:
- out("%s\t%s\n" % (obj.canonical_name, url))
- #////////////////////////////////////////////////////////////
- #{ Helper functions
- #////////////////////////////////////////////////////////////
- def _val_is_public(self, valdoc):
- """Make a best-guess as to whether the given class is public."""
- container = self.docindex.container(valdoc)
- if isinstance(container, NamespaceDoc):
- for vardoc in container.variables.values():
- if vardoc in (UNKNOWN, None): continue
- if vardoc.value is valdoc:
- return vardoc.is_public
- return True
- # [XX] Is it worth-while to pull the anchor tricks that I do here?
- # Or should I just live with the fact that show/hide private moves
- # stuff around?
- write_table_header = compile_template(
- '''
- write_table_header(self, out, css_class, heading=None, \
- private_link=True, colspan=2)
- ''',
- # /------------------------- Template -------------------------\
- '''
- >>> if heading is not None:
- >>> anchor = "section-%s" % re.sub("\W", "", heading)
- <!-- ==================== $heading.upper()$ ==================== -->
- <a name="$anchor$"></a>
- >>> #endif
- <table class="$css_class$" border="1" cellpadding="3"
- cellspacing="0" width="100%" bgcolor="white">
- >>> if heading is not None:
- <tr bgcolor="#70b0f0" class="table-header">
- >>> if private_link and self._show_private:
- <td colspan="$colspan$" class="table-header">
- <table border="0" cellpadding="0" cellspacing="0" width="100%">
- <tr valign="top">
- <td align="left"><span class="table-header">$heading$</span></td>
- <td align="right" valign="top"
- ><span class="options">[<a href="#$anchor$"
- class="privatelink" onclick="toggle_private();"
- >hide private</a>]</span></td>
- </tr>
- </table>
- </td>
- >>> else:
- <td align="left" colspan="2" class="table-header">
- <span class="table-header">$heading$</span></td>
- >>> #endif
- </tr>
- >>> #endif
- ''')
- # \------------------------------------------------------------/
- TABLE_FOOTER = '</table>\n'
- PRIVATE_LINK = '''
- <span class="options">[<a href="javascript:void(0);" class="privatelink"
- onclick="toggle_private();">hide private</a>]</span>
- '''.strip()
- write_group_header = compile_template(
- '''
- write_group_header(self, out, group, tr_class='')
- ''',
- # /------------------------- Template -------------------------\
- '''
- <tr bgcolor="#e8f0f8" $tr_class$>
- <th colspan="2" class="group-header"
- > $group$</th></tr>
- ''')
- # \------------------------------------------------------------/
- _url_cache = {}
- def url(self, obj):
- """
- Return the URL for the given object, which can be a
- C{VariableDoc}, a C{ValueDoc}, or a C{DottedName}.
- """
- cached_url = self._url_cache.get(id(obj))
- if cached_url is not None:
- return cached_url
- else:
- url = self._url_cache[id(obj)] = self._url(obj)
- return url
- def _url(self, obj):
- """
- Internal helper for L{url}.
- """
- # Module: <canonical_name>-module.html
- if isinstance(obj, ModuleDoc):
- if obj not in self.module_set: return None
- return urllib.quote('%s'%obj.canonical_name) + '-module.html'
- # Class: <canonical_name>-class.html
- elif isinstance(obj, ClassDoc):
- if obj not in self.class_set: return None
- return urllib.quote('%s'%obj.canonical_name) + '-class.html'
- # Variable
- elif isinstance(obj, VariableDoc):
- val_doc = obj.value
- if isinstance(val_doc, (ModuleDoc, ClassDoc)):
- return self.url(val_doc)
- elif obj.container in (None, UNKNOWN):
- if val_doc in (None, UNKNOWN): return None
- return self.url(val_doc)
- elif obj.is_imported == True:
- if obj.imported_from is not UNKNOWN:
- return self.url(obj.imported_from)
- else:
- return None
- else:
- container_url = self.url(obj.container)
- if container_url is None: return None
- return '%s#%s' % (container_url, urllib.quote('%s'%obj.name))
- # Value (other than module or class)
- elif isinstance(obj, ValueDoc):
- container = self.docindex.container(obj)
- if container is None:
- return None # We couldn't find it!
- else:
- container_url = self.url(container)
- if container_url is None: return None
- anchor = urllib.quote('%s'%obj.canonical_name[-1])
- return '%s#%s' % (container_url, anchor)
- # Dotted name: look up the corresponding APIDoc
- elif isinstance(obj, DottedName):
- val_doc = self.docindex.get_valdoc(obj)
- if val_doc is None: return None
- return self.url(val_doc)
- # Special pages:
- elif obj == 'indices':
- return 'identifier-index.html'
- elif obj == 'help':
- return 'help.html'
- elif obj == 'trees':
- return self._trees_url
- else:
- raise ValueError, "Don't know what to do with %r" % obj
- def pysrc_link(self, api_doc):
- if not self._incl_sourcecode:
- return ''
- url = self.pysrc_url(api_doc)
- if url is not None:
- return ('<span class="codelink"><a href="%s">source '
- 'code</a></span>' % url)
- else:
- return ''
-
- def pysrc_url(self, api_doc):
- if isinstance(api_doc, VariableDoc):
- if api_doc.value not in (None, UNKNOWN):
- return pysrc_url(api_doc.value)
- else:
- return None
- elif isinstance(api_doc, ModuleDoc):
- if api_doc in self.modules_with_sourcecode:
- return ('%s-pysrc.html' %
- urllib.quote('%s' % api_doc.canonical_name))
- else:
- return None
- else:
- module = api_doc.defining_module
- if module == UNKNOWN: return None
- module_pysrc_url = self.pysrc_url(module)
- if module_pysrc_url is None: return None
- module_name = module.canonical_name
- if not module_name.dominates(api_doc.canonical_name, True):
- log.debug('%r is in %r but name does not dominate' %
- (api_doc, module))
- return module_pysrc_url
- mname_len = len(module.canonical_name)
- anchor = '%s' % api_doc.canonical_name[mname_len:]
- return '%s#%s' % (module_pysrc_url, urllib.quote(anchor))
-
- # We didn't find it:
- return None
- # [xx] add code to automatically do <code> wrapping or the like?
- def href(self, target, label=None, css_class=None, context=None,
- tooltip=None):
- """
- Return the HTML code for an HREF link to the given target
- (which can be a C{VariableDoc}, a C{ValueDoc}, or a
- C{DottedName}.
- If a C{NamespaceDoc} C{context} is specified, the target label is
- contextualized to it.
- """
- assert isinstance(target, (APIDoc, DottedName))
- # Pick a label, if none was given.
- if label is None:
- if isinstance(target, VariableDoc):
- label = target.name
- elif (isinstance(target, ValueDoc) and
- target.canonical_name is not UNKNOWN):
- label = target.canonical_name
- elif isinstance(target, DottedName):
- label = target
- elif isinstance(target, GenericValueDoc):
- raise ValueError("href() should not be called with "
- "GenericValueDoc objects (perhaps you "
- "meant to use the containing variable?)")
- else:
- raise ValueError("Unable to find a label for %r" % target)
-
- if context is not None and isinstance(label, DottedName):
- label = label.contextualize(context.canonical_name.container())
-
- label = plaintext_to_html(str(label))
-
- # Munge names for scripts & unreachable values
- if label.startswith('script-'):
- label = label[7:] + ' (script)'
- if label.startswith('??'):
- label = '<i>unreachable</i>' + label[2:]
- label = re.sub(r'-\d+$', '', label)
- # Get the url for the target.
- url = self.url(target)
- if url is None:
- if tooltip: return '<span title="%s">%s</span>' % (tooltip, label)
- else: return label
- # Construct a string for the class attribute.
- if css_class is None:
- css = ''
- else:
- css = ' class="%s"' % css_class
- onclick = ''
- if ((isinstance(target, VariableDoc) and not target.is_public) or
- (isinstance(target, ValueDoc) and
- not isinstance(target, GenericValueDoc) and
- not self._val_is_public(target))):
- onclick = ' onclick="show_private();"'
- if tooltip:
- tooltip = ' title="%s"' % tooltip
- else:
- tooltip = ''
- return '<a href="%s"%s%s%s>%s</a>' % (url, css, onclick, tooltip, label)
- def _attr_to_html(self, attr, api_doc, indent):
- if api_doc in (None, UNKNOWN):
- return ''
- pds = getattr(api_doc, attr, None) # pds = ParsedDocstring.
- if pds not in (None, UNKNOWN):
- return self.docstring_to_html(pds, api_doc, indent)
- elif isinstance(api_doc, VariableDoc):
- return self._attr_to_html(attr, api_doc.value, indent)
-
- def summary(self, api_doc, indent=0):
- return self._attr_to_html('summary', api_doc, indent)
-
- def descr(self, api_doc, indent=0):
- return self._attr_to_html('descr', api_doc, indent)
- def type_descr(self, api_doc, indent=0):
- return self._attr_to_html('type_descr', api_doc, indent)
- def return_type(self, api_doc, indent=0):
- return self._attr_to_html('return_type', api_doc, indent)
- def return_descr(self, api_doc, indent=0):
- return self._attr_to_html('return_descr', api_doc, indent)
- def docstring_to_html(self, parsed_docstring, where=None, indent=0):
- if parsed_docstring in (None, UNKNOWN): return ''
- linker = _HTMLDocstringLinker(self, where)
- s = parsed_docstring.to_html(linker, indent=indent,
- directory=self._directory,
- docindex=self.docindex,
- context=where).strip()
- if self._mark_docstrings:
- s = '<span class="docstring">%s</span><!--end docstring-->' % s
- return s
- def description(self, parsed_docstring, where=None, indent=0):
- assert isinstance(where, (APIDoc, type(None)))
- if parsed_docstring in (None, UNKNOWN): return ''
- linker = _HTMLDocstringLinker(self, where)
- descr = parsed_docstring.to_html(linker, indent=indent,
- directory=self._directory,
- docindex=self.docindex,
- context=where).strip()
- if descr == '': return ' '
- return descr
- # [xx] Should this be defined by the APIDoc classes themselves??
- def doc_kind(self, doc):
- if isinstance(doc, ModuleDoc) and doc.is_package == True:
- return 'Package'
- elif (isinstance(doc, ModuleDoc) and
- doc.canonical_name[0].startswith('script')):
- return 'Script'
- elif isinstance(doc, ModuleDoc):
- return 'Module'
- elif isinstance(doc, ClassDoc):
- return 'Class'
- elif isinstance(doc, ClassMethodDoc):
- return 'Class Method'
- elif isinstance(doc, StaticMethodDoc):
- return 'Static Method'
- elif isinstance(doc, RoutineDoc):
- if isinstance(self.docindex.container(doc), ClassDoc):
- return 'Method'
- else:
- return 'Function'
- else:
- return 'Variable'
-
- def _doc_or_ancestor_is_private(self, api_doc):
- name = api_doc.canonical_name
- for i in range(len(name), 0, -1):
- # Is it (or an ancestor) a private var?
- var_doc = self.docindex.get_vardoc(name[:i])
- if var_doc is not None and var_doc.is_public == False:
- return True
- # Is it (or an ancestor) a private module?
- val_doc = self.docindex.get_valdoc(name[:i])
- if (val_doc is not None and isinstance(val_doc, ModuleDoc) and
- val_doc.canonical_name[-1].startswith('_')):
- return True
- return False
- def _private_subclasses(self, class_doc):
- """Return a list of all subclasses of the given class that are
- private, as determined by L{_val_is_private}. Recursive
- subclasses are included in this list."""
- queue = [class_doc]
- private = set()
- for cls in queue:
- if (isinstance(cls, ClassDoc) and
- cls.subclasses not in (None, UNKNOWN)):
- queue.extend(cls.subclasses)
- private.update([c for c in cls.subclasses if
- not self._val_is_public(c)])
- return private
-
- class _HTMLDocstringLinker(epydoc.markup.DocstringLinker):
- def __init__(self, htmlwriter, container):
- self.htmlwriter = htmlwriter
- self.docindex = htmlwriter.docindex
- self.container = container
-
- def translate_indexterm(self, indexterm):
- key = self.htmlwriter._term_index_to_anchor(indexterm)
- return ('<a name="%s"></a><i class="indexterm">%s</i>' %
- (key, indexterm.to_html(self)))
-
- def translate_identifier_xref(self, identifier, label=None):
- # Pick a label for this xref.
- if label is None: label = plaintext_to_html(identifier)
- # Find the APIDoc for it (if it's available).
- doc = self.docindex.find(identifier, self.container)
- # If we didn't find a target, then try checking in the contexts
- # of the ancestor classes.
- if doc is None and isinstance(self.container, RoutineDoc):
- container = self.docindex.get_vardoc(
- self.container.canonical_name)
- while (doc is None and container not in (None, UNKNOWN)
- and container.overrides not in (None, UNKNOWN)):
- container = container.overrides
- doc = self.docindex.find(identifier, container)
-
- # Translate it into HTML.
- if doc is None:
- self._failed_xref(identifier)
- return '<code class="link">%s</code>' % label
- else:
- return self.htmlwriter.href(doc, label, 'link')
- # [xx] Should this be added to the DocstringLinker interface???
- # Currently, this is *only* used by dotgraph.
- def url_for(self, identifier):
- if isinstance(identifier, (basestring, DottedName)):
- doc = self.docindex.find(identifier, self.container)
- if doc:
- return self.htmlwriter.url(doc)
- else:
- return None
-
- elif isinstance(identifier, APIDoc):
- return self.htmlwriter.url(identifier)
- doc = identifier
-
- else:
- raise TypeError('Expected string or APIDoc')
- def _failed_xref(self, identifier):
- """Add an identifier to the htmlwriter's failed crossreference
- list."""
- # Don't count it as a failed xref if it's a parameter of the
- # current function.
- if (isinstance(self.container, RoutineDoc) and
- identifier in self.container.all_args()):
- return
-
- failed_xrefs = self.htmlwriter._failed_xrefs
- context = self.container.canonical_name
- failed_xrefs.setdefault(identifier,{})[context] = 1