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