1
2
3
4
5
6
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 *
31
32
33
34
35
36
37
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
84 signature = docstring.lstrip().split('\n',1)[0].strip()
85 func_name = signature.split('(',1)[0].strip()
86
87
88 INLINE = re.compile(r'\$([^\$]+)\$')
89
90 COMMAND = re.compile(r'(^>>>.*)\n?', re.MULTILINE)
91
92
93 template_string = strip_indent(template_string)
94
95
96
97 if debug:
98 signature = re.sub(r'\)\s*$', ', __debug=__debug)', signature)
99
100
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
113 if i%2 == 0:
114 pieces = INLINE.split(command)
115 for j, piece in enumerate(pieces):
116 if j%2 == 0:
117
118 pysrc_lines.append(' '*len(indents)+
119 '%s(%r)' % (output_function, piece))
120 else:
121
122 pysrc_lines.append(' '*len(indents)+
123 '%s(unicode(%s))' % (output_function, piece))
124
125
126 else:
127 srcline = command[3:].lstrip()
128
129 indent = len(command)-len(srcline)
130 while indent <= indents[-1]: indents.pop()
131
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
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
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
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
175
176
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
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
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
387 if self._show_private:
388 self._public_filter = None
389 else:
390 self._public_filter = True
391
392
393 if self._inheritance not in ('listed', 'included',
394 'grouped', 'hidden'):
395 raise ValueError, 'Bad value for inheritance'
396
397
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
403
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
410
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
429 if self.module_list: self._trees_url = 'module-tree.html'
430 else: self._trees_url = 'class-tree.html'
431
432
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
438
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
445 self._top_page_url = self._find_top_page(self._top_page)
446
447
448 self._split_ident_index = (len(self.indexed_docs) >=
449 self.SPLIT_IDENT_INDEX_SIZE)
450
451
452
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
481
482 if pagename:
483
484 if pagename.lower().startswith('http:'):
485 return pagename
486
487
488 try:
489 doc = self.docindex.get_valdoc(pagename)
490 return self.url(doc)
491 except:
492 pass
493
494
495 log.warning('Could not find top page %r; using %s '
496 'instead' % (pagename, self._trees_url))
497 return self._trees_url
498
499
500
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
506 return self._trees_url
507 elif len(root) == 1:
508
509 return self.url(root[0])
510 else:
511
512
513 root = sorted(root, key=lambda