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

Source Code for Module epydoc.docwriter.html

   1  # 
   2  # epydoc -- HTML output generator 
   3  # Edward Loper 
   4  # 
   5  # Created [01/30/01 05:18 PM] 
   6  # $Id: html.py 1804 2008-02-27 19:54:39Z edloper $ 
   7  # 
   8   
   9  """ 
  10  The HTML output generator for epydoc.  The main interface provided by 
  11  this module is the L{HTMLWriter} class. 
  12   
  13  @todo: Add a cache to L{HTMLWriter.url()}? 
  14  """ 
  15  __docformat__ = 'epytext en' 
  16   
  17  import re, os, sys, codecs, sre_constants, pprint, base64 
  18  import urllib 
  19  import __builtin__ 
  20  from epydoc.apidoc import * 
  21  import epydoc.docstringparser 
  22  import time, epydoc, epydoc.markup, epydoc.markup.epytext 
  23  from epydoc.docwriter.html_colorize import PythonSourceColorizer 
  24  from epydoc.docwriter import html_colorize 
  25  from epydoc.docwriter.html_css import STYLESHEETS 
  26  from epydoc.docwriter.html_help import HTML_HELP 
  27  from epydoc.docwriter.dotgraph import * 
  28  from epydoc import log 
  29  from epydoc.util import plaintext_to_html, is_src_filename 
  30  from epydoc.compat import * # Backwards compatibility 
  31   
  32  ###################################################################### 
  33  ## Template Compiler 
  34  ###################################################################### 
  35  # The compile_template() method defined in this section is used to 
  36  # define several of HTMLWriter's methods. 
  37   
