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

Source Code for Module epydoc.docwriter.html

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