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