38 -def compile_template(docstring, template_string, 39 output_function='out', debug=epydoc.DEBUG):
40 """ 41 Given a template string containing inline python source code, 42 return a python function that will fill in the template, and 43 output the result. The signature for this function is taken from 44 the first line of C{docstring}. Output is generated by making 45 repeated calls to the output function with the given name (which 46 is typically one of the function's parameters). 47 48 The templating language used by this function passes through all 49 text as-is, with three exceptions: 50 51 - If every line in the template string is indented by at least 52 M{x} spaces, then the first M{x} spaces are stripped from each 53 line. 54 55 - Any line that begins with '>>>' (with no indentation) 56 should contain python code, and will be inserted as-is into 57 the template-filling function. If the line begins a control 58 block (such as 'if' or 'for'), then the control block will 59 be closed by the first '>>>'-marked line whose indentation is 60 less than or equal to the line's own indentation (including 61 lines that only contain comments.) 62 63 - In any other line, any expression between two '$' signs will 64 be evaluated and inserted into the line (using C{str()} to 65 convert the result to a string). 66 67 Here is a simple example: 68 69 >>> TEMPLATE = ''' 70 ... <book> 71 ... <title>$book.title$</title> 72 ... <pages>$book.count_pages()$</pages> 73 ... >>> for chapter in book.chapters: 74 ... <chaptername>$chapter.name$</chaptername> 75 ... >>> #endfor 76 ... </book> 77 >>> write_book = compile_template('write_book(out, book)', TEMPLATE) 78 79 @newfield acknowledgements: Acknowledgements 80 @acknowledgements: The syntax used by C{compile_template} is 81 loosely based on Cheetah. 82 """ 83 # Extract signature from the docstring: 84 signature = docstring.lstrip().split('\n',1)[0].strip() 85 func_name = signature.split('(',1)[0].strip() 86 87 # Regexp to search for inline substitutions: 88 INLINE = re.compile(r'\$([^\$]+)\$') 89 # Regexp to search for python statements in the template: 90 COMMAND = re.compile(r'(^>>>.*)\n?', re.MULTILINE) 91 92 # Strip indentation from the template. 93 template_string = strip_indent(template_string) 94 95 # If we're debugging, then we'll store the generated function, 96 # so we can print it along with any tracebacks that depend on it. 97 if debug: 98 signature = re.sub(r'\)\s*$', ', __debug=__debug)', signature) 99 100 # Funciton declaration line 101 pysrc_lines = ['def %s:' % signature] 102 indents = [-1] 103 104 if debug: 105 pysrc_lines.append(' try:') 106 indents.append(-1) 107 108 commands = COMMAND.split(template_string.strip()+'\n') 109 for i, command in enumerate(commands): 110 if command == '': continue 111 112 # String literal segment: 113 if i%2 == 0: 114 pieces = INLINE.split(command) 115 for j, piece in enumerate(pieces): 116 if j%2 == 0: 117 # String piece 118 pysrc_lines.append(' '*len(indents)+ 119 '%s(%r)' % (output_function, piece)) 120 else: 121 # Variable piece 122 pysrc_lines.append(' '*len(indents)+ 123 '%s(unicode(%s))' % (output_function, piece)) 124 125 # Python command: 126 else: 127 srcline = command[3:].lstrip() 128 # Update indentation 129 indent = len(command)-len(srcline) 130 while indent <= indents[-1]: indents.pop() 131 # Add on the line. 132 srcline = srcline.rstrip() 133 pysrc_lines.append(' '*len(indents)+srcline) 134 if srcline.endswith(':'): 135 indents.append(indent) 136 137 if debug: 138 pysrc_lines.append(' except Exception,e:') 139 pysrc_lines.append(' pysrc, func_name = __debug ') 140 pysrc_lines.append(' lineno = sys.exc_info()[2].tb_lineno') 141 pysrc_lines.append(' print ("Exception in template %s() on "') 142 pysrc_lines.append(' "line %d:" % (func_name, lineno))') 143 pysrc_lines.append(' print pysrc[lineno-1]') 144 pysrc_lines.append(' raise') 145 146 pysrc = '\n'.join(pysrc_lines)+'\n' 147 #log.debug(pysrc) 148 if debug: localdict = {'__debug': (pysrc_lines, func_name)} 149 else: localdict = {} 150 try: exec pysrc in globals(), localdict 151 except SyntaxError: 152 log.error('Error in script:\n' + pysrc + '\n') 153 raise 154 template_func = localdict[func_name] 155 template_func.__doc__ = docstring 156 return template_func
157
158 -def strip_indent(s):
159 """ 160 Given a multiline string C{s}, find the minimum indentation for 161 all non-blank lines, and return a new string formed by stripping 162 that amount of indentation from all lines in C{s}. 163 """ 164 # Strip indentation from the template. 165 minindent = sys.maxint 166 lines = s.split('\n') 167 for line in lines: 168 stripline = line.lstrip() 169 if stripline: 170 minindent = min(minindent, len(line)-len(stripline)) 171 return '\n'.join([l[minindent:] for l in lines])
172 173 ###################################################################### 174 ## HTML Writer 175 ###################################################################### 176
177 -class HTMLWriter:
178 #//////////////////////////////////////////////////////////// 179 # Table of Contents 180 #//////////////////////////////////////////////////////////// 181 # 182 # 1. Interface Methods 183 # 184 # 2. Page Generation -- write complete web page files 185 # 2.1. Module Pages 186 # 2.2. Class Pages 187 # 2.3. Trees Page 188 # 2.4. Indices Page 189 # 2.5. Help Page 190 # 2.6. Frames-based table of contents pages 191 # 2.7. Homepage (index.html) 192 # 2.8. CSS Stylesheet 193 # 2.9. Javascript file 194 # 2.10. Graphs 195 # 2.11. Images 196 # 197 # 3. Page Element Generation -- write pieces of a web page file 198 # 3.1. Page Header 199 # 3.2. Page Footer 200 # 3.3. Navigation Bar 201 # 3.4. Breadcrumbs 202 # 3.5. Summary Tables 203 # 204 # 4. Helper functions 205
206 - def __init__(self, docindex, **kwargs):
207 """ 208 Construct a new HTML writer, using the given documentation 209 index. 210 211 @param docindex: The documentation index. 212 213 @type prj_name: C{string} 214 @keyword prj_name: The name of the project. Defaults to 215 none. 216 @type prj_url: C{string} 217 @keyword prj_url: The target for the project hopeage link on 218 the navigation bar. If C{prj_url} is not specified, 219 then no hyperlink is created. 220 @type prj_link: C{string} 221 @keyword prj_link: The label for the project link on the 222 navigation bar. This link can contain arbitrary HTML 223 code (e.g. images). By default, a label is constructed 224 from C{prj_name}. 225 @type top_page: C{string} 226 @keyword top_page: The top page for the documentation. This 227 is the default page shown main frame, when frames are 228 enabled. C{top} can be a URL, the name of a 229 module, the name of a class, or one of the special 230 strings C{"trees.html"}, C{"indices.html"}, or 231 C{"help.html"}. By default, the top-level package or 232 module is used, if there is one; otherwise, C{"trees"} 233 is used. 234 @type css: C{string} 235 @keyword css: The CSS stylesheet file. If C{css} is a file 236 name, then the specified file's conents will be used. 237 Otherwise, if C{css} is the name of a CSS stylesheet in 238 L{epydoc.docwriter.html_css}, then that stylesheet will 239 be used. Otherwise, an error is reported. If no stylesheet 240 is specified, then the default stylesheet is used. 241 @type help_file: C{string} 242 @keyword help_file: The name of the help file. If no help file is 243 specified, then the default help file will be used. 244 @type show_private: C{boolean} 245 @keyword show_private: Whether to create documentation for 246 private objects. By default, private objects are documented. 247 @type show_frames: C{boolean}) 248 @keyword show_frames: Whether to create a frames-based table of 249 contents. By default, it is produced. 250 @type show_imports: C{boolean} 251 @keyword show_imports: Whether or not to display lists of 252 imported functions and classes. By default, they are 253 not shown. 254 @type variable_maxlines: C{int} 255 @keyword variable_maxlines: The maximum number of lines that 256 should be displayed for the value of a variable in the 257 variable details section. By default, 8 lines are 258 displayed. 259 @type variable_linelength: C{int} 260 @keyword variable_linelength: The maximum line length used for 261 displaying the values of variables in the variable 262 details sections. If a line is longer than this length, 263 then it will be wrapped to the next line. The default 264 line length is 70 characters. 265 @type variable_summary_linelength: C{int} 266 @keyword variable_summary_linelength: The maximum line length 267 used for displaying the values of variables in the summary 268 section. If a line is longer than this length, then it 269 will be truncated. The default is 40 characters. 270 @type variable_tooltip_linelength: C{int} 271 @keyword variable_tooltip_linelength: The maximum line length 272 used for tooltips for the values of variables. If a 273 line is longer than this length, then it will be 274 truncated. The default is 600 characters. 275 @type property_function_linelength: C{int} 276 @keyword property_function_linelength: The maximum line length 277 used to dispaly property functions (C{fget}, C{fset}, and 278 C{fdel}) that contain something other than a function 279 object. The default length is 40 characters. 280 @type inheritance: C{string} 281 @keyword inheritance: How inherited objects should be displayed. 282 If C{inheritance='grouped'}, then inherited objects are 283 gathered into groups; if C{inheritance='listed'}, then 284 inherited objects are listed in a short list at the 285 end of their group; if C{inheritance='included'}, then 286 inherited objects are mixed in with non-inherited 287 objects. The default is 'grouped'. 288 @type include_source_code: C{boolean} 289 @keyword include_source_code: If true, then generate colorized 290 source code files for each python module. 291 @type include_log: C{boolean} 292 @keyword include_log: If true, the the footer will include an 293 href to the page 'epydoc-log.html'. 294 @type include_timestamp: C{boolean} 295 @keyword include_timestamp: If true, then include a timestamp in 296 the footer. 297 @type src_code_tab_width: C{int} 298 @keyword src_code_tab_width: Number of spaces to replace each tab 299 with in source code listings. 300 """ 301 self.docindex = docindex 302 303 # Process keyword arguments. 304 self._show_private = kwargs.get('show_private', 1) 305 """Should private docs be included?""" 306 307 self._prj_name = kwargs.get('prj_name', None) 308 """The project's name (for the project link in the navbar)""" 309 310 self._prj_url = kwargs.get('prj_url', None) 311 """URL for the project link in the navbar""" 312 313 self._prj_link = kwargs.get('prj_link', None) 314 """HTML code for the project link in the navbar""" 315 316 self._top_page = kwargs.get('top_page', None) 317 """The 'main' page""" 318 319 self._css = kwargs.get('css') 320 """CSS stylesheet to use""" 321 322 self._helpfile = kwargs.get('help_file', None) 323 """Filename of file to extract help contents from""" 324 325 self._frames_index = kwargs.get('show_frames', 1) 326 """Should a frames index be created?""" 327 328 self._show_imports = kwargs.get('show_imports', False) 329 """Should imports be listed?""" 330 331 self._propfunc_linelen = kwargs.get('property_function_linelength', 40) 332 """[XXX] Not used!""" 333 334 self._variable_maxlines = kwargs.get('variable_maxlines', 8) 335 """Max lines for variable values""" 336 337 self._variable_linelen = kwargs.get('variable_linelength', 70) 338 """Max line length for variable values""" 339 340 self._variable_summary_linelen = \ 341 kwargs.get('variable_summary_linelength', 65) 342 """Max length for variable value summaries""" 343 344 self._variable_tooltip_linelen = \ 345 kwargs.get('variable_tooltip_linelength', 600) 346 """Max length for variable tooltips""" 347 348 self._inheritance = kwargs.get('inheritance', 'listed') 349 """How should inheritance be displayed? 'listed', 'included', 350 or 'grouped'""" 351 352 self._incl_sourcecode = kwargs.get('include_source_code', True) 353 """Should pages be generated for source code of modules?""" 354 355 self._mark_docstrings = kwargs.get('mark_docstrings', False) 356 """Wrap <span class='docstring'>...</span> around docstrings?""" 357 358 self._graph_types = kwargs.get('graphs', ()) or () 359 """Graphs that we should include in our output.""" 360 361 self._include_log = kwargs.get('include_log', False) 362 """Are we generating an HTML log page?""" 363 364 self._include_timestamp = kwargs.get('include_timestamp', True) 365 """Include a timestamp on the generated docs?""" 366 367 self._src_code_tab_width = kwargs.get('src_code_tab_width', 8) 368 """Number of spaces to replace each tab with in source code 369 listings.""" 370 371 self._callgraph_cache = {} 372 """Map the callgraph L{uid<DotGraph.uid>} to their HTML 373 representation.""" 374 375 self._redundant_details = kwargs.get('redundant_details', False) 376 """If true, then include objects in the details list even if all 377 info about them is already provided by the summary table.""" 378 379 self._show_submodule_list = kwargs.get('show_submodule_list', True) 380 """If true, the include a list of submodules on the package 381 documentation page.""" 382 383 self._google_analytics = kwargs.get('google_analytics', None) 384 """A tracker code for google analytics; or None""" 385 386 # For use with select_variables(): 387 if self._show_private: 388 self._public_filter = None 389 else: 390 self._public_filter = True 391 392 # Make sure inheritance has a sane value. 393 if self._inheritance not in ('listed', 'included', 394 'grouped', 'hidden'): 395 raise ValueError, 'Bad value for inheritance' 396 397 # Create the project homepage link, if it was not specified. 398 if (self._prj_name or self._prj_url) and not self._prj_link: 399 self._prj_link = plaintext_to_html(self._prj_name or 400 'Project Homepage') 401 402 # Add a hyperlink to _prj_url, if _prj_link doesn't already 403 # contain any hyperlinks. 404 if (self._prj_link and self._prj_url and 405 not re.search(r'<a[^>]*\shref', self._prj_link)): 406 self._prj_link = ('<a class="navbar" target="_top" href="'+ 407 self._prj_url+'">'+self._prj_link+'</a>') 408 409 # Precompute lists & sets of APIDoc objects that we're 410 # interested in. 411 self.valdocs = valdocs = sorted(docindex.reachable_valdocs( 412 imports=False, packages=False, bases=False, submodules=False, 413 subclasses=False, private=self._show_private)) 414 self.module_list = [d for d in valdocs if isinstance(d, ModuleDoc)] 415 """The list of L{ModuleDoc}s for the documented modules.""" 416 self.module_set = set(self.module_list) 417 """The set of L{ModuleDoc}s for the documented modules.""" 418 self.class_list = [d for d in valdocs if isinstance(d, ClassDoc)] 419 """The list of L{ClassDoc}s for the documented classes.""" 420 self.class_set = set(self.class_list) 421 """The set of L{ClassDoc}s for the documented classes.""" 422 self.routine_list = [d for d in valdocs if isinstance(d, RoutineDoc)] 423 """The list of L{RoutineDoc}s for the documented routines.""" 424 self.indexed_docs = [] 425 """The list of L{APIDoc}s for variables and values that should 426 be included in the index.""" 427 428 # URL for 'trees' page 429 if self.module_list: self._trees_url = 'module-tree.html' 430 else: self._trees_url = 'class-tree.html' 431 432 # Construct the value for self.indexed_docs. 433 self.indexed_docs += [d for d in valdocs 434 if not isinstance(d, GenericValueDoc)] 435 for doc in valdocs: 436 if isinstance(doc, NamespaceDoc): 437 # add any vars with generic values; but don't include 438 # inherited vars. 439 self.indexed_docs += [d for d in doc.variables.values() if 440 isinstance(d.value, GenericValueDoc) 441 and d.container == doc] 442 self.indexed_docs.sort() 443 444 # Figure out the url for the top page. 445 self._top_page_url = self._find_top_page(self._top_page) 446 447 # Decide whether or not to split the identifier index. 448 self._split_ident_index = (len(self.indexed_docs) >= 449 self.SPLIT_IDENT_INDEX_SIZE) 450 451 # Figure out how many output files there will be (for progress 452 # reporting). 453 self.modules_with_sourcecode = set() 454 for doc in self.module_list: 455 if isinstance(doc, ModuleDoc) and is_src_filename(doc.filename): 456 self.modules_with_sourcecode.add(doc) 457 self._num_files = (len(self.class_list) + len(self.module_list) + 458 10 + len(self.METADATA_INDICES)) 459 if self._frames_index: 460 self._num_files += len(self.module_list) + 3 461 462 if self._incl_sourcecode: 463 self._num_files += len(self.modules_with_sourcecode) 464 if self._split_ident_index: 465 self._num_files += len(self.LETTERS)
466
467 - def _find_top_page(self, pagename):
468 """ 469 Find the top page for the API documentation. This page is 470 used as the default page shown in the main frame, when frames 471 are used. When frames are not used, this page is copied to 472 C{index.html}. 473 474 @param pagename: The name of the page, as specified by the 475 keyword argument C{top} to the constructor. 476 @type pagename: C{string} 477 @return: The URL of the top page. 478 @rtype: C{string} 479 """ 480 # If a page name was specified, then we need to figure out 481 # what it points to. 482 if pagename: 483 # If it's a URL, then use it directly. 484 if pagename.lower().startswith('http:'): 485 return pagename 486 487 # If it's an object, then use that object's page. 488 try: 489 doc = self.docindex.get_valdoc(pagename) 490 return self.url(doc) 491 except: 492 pass 493 494 # Otherwise, give up. 495 log.warning('Could not find top page %r; using %s ' 496 'instead' % (pagename, self._trees_url)) 497 return self._trees_url 498 499 # If no page name was specified, then try to choose one 500 # automatically. 501 else: 502 root = [val_doc for val_doc in self.docindex.root 503 if isinstance(val_doc, (ClassDoc, ModuleDoc))] 504 if len(root) == 0: 505 # No docs?? Try the trees page. 506 return self._trees_url 507 elif len(root) == 1: 508 # One item in the root; use that. 509 return self.url(root[0]) 510 else: 511 # Multiple root items; if they're all in one package, 512 # then use that. Otherwise, use self._trees_url 513 root = sorted(root, key=lambda