PageRenderTime 84ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/python/helpers/epydoc/docwriter/html.py

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