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