/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
Large files files are truncated, but you can click here to view the full file
- #
- # 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…
Large files files are truncated, but you can click here to view the full file