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 __docformat__ = 'epytext en'
14
15 import re, os, sys, codecs, sre_constants, pprint
16 import urllib
17 from epydoc.apidoc import *
18 import epydoc.docstringparser
19 import time, epydoc, epydoc.markup
20 from epydoc.docwriter.html_colorize import colorize_re
21 from epydoc.docwriter.html_colorize import PythonSourceColorizer
22 from epydoc.docwriter import html_colorize
23 from epydoc.docwriter.html_css import STYLESHEETS
24 from epydoc.docwriter.html_help import HTML_HELP
25 from epydoc.docwriter.dotgraph import *
26 from epydoc import log
27 from epydoc.util import plaintext_to_html, is_src_filename
28 from epydoc.compat import *
29
30
31
32
33
34
35
38 """
39 Given a template string containing inline python source code,
40 return a python function that will fill in the template, and
41 output the result. The signature for this function is taken from
42 the first line of C{docstring}. Output is generated by making
43 repeated calls to the output function with the given name (which
44 is typically one of the function's parameters).
45
46 The templating language used by this function passes through all
47 text as-is, with three exceptions:
48
49 - If every line in the template string is indented by at least
50 M{x} spaces, then the first M{x} spaces are stripped from each
51 line.
52
53 - Any line that begins with '>>>' (with no indentation)
54 should contain python code, and will be inserted as-is into
55 the template-filling function. If the line begins a control
56 block (such as 'if' or 'for'), then the control block will
57 be closed by the first '>>>'-marked line whose indentation is
58 less than or equal to the line's own indentation (including
59 lines that only contain comments.)
60
61 - In any other line, any expression between two '$' signs will
62 be evaluated and inserted into the line (using C{str()} to
63 convert the result to a string).
64
65 Here is a simple example:
66
67 >>> TEMPLATE = '''
68 ... <book>
69 ... <title>$book.title$</title>
70 ... <pages>$book.count_pages()$</pages>
71 ... >>> for chapter in book.chapters:
72 ... <chaptername>$chapter.name$</chaptername>
73 ... >>> #endfor
74 ... </book>
75 >>> write_book = compile_template('write_book(out, book)', TEMPLATE)
76
77 @newfield acknowledgements: Acknowledgements
78 @acknowledgements: The syntax used by C{compile_template} is
79 loosely based on Cheetah.
80 """
81
82 signature = docstring.lstrip().split('\n',1)[0].strip()
83 func_name = signature.split('(',1)[0].strip()
84
85
86 INLINE = re.compile(r'\$([^\$]+)\$')
87
88 COMMAND = re.compile(r'(^>>>.*)\n?', re.MULTILINE)
89
90
91 template_string = strip_indent(template_string)
92
93
94
95 if debug:
96 signature = re.sub(r'\)\s*$', ', __debug=__debug)', signature)
97
98
99 pysrc_lines = ['def %s:' % signature]
100 indents = [-1]
101
102 if debug:
103 pysrc_lines.append(' try:')
104 indents.append(-1)
105
106 commands = COMMAND.split(template_string.strip()+'\n')
107 for i, command in enumerate(commands):
108 if command == '': continue
109
110
111 if i%2 == 0:
112 pieces = INLINE.split(command)
113 for j, piece in enumerate(pieces):
114 if j%2 == 0:
115
116 pysrc_lines.append(' '*len(indents)+
117 '%s(%r)' % (output_function, piece))
118 else:
119
120 pysrc_lines.append(' '*len(indents)+
121 '%s(unicode(%s))' % (output_function, piece))
122
123
124 else:
125 srcline = command[3:].lstrip()
126
127 indent = len(command)-len(srcline)
128 while indent <= indents[-1]: indents.pop()
129
130 srcline = srcline.rstrip()
131 pysrc_lines.append(' '*len(indents)+srcline)
132 if srcline.endswith(':'):
133 indents.append(indent)
134
135 if debug:
136 pysrc_lines.append(' except Exception,e:')
137 pysrc_lines.append(' pysrc, func_name = __debug ')
138 pysrc_lines.append(' lineno = sys.exc_info()[2].tb_lineno')
139 pysrc_lines.append(' print ("Exception in template %s() on "')
140 pysrc_lines.append(' "line %d:" % (func_name, lineno))')
141 pysrc_lines.append(' print pysrc[lineno-1]')
142 pysrc_lines.append(' raise')
143
144 pysrc = '\n'.join(pysrc_lines)+'\n'
145 if debug: localdict = {'__debug': (pysrc_lines, func_name)}
146 else: localdict = {}
147 try: exec pysrc in globals(), localdict
148 except SyntaxError:
149 log.error('Error in script:\n' + pysrc + '\n')
150 raise
151 template_func = localdict[func_name]
152 template_func.__doc__ = docstring
153 return template_func
154
156 """
157 Given a multiline string C{s}, find the minimum indentation for
158 all non-blank lines, and return a new string formed by stripping
159 that amount of indentation from all lines in C{s}.
160 """
161
162 minindent = sys.maxint
163 lines = s.split('\n')
164 for line in lines:
165 stripline = line.lstrip()
166 if stripline:
167 minindent = min(minindent, len(line)-len(stripline))
168 return '\n'.join([l[minindent:] for l in lines])
169
170
171
172
173
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201 - def __init__(self, docindex, **kwargs):
202 """
203 Construct a new HTML writer, using the given documentation
204 index.
205
206 @param docmap: The documentation index.
207
208 @type prj_name: C{string}
209 @keyword prj_name: The name of the project. Defaults to
210 none.
211 @type prj_url: C{string}
212 @keyword prj_url: The target for the project hopeage link on
213 the navigation bar. If C{prj_url} is not specified,
214 then no hyperlink is created.
215 @type prj_link: C{string}
216 @keyword prj_link: The label for the project link on the
217 navigation bar. This link can contain arbitrary HTML
218 code (e.g. images). By default, a label is constructed
219 from C{prj_name}.
220 @type top_page: C{string}
221 @keyword top_page: The top page for the documentation. This
222 is the default page shown main frame, when frames are
223 enabled. C{top} can be a URL, the name of a
224 module, the name of a class, or one of the special
225 strings C{"trees.html"}, C{"indices.html"}, or
226 C{"help.html"}. By default, the top-level package or
227 module is used, if there is one; otherwise, C{"trees"}
228 is used.
229 @type css: C{string}
230 @keyword css: The CSS stylesheet file. If C{css} is a file
231 name, then the specified file's conents will be used.
232 Otherwise, if C{css} is the name of a CSS stylesheet in
233 L{epydoc.docwriter.html_css}, then that stylesheet will
234 be used. Otherwise, an error is reported. If no stylesheet
235 is specified, then the default stylesheet is used.
236 @type help_file: C{string}
237 @keyword help_file: The name of the help file. If no help file is
238 specified, then the default help file will be used.
239 @type show_private: C{boolean}
240 @keyword show_private: Whether to create documentation for
241 private objects. By default, private objects are documented.
242 @type show_frames: C{boolean})
243 @keyword show_frames: Whether to create a frames-based table of
244 contents. By default, it is produced.
245 @type show_imports: C{boolean}
246 @keyword show_imports: Whether or not to display lists of
247 imported functions and classes. By default, they are
248 not shown.
249 @type variable_maxlines: C{int}
250 @keyword variable_maxlines: The maximum number of lines that
251 should be displayed for the value of a variable in the
252 variable details section. By default, 8 lines are
253 displayed.
254 @type variable_linelength: C{int}
255 @keyword variable_linelength: The maximum line length used for
256 displaying the values of variables in the variable
257 details sections. If a line is longer than this length,
258 then it will be wrapped to the next line. The default
259 line length is 70 characters.
260 @type variable_summary_linelength: C{int}
261 @keyword variable_summary_linelength: The maximum line length
262 used for displaying the values of variables in the summary
263 section. If a line is longer than this length, then it
264 will be truncated. The default is 40 characters.
265 @type variable_tooltip_linelength: C{int}
266 @keyword variable_tooltip_linelength: The maximum line length
267 used for tooltips for the values of variables. If a
268 line is longer than this length, then it will be
269 truncated. The default is 600 characters.
270 @type property_function_linelength: C{int}
271 @keyword property_function_linelength: The maximum line length
272 used to dispaly property functions (C{fget}, C{fset}, and
273 C{fdel}) that contain something other than a function
274 object. The default length is 40 characters.
275 @type inheritance: C{string}
276 @keyword inheritance: How inherited objects should be displayed.
277 If C{inheritance='grouped'}, then inherited objects are
278 gathered into groups; if C{inheritance='listed'}, then
279 inherited objects are listed in a short list at the
280 end of their group; if C{inheritance='included'}, then
281 inherited objects are mixed in with non-inherited
282 objects. The default is 'grouped'.
283 @type include_sourcecode: C{boolean}
284 @param include_sourcecode: If true, then generate colorized
285 source code files for each python module.
286 """
287 self.docindex = docindex
288
289
290 self._show_private = kwargs.get('show_private', 1)
291 """Should private docs be included?"""
292
293 self._prj_name = kwargs.get('prj_name', None)
294 """The project's name (for the project link in the navbar)"""
295
296 self._prj_url = kwargs.get('prj_url', None)
297 """URL for the project link in the navbar"""
298
299 self._prj_link = kwargs.get('prj_link', None)
300 """HTML code for the project link in the navbar"""
301
302 self._top_page = kwargs.get('top_page', None)
303 """The 'main' page"""
304
305 self._css = kwargs.get('css')
306 """CSS stylesheet to use"""
307
308 self._helpfile = kwargs.get('help_file', None)
309 """Filename of file to extract help contents from"""
310
311 self._frames_index = kwargs.get('show_frames', 1)
312 """Should a frames index be created?"""
313
314 self._show_imports = kwargs.get('show_imports', False)
315 """Should imports be listed?"""
316
317 self._propfunc_linelen = kwargs.get('property_function_linelength', 40)
318 """[XXX] Not used!"""
319
320 self._variable_maxlines = kwargs.get('variable_maxlines', 8)
321 """Max lines for variable values"""
322
323 self._variable_linelen = kwargs.get('variable_linelength', 70)
324 """Max line length for variable values"""
325
326 self._variable_summary_linelen = \
327 kwargs.get('variable_summary_linelength', 55)
328 """Max length for variable value summaries"""
329
330 self._variable_tooltip_linelen = \
331 kwargs.get('variable_tooltip_linelength', 600)
332 """Max length for variable tooltips"""
333
334 self._inheritance = kwargs.get('inheritance', 'listed')
335 """How should inheritance be displayed? 'listed', 'included',
336 or 'grouped'"""
337
338 self._incl_sourcecode = kwargs.get('include_source_code', True)
339 """Should pages be generated for source code of modules?"""
340
341 self._mark_docstrings = kwargs.get('mark_docstrings', False)
342 """Wrap <span class='docstring'>...</span> around docstrings?"""
343
344 self._graph_types = kwargs.get('graphs', ()) or ()
345 """Graphs that we should include in our output."""
346
347
348 if self._show_private:
349 self._public_filter = None
350 else:
351 self._public_filter = True
352
353
354 if self._inheritance not in ('listed', 'included', 'grouped'):
355 raise ValueError, 'Bad value for inheritance'
356
357
358 if (self._prj_name or self._prj_url) and not self._prj_link:
359 self._prj_link = plaintext_to_html(self._prj_name or
360 'Project Homepage')
361
362
363
364 if (self._prj_link and self._prj_url and
365 not re.search(r'<a[^>]*\shref', self._prj_link)):
366 self._prj_link = ('<a class="navbar" target="_top" href="'+
367 self._prj_url+'">'+self._prj_link+'</a>')
368
369
370
371 self.valdocs = valdocs = sorted(docindex.reachable_valdocs(
372 imports=False, packages=False, bases=False, submodules=False,
373 subclasses=False, private=self._show_private))
374 self.module_list = [d for d in valdocs if isinstance(d, ModuleDoc)]
375 """The list of L{ModuleDoc}s for the documented modules."""
376 self.module_set = set(self.module_list)
377 """The set of L{ModuleDoc}s for the documented modules."""
378 self.class_list = [d for d in valdocs if isinstance(d, ClassDoc)]
379 """The list of L{ClassDoc}s for the documented classes."""
380 self.class_set = set(self.class_list)
381 """The set of L{ClassDoc}s for the documented classes."""
382 self.routine_list = [d for d in valdocs if isinstance(d, RoutineDoc)]
383 """The list of L{RoutineDoc}s for the documented routines."""
384 self.indexed_docs = []
385 """The list of L{APIDoc}s for variables and values that should
386 be included in the index."""
387
388
389 self.indexed_docs += [d for d in valdocs
390 if not isinstance(d, GenericValueDoc)]
391 for doc in valdocs:
392 if isinstance(doc, NamespaceDoc):
393 self.indexed_docs += [doc for doc in doc.variables.values() if
394 isinstance(doc.value, GenericValueDoc)]
395 self.indexed_docs.sort()
396
397
398 self._top_page_url = self._find_top_page(self._top_page)
399
400
401
402 self.modules_with_sourcecode = set()
403 for doc in self.module_list:
404 if isinstance(doc, ModuleDoc) and is_src_filename(doc.filename):
405 self.modules_with_sourcecode.add(doc)
406 self._num_files = len(self.class_list) + 2*len(self.module_list) + 9
407 if self._incl_sourcecode:
408 self._num_files += len(self.modules_with_sourcecode)
409
410 - def _find_top_page(self, pagename):
411 """
412 Find the top page for the API documentation. This page is
413 used as the default page shown in the main frame, when frames
414 are used. When frames are not used, this page is copied to
415 C{index.html}.
416
417 @param pagename: The name of the page, as specified by the
418 keyword argument C{top} to the constructor.
419 @type pagename: C{string}
420 @return: The URL of the top page.
421 @rtype: C{string}
422 """
423
424
425 if pagename:
426
427 if pagename.lower().startswith('http:'):
428 return pagename
429
430
431 try:
432 doc = self.docindex.get_valdoc(pagename)
433 return self.url(doc)
434 except:
435 pass
436
437
438 log.warning('Could not find top page %r; using trees.html '
439 'instead' % pagename)
440
441
442
443 else:
444 root = [val_doc for val_doc in self.docindex.root
445 if isinstance(val_doc, (ClassDoc, ModuleDoc))]
446 if len(root) == 0:
447
448 return 'trees.html'
449 elif len(root) == 1:
450
451 return self.url(root[0])
452 else:
453
454
455 root = sorted(root, key=lambda v:len(v.canonical_name))
456 top = root[0]
457 for doc in root[1:]:
458 if not top.canonical_name.dominates(doc.canonical_name):
459 return 'trees.html'
460 else:
461 return self.url(top)
462
463
464
465
466
467 - def write(self, directory=None):
468 """
469 Write the documentation to the given directory.
470
471 @type directory: C{string}
472 @param directory: The directory to which output should be
473 written. If no directory is specified, output will be
474 written to the current directory. If the directory does
475 not exist, it will be created.
476 @rtype: C{None}
477 @raise OSError: If C{directory} cannot be created.
478 @raise OSError: If any file cannot be created or written to.
479 """
480
481 self._files_written = 0.
482
483
484 self._failed_xrefs = {}
485
486
487 if not directory: directory = os.curdir
488 self._mkdir(directory)
489 self._directory = directory
490
491
492 self._files_written += 1
493 log.progress(self._files_written/self._num_files, 'epydoc.css')
494 self.write_css(directory, self._css)
495
496
497 self._files_written += 1
498 log.progress(self._files_written/self._num_files, 'epydoc.js')
499 self.write_javascript(directory)
500
501
502 self._write(self.write_indices, directory, 'indices.html')
503
504
505 self._write(self.write_trees, directory, 'trees.html')
506
507
508 self._write(self.write_help, directory,'help.html')
509
510
511 self._write(self.write_frames_index, directory, 'frames.html')
512 self._write(self.write_toc, directory, 'toc.html')
513 self._write(self.write_project_toc, directory, 'toc-everything.html')
514 for doc in self.module_list:
515 filename = 'toc-%s' % urllib.unquote(self.url(doc))
516 self._write(self.write_module_toc, directory, filename, doc)
517
518
519 for doc in self.module_list:
520 filename = urllib.unquote(self.url(doc))
521 self._write(self.write_module, directory, filename, doc)
522 for doc in self.class_list:
523 filename = urllib.unquote(self.url(doc))
524 self._write(self.write_class, directory, filename, doc)
525
526
527 if self._incl_sourcecode:
528 for doc in self.modules_with_sourcecode:
529 filename = urllib.unquote(self.pysrc_url(doc))
530 self._write(self.write_sourcecode, directory, filename, doc)
531
532
533
534 self._files_written += 1
535 log.progress(self._files_written/self._num_files, 'index.html')
536 self.write_homepage(directory)
537
538
539 if self._failed_xrefs:
540 estr = 'Failed identifier crossreference targets:\n'
541 failed_identifiers = self._failed_xrefs.keys()
542 failed_identifiers.sort()
543 for identifier in failed_identifiers:
544 names = self._failed_xrefs[identifier].keys()
545 names.sort()
546 estr += '- %s' % identifier
547 estr += '\n'
548 for name in names:
549 estr += ' (from %s)\n' % name
550 log.docstring_warning(estr)
551
552 - def _write(self, write_func, directory, filename, *args):
561
563 """
564 If the given directory does not exist, then attempt to create it.
565 @rtype: C{None}
566 """
567 if not os.path.isdir(directory):
568 if os.path.exists(directory):
569 raise OSError('%r is not a directory' % directory)
570 os.mkdir(directory)
571
572
573
574
575
577 """
578 Write an HTML page containing the API documentation for the
579 given module to C{out}.
580
581 @param doc: A L{ModuleDoc} containing the API documentation
582 for the module that should be described.
583 """
584 longname = doc.canonical_name
585 shortname = doc.canonical_name[-1]
586
587
588 self.write_header(out, str(longname))
589 self.write_navbar(out, doc)
590 self.write_breadcrumbs(out, doc, self.url(doc))
591
592
593 if doc.is_package is True: typ = 'Package'
594 else: typ = 'Module'
595 if longname[0].startswith('script-'):
596 shortname = str(longname)[7:]
597 typ = 'Script'
598 out('<!-- ==================== %s ' % typ.upper() +
599 'DESCRIPTION ==================== -->\n')
600 out('<h2 class="%s">%s %s' % (typ.lower(), typ, shortname))
601 src_link = self.pysrc_link(doc)
602 if src_link: out('\n<br/>' + src_link)
603 out('</h2>\n')
604
605
606 if doc.descr not in (None, UNKNOWN):
607 out(self.descr(doc, 2)+'<br /><br />\n\n')
608
609
610 if doc.metadata is not UNKNOWN and doc.metadata:
611 out('<hr />\n')
612 self.write_standard_fields(out, doc)
613
614
615 if doc.is_package is True:
616 self.write_module_list(out, doc)
617
618
619
620 self.write_summary_table(out, "Classes", doc, "class")
621 self.write_summary_table(out, "Functions", doc, "function")
622 self.write_summary_table(out, "Variables", doc, "other")
623
624
625 if self._show_imports:
626 self.write_imports(out, doc)
627
628
629
630 self.write_details_list(out, "Function Details", doc, "function")
631 self.write_details_list(out, "Variables Details", doc, "other")
632
633
634 self.write_navbar(out, doc)
635 self.write_footer(out)
636
637
638
639
640
642 filename = doc.filename
643 name = str(doc.canonical_name)
644
645
646 self.write_header(out, name)
647 self.write_navbar(out, doc)
648 self.write_breadcrumbs(out, doc, self.pysrc_url(doc))
649
650
651 out('<h2 class="py-src">Source Code for %s</h2>\n' %
652 self.href(doc, label='%s %s' % (self.doc_kind(doc), name)))
653 out('<div class="py-src">\n')
654 out('<pre class="py-src">\n')
655 out(PythonSourceColorizer(filename, name, self.docindex,
656 self.indexed_docs, self.url).colorize())
657 out('</pre>\n</div>\n<br />\n')
658
659
660 self.write_navbar(out, doc)
661 self.write_footer(out)
662
663
664
665
666
668 """
669 Write an HTML page containing the API documentation for the
670 given class to C{out}.
671
672 @param doc: A L{ClassDoc} containing the API documentation
673 for the class that should be described.
674 """
675 longname = doc.canonical_name
676 shortname = doc.canonical_name[-1]
677
678
679 self.write_header(out, str(longname))
680 self.write_navbar(out, doc)
681 self.write_breadcrumbs(out, doc, self.url(doc))
682
683
684 if doc.is_type(): typ = 'Type'
685 elif doc.is_exception(): typ = 'Exception'
686 else: typ = 'Class'
687 out('<!-- ==================== %s ' % typ.upper() +
688 'DESCRIPTION ==================== -->\n')
689 out('<h2 class="%s">%s %s' %
690 (typ.lower(), typ, shortname))
691 src_link = self.pysrc_link(doc)
692 if src_link: out('\n<br/>' + src_link)
693 out('</h2>\n')
694
695 if ((doc.bases not in (UNKNOWN, None) and len(doc.bases) > 0) or
696 (doc.subclasses not in (UNKNOWN,None) and len(doc.subclasses)>0)):
697
698 if 'umlclasstree' in self._graph_types:
699 linker = _HTMLDocstringLinker(self, doc)
700 graph = uml_class_tree_graph(doc, linker, doc)
701 out('<center>\n%s</center>\n' % self.render_graph(graph))
702
703 elif 'classtree' in self._graph_types:
704 linker = _HTMLDocstringLinker(self, doc)
705 graph = class_tree_graph([doc], linker, doc)
706 out('<center>\n%s</center>\n' % self.render_graph(graph))
707
708
709 else:
710
711 if doc.bases not in (UNKNOWN, None) and len(doc.bases) > 0:
712 out('<pre class="base-tree">\n%s</pre>\n\n' %
713 self.base_tree(doc))
714
715
716 if (doc.subclasses not in (UNKNOWN, None) and
717 len(doc.subclasses) > 0):
718 out('<dl><dt>Known Subclasses:</dt>\n<dd>\n ')
719 out(',\n '.join([self.href(c, context=doc)
720 for c in doc.subclasses]))
721 out('\n</dd></dl>\n\n')
722
723 out('<hr />\n')
724
725
726 if doc.descr not in (None, UNKNOWN):
727 out(self.descr(doc, 2)+'<br /><br />\n\n')
728
729
730 if doc.metadata is not UNKNOWN and doc.metadata:
731 out('<hr />\n')
732 self.write_standard_fields(out, doc)
733
734
735
736 self.write_summary_table(out, "Nested Classes", doc, "class")
737 self.write_summary_table(out, "Instance Methods", doc,
738 "instancemethod")
739 self.write_summary_table(out, "Class Methods", doc, "classmethod")
740 self.write_summary_table(out, "Static Methods", doc, "staticmethod")
741 self.write_summary_table(out, "Class Variables", doc,
742 "classvariable")
743 self.write_summary_table(out, "Instance Variables", doc,
744 "instancevariable")
745 self.write_summary_table(out, "Properties", doc, "property")
746
747
748 if self._show_imports:
749 self.write_imports(out, doc)
750
751
752
753
754
755
756 self.write_details_list(out, "Method Details", doc, "method")
757 self.write_details_list(out, "Class Variable Details", doc,
758 "classvariable")
759 self.write_details_list(out, "Instance Variable Details", doc,
760 "instancevariable")
761 self.write_details_list(out, "Property Details", doc, "property")
762
763
764 self.write_navbar(out, doc)
765 self.write_footer(out)
766
767
768
769
770
772 """
773 Write an HTML page containing the module and class hierarchies
774 to the given streams.
775 @param public: The output stream for the public version of the page.
776 @param private: The output stream for the private version of the page.
777 """
778
779 self.write_header(out, 'Trees')
780 self.write_navbar(out, 'trees')
781 self.write_breadcrumbs(out, 'trees', 'trees.html')
782
783
784 out('<!-- ==================== '
785 'MODULE HIERARCHY ==================== -->\n')
786 out('<h2>Module Hierarchy</h2>\n')
787 self.write_module_tree(out)
788
789
790 defines_classes = len(self.class_list) > 0
791
792
793 if defines_classes:
794 out('<!-- ==================== '
795 'CLASS HIERARCHY ==================== -->\n')
796 out('<h2>Class Hierarchy</h2>\n')
797 self.write_class_tree(out)
798
799
800 self.write_navbar(out, 'trees')
801 self.write_footer(out)
802
803
804
805
806
808 """
809 Write an HTML page containing the term and identifier indices
810 to the given streams.
811 @bug: If there are private indexed terms, but no public
812 indexed terms, then this function will still write a
813 header for the Term Index to the public stream.
814 @param public: The output stream for the public version of the page.
815 @param private: The output stream for the private version of the page.
816 """
817
818 self.write_header(out, 'Index')
819 self.write_navbar(out, 'indices')
820 self.write_breadcrumbs(out, 'indices', 'indices.html')
821 out('<br />\n')
822
823 terms = self._extract_term_index()
824 if terms:
825 self.write_term_index(out, terms)
826
827
828
829 identifiers = []
830 for doc in self.indexed_docs:
831 name = doc.canonical_name
832 if self.url(doc) is None: continue
833 key = name[-1].lower()
834 key = (key[:1] in 'abcdefghijklmnopqrstuvwxyz', key)
835 identifiers.append( (key, name, doc) )
836
837 identifiers.sort()
838 if identifiers:
839 self.write_identifier_index(out, identifiers)
840
841
842 self.write_navbar(out, 'indices')
843 self.write_footer(out)
844
845 write_identifier_index_header = compile_template(
846 """
847 write_identifier_index_header(self, out)
848 """,
849
850 '''
851 <!-- ==================== IDENTIFIER INDEX ==================== -->
852 <table class="index" border="1" cellpadding="3"
853 cellspacing="0" width="100%" bgcolor="white">
854 <tr bgcolor="#70b0f0" class="index"><th colspan="2">
855 <table border="0" cellpadding="0" cellspacing="0" width="100%">
856 <tr><th class="index">Identifier Index</th>
857 <td width="100%" align="right"> [
858 <a href="#_">_</a>
859 >>> for c in "abcdefghijklmnopqrstuvwxyz":
860 <a href="#$c$">$c$</a>
861 >>> #endfor
862 ] </td>
863 </tr></table>
864 </th></tr>
865 ''')
866
867
868 write_identifier_index = compile_template(
869 """
870 write_identifier_index(self, out, index)
871 """,
872
873 '''
874 >>> #self.write_table_header(out, "index", "Identifier Index")
875 >>> self.write_identifier_index_header(out)
876 >>> letters = "abcdefghijklmnopqrstuvwxyz"
877 <a name="_"></a>
878 >>> for sortkey, name, doc in index:
879 >>> if self._doc_or_ancestor_is_private(doc):
880 >>> if not self._show_private: continue
881 <tr class="private"><td width="15%">
882 >>> else:
883 <tr><td width="15%">
884 >>> #endif
885 >>> while letters and letters[0] <= name[-1][:1].lower():
886 <a name="$letters[0]$"></a>
887 >>> letters = letters[1:]
888 >>> #endif
889 $self.href(doc, name[-1])$
890 </td>
891 <td>$self.doc_kind(doc)$
892 >>> container_name = name.container()
893 >>> if container_name is not None:
894 >>> container = self.docindex.get_valdoc(container_name)
895 >>> if container is not None:
896 in $self.doc_kind(container)$ $self.href(container)$
897 >>> #endif
898 >>> #endif
899 </td>
900 </tr>
901 >>> #endfor
902 </table>
903 >>> for letter in letters:
904 <a name="$letter$"></a>
905 >>> #endfor
906 <br />
907 ''')
908
909
910 write_term_index = compile_template(
911 """
912 write_term_index(self, out, index)
913 """,
914
915 '''
916 >>> if not index: return
917 >>> self.write_table_header(out, "index", "Term Index")
918 >>> for (key, term, links) in index:
919 <tr><td width="15%">$term.to_plaintext(None)$</td>
920 <td>
921 >>> for link in links[:-1]:
922 <em>$self.href(link)$</em>,
923 >>> #endfor
924 <em>$self.href(links[-1])$</em>
925 </td>
926 </tr>
927 >>> #endfor
928 </table>
929 <br />
930 ''')
931
932
933
934
935
936
938 """
939 Write an HTML help file to the given stream. If
940 C{self._helpfile} contains a help file, then use it;
941 otherwise, use the default helpfile from
942 L{epydoc.docwriter.html_help}.
943
944 @param public: The output stream for the public version of the page.
945 @param private: The output stream for the private version of the page.
946 """
947
948
949
950 if self._helpfile:
951 if os.path.exists(self._helpfile):
952 try: help = open(self._helpfile).read()
953 except: raise IOError("Can't open help file: %r" %
954 self._helpfile)
955 else:
956 raise IOError("Can't find help file: %r" % self._helpfile)
957 else:
958 if self._prj_name: thisprj = self._prj_name
959 else: thisprj = 'this project'
960 help = HTML_HELP % {'this_project':thisprj}
961
962
963 self.write_header(out, 'Help')
964 self.write_navbar(out, 'help')
965 self.write_breadcrumbs(out, 'help', 'help.html')
966 out(help)
967 self.write_navbar(out, 'help')
968 self.write_footer(out)
969
970
971
972
973
974 write_frames_index = compile_template(
975 """
976 write_frames_index(self, out)
977
978 Write the frames index file for the frames-based table of
979 contents to the given streams.
980 """,
981
982 '''
983 <?xml version="1.0" encoding="iso-8859-1"?>
984 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
985 "DTD/xhtml1-frameset.dtd">
986 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
987 <head>
988 <title> $self._prj_name or "API Documentation"$ </title>
989 </head>
990 <frameset cols="20%,80%">
991 <frameset rows="30%,70%">
992 <frame src="toc.html" name="moduleListFrame"
993 id="moduleListFrame" />
994 <frame src="toc-everything.html" name="moduleFrame"
995 id="moduleFrame" />
996 </frameset>
997 <frame src="$self._top_page_url$" name="mainFrame" id="mainFrame" />
998 </frameset>
999 </html>
1000 ''')
1001
1002
1003 write_toc = compile_template(
1004 """
1005 write_toc(self, out)
1006 """,
1007
1008 '''
1009 >>> self.write_header(out, "Table of Contents")
1010 <h1 class="tocheading">Table of Contents</h1>
1011 <hr />
1012 <p class="toc">
1013 <a target="moduleFrame" href="toc-everything.html">Everything</a>
1014 </p>
1015 >>> self.write_toc_section(out, "Modules", self.module_list)
1016 <hr />
1017 >>> if self._show_private:
1018 $self.PRIVATE_LINK$
1019 >>> #endif
1020 >>> self.write_footer(out, short=True)
1021 ''')
1022
1023
1025 if not docs: return
1026
1027
1028 if fullname:
1029 docs = [(str(d.canonical_name), d) for d in docs]
1030 else:
1031 docs = [(str(d.canonical_name[-1]), d) for d in docs]
1032 docs.sort()
1033
1034 out(' <h2 class="tocheading">%s</h2>\n' % name)
1035 for label, doc in docs:
1036 doc_url = self.url(doc)
1037 toc_url = 'toc-%s' % doc_url
1038 is_private = self._doc_or_ancestor_is_private(doc)
1039 if is_private:
1040 if not self._show_private: continue
1041 out(' <div class="private">\n')
1042
1043 out(' <p class="toc">\n')
1044 if isinstance(doc, ModuleDoc):
1045 out(' <a target="moduleFrame" href="%s"\n'
1046 ' onclick="setFrame(\'%s\',\'%s\');"'
1047 ' >%s</a></p>' % (toc_url, toc_url, doc_url, label))
1048 else:
1049 out(' <a target="mainFrame" href="%s"\n'
1050 ' >%s</a></p>' % (doc_url, label))
1051 if is_private:
1052 out(' </div>\n')
1053
1055 self.write_header(out, "Everything")
1056 out('<h1 class="tocheading">Everything</h1>\n')
1057 out('<hr />\n')
1058
1059
1060 self.write_toc_section(out, "All Classes", self.class_list)
1061
1062
1063 funcs = [d for d in self.routine_list
1064 if not isinstance(self.docindex.container(d),
1065 (ClassDoc, types.NoneType))]
1066 self.write_toc_section(out, "All Functions", funcs)
1067
1068
1069 vars = []
1070 for doc in self.module_list:
1071 vars += doc.select_variables(value_type='other',
1072 imported=False,
1073 public=self._public_filter)
1074 self.write_toc_section(out, "All Variables", vars)
1075
1076
1077 out('<hr />\n')
1078 if self._show_private:
1079 out(self.PRIVATE_LINK+'\n')
1080 self.write_footer(out, short=True)
1081
1083 """
1084 Write an HTML page containing the table of contents page for
1085 the given module to the given streams. This page lists the
1086 modules, classes, exceptions, functions, and variables defined
1087 by the module.
1088 @param public: The output stream for the public version of the page.
1089 @param private: The output stream for the private version of the page.
1090 """
1091 name = doc.canonical_name[-1]
1092 self.write_header(out, name)
1093 out('<h1 class="tocheading">Module %s</h1>\n' % name)
1094 out('<hr />\n')
1095
1096
1097
1098 classes = doc.select_variables(value_type='class', imported=False,
1099 public=self._public_filter)
1100 self.write_toc_section(out, "Classes", classes, fullname=False)
1101
1102
1103 funcs = doc.select_variables(value_type='function', imported=False,
1104 public=self._public_filter)
1105 self.write_toc_section(out, "Functions", funcs, fullname=False)
1106
1107
1108 variables = doc.select_variables(value_type='other', imported=False,
1109 public=self._public_filter)
1110 self.write_toc_section(out, "Variables", variables, fullname=False)
1111
1112
1113 out('<hr />\n')
1114 if self._show_private:
1115 out(self.PRIVATE_LINK+'\n')
1116 self.write_footer(out, short=True)
1117
1118
1119
1120
1121
1122 - def write_homepage(self, directory):
1123 """
1124 Write an C{index.html} file in the given directory. The
1125 contents of this file are copied or linked from an existing
1126 page, so this method must be called after all pages have been
1127 written. The page used is determined by L{_frames_index} and
1128 L{_top_page}:
1129 - If L{_frames_index} is true, then C{frames.html} is
1130 copied.
1131 - Otherwise, the page specified by L{_top_page} is
1132 copied.
1133 """
1134 filename = os.path.join(directory, 'index.html')
1135 if self._frames_index: top = 'frames.html'
1136 else: top = self._top_page_url
1137
1138
1139 if top[:5] != 'http:' and '/' not in top:
1140 try:
1141
1142 topfile = os.path.join(directory, top)
1143 s = open(topfile, 'r').read()
1144
1145
1146 open(filename, 'w').write(s)
1147 return
1148 except:
1149 log.error('Warning: error copying index; '
1150 'using a redirect page')
1151
1152
1153 name = self._prj_name or 'this project'
1154 f = open(filename, 'w')
1155 self.write_redirect_index(f.write, top, name)
1156 f.close()
1157
1158 write_redirect_index = compile_template(
1159 """
1160 write_redirect_index(self, out, top, name)
1161 """,
1162
1163 '''
1164 <?xml version="1.0" encoding="iso-8859-1"?>
1165 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
1166 "DTD/xhtml1-strict.dtd">
1167 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
1168 <head>
1169 <title> Redirect </title>
1170 <meta http-equiv="refresh" content="1;url=$top$" />
1171 <link rel="stylesheet" href="epydoc.css" type="text/css"></link>
1172 </head>
1173 <body>
1174 <p>Redirecting to the API documentation for
1175 <a href="$top$">$self._prj_name or "this project"$</a>...</p>
1176 </body>
1177 </html>
1178 ''')
1179
1180
1181
1182
1183
1184
1186 """
1187 Write the CSS stylesheet in the given directory. If
1188 C{cssname} contains a stylesheet file or name (from
1189 L{epydoc.docwriter.html_css}), then use that stylesheet;
1190 otherwise, use the default stylesheet.
1191
1192 @rtype: C{None}
1193 """
1194 filename = os.path.join(directory, 'epydoc.css')
1195
1196
1197 if cssname is None:
1198 css = STYLESHEETS['default'][0]
1199 else:
1200 if os.path.exists(cssname):
1201 try: css = open(cssname).read()
1202 except: raise IOError("Can't open CSS file: %r" % cssname)
1203 elif STYLESHEETS.has_key(cssname):
1204 css = STYLESHEETS[cssname][0]
1205 else:
1206 raise IOError("Can't find CSS file: %r" % cssname)
1207
1208
1209 cssfile = open(filename, 'w')
1210 cssfile.write(css)
1211 cssfile.close()
1212
1213
1214
1215
1216
1226
1227
1228
1229
1230
1231 TOGGLE_PRIVATE_JS = '''
1232 function toggle_private() {
1233 // Search for any private/public links on this page. Store
1234 // their old text in "cmd," so we will know what action to
1235 // take; and change their text to the opposite action.
1236 var cmd = "?";
1237 var elts = document.getElementsByTagName("a");
1238 for(var i=0; i<elts.length; i++) {
1239 if (elts[i].className == "privatelink") {
1240 cmd = elts[i].innerHTML;
1241 elts[i].innerHTML = ((cmd=="show private")?"hide private":
1242 "show private");
1243 }
1244 }
1245 // Update all DIVs containing private objects.
1246 var elts = document.getElementsByTagName("div");
1247 for(var i=0; i<elts.length; i++) {
1248 if (elts[i].className == "private") {
1249 elts[i].style.display = ((cmd=="hide private")?"none":"block");
1250 }
1251 }
1252 // Update all table rowss containing private objects. Note, we
1253 // use "" instead of "block" becaue IE & firefox disagree on what
1254 // this should be (block vs table-row), and "" just gives the
1255 // default for both browsers.
1256 var elts = document.getElementsByTagName("tr");
1257 for(var i=0; i<elts.length; i++) {
1258 if (elts[i].className == "private") {
1259 elts[i].style.display = ((cmd=="hide private")?"none":"");
1260 }
1261 }
1262 // Update all list items containing private objects.
1263 var elts = document.getElementsByTagName("li");
1264 for(var i=0; i<elts.length; i++) {
1265 if (elts[i].className == "private") {
1266 elts[i].style.display = ((cmd=="hide private")?"none":"list-item");
1267 }
1268 }
1269 // Update all list items containing private objects.
1270 var elts = document.getElementsByTagName("ul");
1271 for(var i=0; i<elts.length; i++) {
1272 if (elts[i].className == "private") {
1273 elts[i].style.display = ((cmd=="hide private")?"none":"block");
1274 }
1275 }
1276 // Set a cookie to remember the current option.
1277 document.cookie = "EpydocPrivate="+cmd;
1278 }
1279 '''.strip()
1280
1281
1282
1283
1284 GET_COOKIE_JS = '''
1285 function getCookie(name) {
1286 var dc = document.cookie;
1287 var prefix = name + "=";
1288 var begin = dc.indexOf("; " + prefix);
1289 if (begin == -1) {
1290 begin = dc.indexOf(prefix);
1291 if (begin != 0) return null;
1292 } else
1293 { begin += 2; }
1294 var end = document.cookie.indexOf(";", begin);
1295 if (end == -1)
1296 { end = dc.length; }
1297 return unescape(dc.substring(begin + prefix.length, end));
1298 }
1299 '''.strip()
1300
1301
1302
1303
1304
1305 SET_FRAME_JS = '''
1306 function setFrame(url1, url2) {
1307 parent.frames[1].location.href = url1;
1308 parent.frames[2].location.href = url2;
1309 }
1310 '''.strip()
1311
1312
1313
1314
1315 HIDE_PRIVATE_JS = '''
1316 function checkCookie() {
1317 var cmd=getCookie("EpydocPrivate");
1318 if (cmd!="show private" && location.href.indexOf("#_") < 0)
1319 toggle_private();
1320 }
1321 '''.strip()
1322
1323 TOGGLE_CALLGRAPH_JS = '''
1324 function toggleCallGraph(id) {
1325 var elt = document.getElementById(id);
1326 if (elt.style.display == "none")
1327 elt.style.display = "block";
1328 else
1329 elt.style.display = "none";
1330 }
1331 '''.strip()
1332
1333
1334
1335
1336
1337
1339 if graph is None: return ''
1340 graph.caption = graph.title = None
1341 image_url = '%s.gif' % graph.uid
1342 image_file = os.path.join(self._directory, image_url)
1343 return graph.to_html(image_file, image_url)
1344
1346 graph_html = self.render_graph(callgraph, css='graph-with-title')
1347 if graph_html == '': return ''
1348 return ('<div style="display:none" id="%s-div"><center>\n'
1349 '<table border="0" cellpadding="0" cellspacing="0">\n'
1350 ' <tr><td>%s</td></tr>\n'
1351 ' <tr><th>Call Graph</th></tr>\n'
1352 '</table><br />\n</center></div>\n' %
1353 (callgraph.uid, graph_html))
1354
1356 if callgraph is None: return ''
1357 return ('<br /><span class="codelink"><a href="javascript: void(0);" '
1358 'onclick="toggleCallGraph(\'%s-div\');return false;">'
1359 'call graph</a></span> ' % callgraph.uid)
1360
1361
1362
1363
1364
1365 write_header = compile_template(
1366 """
1367 write_header(self, out, title)
1368
1369 Generate HTML code for the standard page header, and write it
1370 to C{out}. C{title} is a string containing the page title.
1371 It should be appropriately escaped/encoded.
1372 """,
1373
1374 '''
1375 <?xml version="1.0" encoding="ascii"?>
1376 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
1377 "DTD/xhtml1-transitional.dtd">
1378 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
1379 <head>
1380 <title>$title$</title>
1381 <link rel="stylesheet" href="epydoc.css" type="text/css" />
1382 <script type="text/javascript" src="epydoc.js"></script>
1383 </head>
1384
1385 <body bgcolor="white" text="black" link="blue" vlink="#204080"
1386 alink="#204080">
1387 ''')
1388
1389
1390
1391
1392
1393
1394 write_footer = compile_template(
1395 """
1396 write_footer(self, out, short=False)
1397
1398 Generate HTML code for the standard page footer, and write it
1399 to C{out}.
1400 """,
1401
1402 '''
1403 >>> if not short:
1404 <table border="0" cellpadding="0" cellspacing="0" width="100%%">
1405 <tr>
1406 <td align="left" class="footer">Generated by Epydoc
1407 $epydoc.__version__$ on $time.asctime()$</td>
1408 <td align="right" class="footer">
1409 <a href="http://epydoc.sourceforge.net">http://epydoc.sf.net</a>
1410 </td>
1411 </tr>
1412 </table>
1413 >>> #endif
1414
1415 <script type="text/javascript">
1416 <!--
1417 // Private objects are initially displayed (because if
1418 // javascript is turned off then we want them to be
1419 // visible); but by default, we want to hide them. So hide
1420 // them unless we have a cookie that says to show them.
1421 checkCookie()
1422 // -->
1423 </script>
1424
1425 </body>
1426 </html>
1427 ''')
1428
1429
1430
1431
1432
1433
1434 write_navbar = compile_template(
1435 """
1436 write_navbar(self, out, context)
1437
1438 Generate HTML code for the navigation bar, and write it to
1439 C{out}. The navigation bar typically looks like::
1440
1441 [ Home Trees Index Help Project ]
1442
1443 @param context: A value indicating what page we're generating
1444 a navigation bar for. If we're generating an API
1445 documentation page for an object, then C{context} is a
1446 L{ValueDoc} containing the documentation for that object;
1447 otherwise, C{context} is a string name for the page. The
1448 following string names are recognized: C{'tree'}, C{'index'},
1449 and C{'help'}.
1450 """,
1451
1452 '''
1453 <!-- ==================== NAVIGATION BAR ==================== -->
1454 <table class="navbar" border="0" width="100%" cellpadding="0"
1455 bgcolor="#a0c0ff" cellspacing="0">
1456 <tr valign="middle">
1457 >>> if self._top_page_url not in ("trees.html", "indices.html", "help.html"):
1458 <!-- Home link -->
1459 >>> if (isinstance(context, ValueDoc) and
1460 >>> self._top_page_url == self.url(context.canonical_name)):
1461 <th bgcolor="#70b0f0" class="navselect"
1462 > Home </th>
1463 >>> else:
1464 <th class="navbar"> <a class="navbar"
1465 href="$self._top_page_url$">Home</a> </th>
1466 >>> #endif
1467
1468 <!-- Tree link -->
1469 >>> if context == "trees":
1470 <th bgcolor="#70b0f0" class="navselect"
1471 > Trees </th>
1472 >>> else:
1473 <th class="navbar"> <a class="navbar"
1474 href="trees.html">Trees</a> </th>
1475 >>> #endif
1476
1477 <!-- Index link -->
1478 >>> if context == "indices":
1479 <th bgcolor="#70b0f0" class="navselect"
1480 > Index </th>
1481 >>> else:
1482 <th class="navbar"> <a class="navbar"
1483 href="indices.html">Index</a> </th>
1484 >>> #endif
1485
1486 <!-- Help link -->
1487 >>> if context == "help":
1488 <th bgcolor="#70b0f0" class="navselect"
1489 > Help </th>
1490 >>> else:
1491 <th class="navbar"> <a class="navbar"
1492 href="help.html">Help</a> </th>
1493 >>> #endif
1494
1495 >>> if self._prj_link:
1496 <!-- Project homepage -->
1497 <th class="navbar" align="right" width="100%">
1498 <table border="0" cellpadding="0" cellspacing="0">
1499 <tr><th class="navbar" align="center">
1500 <p class="nomargin">
1501 $self._prj_link$
1502 </p></th></tr></table></th>
1503 >>> else:
1504 <th class="navbar" width="100%"></th>
1505 >>> #endif
1506 </tr>
1507 </table>
1508 ''')
1509
1510
1511
1512
1513
1514
1515 write_breadcrumbs = compile_template(
1516 """
1517 write_breadcrumbs(self, out, context, context_url)
1518
1519 Generate HTML for the breadcrumbs line, and write it to
1520 C{out}. The breadcrumbs line is an invisible table with a
1521 list of pointers to the current object's ancestors on the
1522 left; and the show/hide private selector and the
1523 frames/noframes selector on the right.
1524
1525 @param context: The API documentation for the object whose
1526 breadcrumbs we should generate.
1527 @type context: L{ValueDoc}
1528 """,
1529
1530 '''
1531 <table width="100%" cellpadding="0" cellspacing="0">
1532 <tr valign="top">
1533 >>> if isinstance(context, APIDoc):
1534 <td width="100%">
1535 <span class="breadcrumbs">
1536 >>> crumbs = self.breadcrumbs(context)
1537 >>> for crumb in crumbs[:-1]:
1538 $crumb$ ::
1539 >>> #endfor
1540 $crumbs[-1]$
1541 </span>
1542 </td>
1543 >>> else:
1544 <td width="100%"> </td>
1545 >>> #endif
1546 <td>
1547 <table cellpadding="0" cellspacing="0">
1548 <!-- hide/show private -->
1549 >>> if self._show_private:
1550 <tr><td align="right">$self.PRIVATE_LINK$</td></tr>
1551 >>> #endif
1552 <tr><td align="right"><span class="options"
1553 >[<a href="frames.html" target="_top">frames</a
1554 >] | <a href="$context_url$"
1555 target="_top">no frames</a>]</span></td></tr>
1556 </table>
1557 </td>
1558 </tr>
1559 </table>
1560 ''')
1561
1562
1582
1588
1589
1590
1591
1592
1594 """
1595 Generate HTML code for a summary table, and write it to
1596 C{out}. A summary table is a table that includes a one-row
1597 description for each variable (of a given type) in a module
1598 or class.
1599
1600 @param heading: The heading for the summary table; typically,
1601 this indicates what kind of value the table describes
1602 (e.g., functions or classes).
1603 @param doc: A L{ValueDoc} object containing the API
1604 documentation for the module or class whose variables
1605 we should summarize.
1606 @param value_type: A string indicating what type of value
1607 should be listed in this summary table. This value
1608 is passed on to C{doc}'s C{select_variables()} method.
1609 """
1610
1611
1612
1613
1614 grouped_inh_vars = {}
1615
1616
1617 groups = [(plaintext_to_html(group_name),
1618 doc.select_variables(group=group_name, imported=False,
1619 value_type=value_type,
1620 public=self._public_filter))
1621 for group_name in doc.group_names()]
1622
1623
1624 groups = [(g,vars) for (g,vars) in groups if vars]
1625 if not groups: return
1626
1627
1628 self.write_table_header(out, "summary", heading)
1629
1630
1631 for name, var_docs in groups:
1632 self.write_summary_group(out, doc, name,
1633 var_docs, grouped_inh_vars)
1634
1635
1636
1637 if grouped_inh_vars:
1638 for base in doc.mro():
1639 if base in grouped_inh_vars:
1640 hdr = 'Inherited from %s' % self.href(base, context=doc)
1641 tr_class = ''
1642 if len([v for v in grouped_inh_vars[base]
1643 if v.is_public]) == 0:
1644 tr_class = ' class="private"'
1645 self.write_group_header(out, hdr, tr_class)
1646 for var_doc in grouped_inh_vars[base]:
1647 self.write_summary_line(out, var_doc, doc)
1648
1649
1650 out(self.TABLE_FOOTER)
1651 out('\n<br />\n')
1652
1654
1655
1656
1657
1658
1659 listed_inh_vars = {}
1660 normal_vars = []
1661 for var_doc in var_docs:
1662 if var_doc.container != doc:
1663 base = var_doc.container
1664 if (base not in self.class_set or
1665 self._inheritance == 'listed'):
1666 listed_inh_vars.setdefault(base,[]).append(var_doc)
1667 elif self._inheritance == 'grouped':
1668 grouped_inh_vars.setdefault(base,[]).append(var_doc)
1669 else:
1670 normal_vars.append(var_doc)
1671 else:
1672 normal_vars.append(var_doc)
1673
1674
1675 if name != '':
1676 tr_class = ''
1677 if len([v for v in var_docs if v.is_public]) == 0:
1678 tr_class = ' class="private"'
1679 self.write_group_header(out, name, tr_class)
1680
1681
1682 for var_doc in normal_vars:
1683 self.write_summary_line(out, var_doc, doc)
1684
1685 if listed_inh_vars:
1686 self.write_inheritance_list(out, doc, listed_inh_vars)
1687
1689 out(' <tr>\n <td colspan="2">\n')
1690 for base in doc.mro():
1691 if base not in listed_inh_vars: continue
1692 public_vars = [v for v in listed_inh_vars[base]
1693 if v.is_public]
1694 private_vars = [v for v in listed_inh_vars[base]
1695 if not v.is_public]
1696 if public_vars:
1697 out(' <p class="varlist">'
1698 '<span class="varlist-header">Inherited '
1699 'from <code>%s</code></span>:\n' %
1700 self.href(base, context=doc))
1701 self.write_var_list(out, public_vars)
1702 out(' </p>\n')
1703 if private_vars and self._show_private:
1704 out(' <div class="private">')
1705 out(' <p class="varlist">'
1706 '<span class="varlist-header">Inherited '
1707 'from <code>%s</code></span> (private):\n' %
1708 self.href(base, context=doc))
1709 self.write_var_list(out, private_vars)
1710 out(' </p></div>\n')
1711 out(' </td>\n </tr>\n')
1712
1714 out(' ')
1715 out(',\n '.join(['<code>%s</code>' % self.href(v,v.name)
1716 for v in vardocs])+'\n')
1717
1719 """
1720 Generate HTML code for a single line of a summary table, and
1721 write it to C{out}. See L{write_summary_table} for more
1722 information.
1723
1724 @param var_doc: The API documentation for the variable that
1725 should be described by this line of the summary table.
1726 @param container: The API documentation for the class or
1727 module whose summary table we're writing.
1728 """
1729
1730 if var_doc.is_public: tr_class = ''
1731 else: tr_class = ' class="private"'
1732
1733
1734 summary = self.summary(var_doc, indent=6)
1735
1736
1737 if var_doc.container != container and self._inheritance=="included":
1738 summary += ("\n <em>(Inherited from " +
1739 self.href(var_doc.container) + ")</em>")
1740
1741 if isinstance(var_doc.value, RoutineDoc):
1742 if summary: summary = '<br />'+summary
1743 self.write_function_summary_line(out, var_doc, tr_class, summary)
1744 else:
1745
1746 self.write_variable_summary_line(out, var_doc, tr_class, summary)
1747
1748 write_function_summary_line = compile_template(
1749 """
1750 write_function_summary_line(self, out, var_doc, tr_class, summary)
1751
1752 Generate HTML code for a single line of a summary table,
1753 describing a variable whose value is a function, and write
1754 it to C{out}.
1755
1756 @param var_doc: The API documentation for the variable that
1757 should be described by this line of the summary table.
1758 @param container: The API documentation for the class or
1759 module whose summary table we're writing.
1760 """,
1761
1762 '''
1763 <tr$tr_class$>
1764 <td width="15%" align="right" valign="top" class="rtype">
1765 $self.rtype(var_doc, indent=6) or " "$
1766 </td>
1767 <td>
1768 $self.function_signature(var_doc, link_name=True)$
1769 $summary$
1770 </td>
1771 </tr>
1772 ''')
1773
1774
1775 write_variable_summary_line = compile_template(
1776 '''
1777 write_variable_summary_line(self, out, var_doc, tr_class, summary)
1778 ''',
1779
1780 '''
1781 <tr$tr_class$>
1782 <td width="15%">
1783 <strong>$self.href(var_doc)$</strong></td>
1784 <td>$summary or " "$</td>
1785 </tr>
1786 ''')
1787
1788
1789
1790
1791
1792
1794
1795 if isinstance(doc, ClassDoc):
1796 var_docs = doc.select_variables(value_type=value_type,
1797 imported=False, inherited=False,
1798 public=self._public_filter)
1799 else:
1800 var_docs = doc.select_variables(value_type=value_type,
1801 imported=False,
1802 public=self._public_filter)
1803 if not var_docs: return
1804
1805
1806 self.write_table_header(out, "details", heading)
1807 out(self.TABLE_FOOTER)
1808
1809 for var_doc in var_docs:
1810 self.write_details_entry(out, var_doc)
1811
1812 out('<br />\n')
1813
1814 - def write_details_entry(self, out, var_doc):
1815 descr = self.descr(var_doc, indent=2)
1816 if var_doc.is_public: div_class = ''
1817 else: div_class = ' class="private"'
1818
1819
1820 if isinstance(var_doc.value, RoutineDoc):
1821 rtype = self.rtype(var_doc, indent=10)
1822 rdescr = self.return_descr(var_doc, indent=10)
1823 arg_descrs = []
1824
1825
1826 for (arg_names, arg_descr) in var_doc.value.arg_descrs:
1827 lhs = ', '.join([self.arg_name_to_html(var_doc.value, n)
1828 for n in arg_names])
1829 rhs = self.docstring_to_html(arg_descr, var_doc.value, 10)
1830 arg_descrs.append( (lhs, rhs) )
1831
1832 if 'callgraph' in self._graph_types:
1833 linker = _HTMLDocstringLinker(self, var_doc.value)
1834 callgraph = call_graph([var_doc.value], self.docindex,
1835 linker, var_doc, add_callers=True,
1836 add_callees=True)
1837 if callgraph is not None and len(callgraph.nodes) == 0:
1838 callgraph = None
1839 else:
1840 callgraph = None
1841 self.write_function_details_entry(out, var_doc, descr, callgraph,
1842 rtype, rdescr, arg_descrs,
1843 div_class)
1844
1845
1846 elif isinstance(var_doc.value, PropertyDoc):
1847 prop_doc = var_doc.value
1848 accessors = [(name, self.property_accessor_to_html(val_doc),
1849 self.summary(val_doc)) for (name, val_doc) in
1850 [('Get', prop_doc.fget), ('Set', prop_doc.fset),
1851 ('Delete', prop_doc.fdel)]]
1852 self.write_property_details_entry(out, var_doc, descr,
1853 accessors, div_class)
1854
1855
1856 else:
1857 self.write_variable_details_entry(out, var_doc, descr, div_class)
1858
1860
1861
1862
1863 m = re.match(r'^<p( [^>]+)?>', rhs)
1864 if m:
1865 lhs = m.group() + lhs
1866 rhs = rhs[m.end():]
1867
1868 return '<li>%s - %s</li>' % (lhs, rhs)
1869
1888
1890 """
1891 A helper function used to format an argument name, for use in
1892 the argument description list under a routine's details entry.
1893 This just wraps strong & code tags around the arg name; and if
1894 the arg name is associated with a type, then adds it
1895 parenthetically after the name.
1896 """
1897 s = '<strong class="pname"><code>%s</code></strong>' % arg_name
1898 if arg_name in func_doc.arg_types:
1899 typ = func_doc.arg_types[arg_name]
1900 typ_html = self.docstring_to_html(typ, func_doc, 10)
1901 s += " (<code>%s</code>)" % typ_html
1902 return s
1903
1904 write_function_details_entry = compile_template(
1905 '''
1906 write_function_details_entry(self, out, var_doc, descr, callgraph, \
1907 rtype, rdescr, arg_descrs, div_class)
1908 ''',
1909
1910 '''
1911 >>> func_doc = var_doc.value
1912 <a name="$var_doc.name$"></a>
1913 <div$div_class$>
1914 <table width="100%" class="func-details" bgcolor="#e0e0e0"><tr><td>
1915 <table width="100%" cellpadding="0" cellspacing="0" border="0">
1916 <tr valign="top"><td>
1917 <h3>$self.function_signature(var_doc)$
1918 >>> if var_doc.name in self.SPECIAL_METHODS:
1919 <br /><em class="fname">($self.SPECIAL_METHODS[var_doc.name]$)</em>
1920 >>> #endif
1921 </h3>
1922 </td><td align="right" valign="top"
1923 >$self.pysrc_link(func_doc)$ </span
1924 >$self.callgraph_link(callgraph)$</td>
1925 </table>
1926 $self.render_callgraph(callgraph)$
1927 $descr$
1928 <dl><dt></dt><dd>
1929 >>> # === parameters ===
1930 >>> if arg_descrs:
1931 <dl><dt>Parameters:</dt></dl>
1932 <ul>
1933 >>> for lhs, rhs in arg_descrs:
1934 $self.labelled_list_item(lhs, rhs)$
1935 >>> #endfor
1936 </ul>
1937 >>> #endif
1938 >>> # === return type ===
1939 >>> if rdescr and rtype:
1940 <dl><dt>Returns: <code>$rtype$</code></dt>
1941 <dd>$rdescr$</dd></dl>
1942 >>> elif rdescr:
1943 <dl><dt>Returns:</dt>
1944 <dd>$rdescr$</dd></dl>
1945 >>> elif rtype:
1946 <dl><dt>Returns: <code>$rtype$</code></dt></dl>
1947 >>> #endif
1948 >>> # === exceptions ===
1949 >>> if func_doc.exception_descrs not in (None, UNKNOWN, (), []):
1950 <dl><dt>Raises:</dt></dl>
1951 <ul>
1952 >>> for name, descr in func_doc.exception_descrs:
1953 $self.labelled_list_item(
1954 "<code><strong class=\'fraise\'>" +
1955 self.href(name) + "</strong></code>",
1956 self.docstring_to_html(descr, func_doc, 8))$
1957 >>> #endfor
1958 </ul>
1959 >>> #endif
1960 >>> # === overrides ===
1961 >>> if var_doc.overrides not in (None, UNKNOWN):
1962 <dl><dt>Overrides:
1963 $self.href(var_doc.overrides.value, context=var_doc)$
1964 >>> if (func_doc.docstring in (None, UNKNOWN) and
1965 >>> var_doc.overrides.value.docstring not in (None, UNKNOWN)):
1966 <dd><em class="note">(inherited documentation)</em></dd>
1967 >>> #endif
1968 </dt></dl>
1969 >>> #endif
1970 >>> # === metadata ===
1971 >>> self.write_standard_fields(out, func_doc)
1972 </dd></dl>
1973 </td></tr></table>
1974 </div>
1975 ''')
1976
1977
1978
1979 SPECIAL_METHODS ={
1980 '__init__': 'Constructor',
1981 '__del__': 'Destructor',
1982 '__add__': 'Addition operator',
1983 '__sub__': 'Subtraction operator',
1984 '__and__': 'And operator',
1985 '__or__': 'Or operator',
1986 '__repr__': 'Representation operator',
1987 '__call__': 'Call operator',
1988 '__getattr__': 'Qualification operator',
1989 '__getitem__': 'Indexing operator',
1990 '__setitem__': 'Index assignment operator',
1991 '__delitem__': 'Index deletion operator',
1992 '__delslice__': 'Slice deletion operator',
1993 '__setslice__': 'Slice assignment operator',
1994 '__getslice__': 'Slicling operator',
1995 '__len__': 'Length operator',
1996 '__cmp__': 'Comparison operator',
1997 '__eq__': 'Equality operator',
1998 '__in__': 'Containership operator',
1999 '__gt__': 'Greater-than operator',
2000 '__lt__': 'Less-than operator',
2001 '__ge__': 'Greater-than-or-equals operator',
2002 '__le__': 'Less-than-or-equals operator',
2003 '__radd__': 'Right-side addition operator',
2004 '__hash__': 'Hashing function',
2005 '__contains__': 'In operator',
2006 '__nonzero__': 'Boolean test operator',
2007 '__str__': 'Informal representation operator',
2008 }
2009
2010 write_property_details_entry = compile_template(
2011 '''
2012 write_property_details_entry(self, out, var_doc, descr, \
2013 accessors, div_class)
2014 ''',
2015
2016 '''
2017 >>> prop_doc = var_doc.value
2018 <a name="$var_doc.name$"></a>
2019 <div$div_class$>
2020 <table width="100%" class="prop-details" bgcolor="#e0e0e0"><tr><td>
2021 <h3>$var_doc.name$</h3>
2022 $descr$
2023 <dl><dt></dt><dd>
2024 >>> if prop_doc.type_descr not in (None, UNKNOWN):
2025 <dl><dt>Type:</dt>
2026 <dd>$self.type_descr(var_doc, indent=6)$</dd></dl>
2027 >>> #endif
2028 >>> for (name, val, summary) in accessors:
2029 <dt>$name$ Method:</dt>
2030 <dd>$val$
2031 >>> if summary:
2032 - $summary$
2033 >>> #endif
2034 </dd>
2035 >>> #endfor
2036 </dd></dl>
2037 </td></tr></table>
2038 </div>
2039 ''')
2040
2041
2042 write_variable_details_entry = compile_template(
2043 '''
2044 write_variable_details_entry(self, out, var_doc, descr, div_class)
2045 ''',
2046
2047 '''
2048 <a name="$var_doc.name$"></a>
2049 <div$div_class$>
2050 <table width="100%" class="var-details" bgcolor="#e0e0e0"><tr><td>
2051 <h3>$var_doc.name$</h3>
2052 $descr$
2053 <dl><dt></dt><dd>
2054 >>> if var_doc.type_descr not in (None, UNKNOWN):
2055 <dl><dt>Type:</dt>
2056 <dd>$self.type_descr(var_doc, indent=6)$</dd></dl>
2057 >>> #endif
2058 >>> tooltip = self.variable_tooltip(var_doc)
2059 >>> if var_doc.value is not UNKNOWN:
2060 <dl$tooltip$><dt>Value:</dt>
2061 <dd><table><tr><td><pre class="variable">
2062 $self.pprint_value(var_doc.value)$
2063 </pre></td></tr></table></dd>
2064 </dl>
2065 >>> #endif
2066 </dd></dl>
2067 </td></tr></table>
2068 </div>
2069 ''')
2070
2071
2072 _variable_linelen = 70
2073 _variable_maxlines = 3
2074 _variable_tooltip_linelen = 70
2090
2091 - def pprint_value(self, val_doc, multiline=True, summary_linelen=0):
2101
2102 - def pprint_pyval(self, pyval, multiline=True, summary_linelen=0):
2103
2104
2105
2106
2107
2108 if (type(pyval) is types.IntType or type(pyval) is types.FloatType or
2109 type(pyval) is types.NoneType or type(pyval) is types.ComplexType):
2110
2111 vstr = repr(pyval)
2112 return vstr + ' ' * (self._variable_linelen-len(vstr))
2113
2114
2115
2116 elif type(pyval) is types.StringType:
2117 vstr = repr(pyval)
2118 if vstr.find(r'\n') >= 0 and multiline:
2119 body = vstr[1:-1].replace(r'\n', '\n')
2120 vstr = ('<span class="variable-quote">'+vstr[0]*3+'</span>'+
2121 plaintext_to_html(body) +
2122 '<span class="variable-quote">'+vstr[0]*3+'</span>')
2123
2124 else:
2125 vstr = ('<span class="variable-quote">'+vstr[0]+'</span>'+
2126 plaintext_to_html(vstr[1:-1])+
2127 '<span class="variable-quote">'+vstr[0]+'</span>')
2128
2129
2130
2131
2132 elif type(pyval) is types.TupleType or type(pyval) is types.ListType:
2133 try: vstr = repr(pyval)
2134 except: vstr = '...'
2135 if multiline and len(vstr) > self._variable_linelen:
2136 vstr = pprint.pformat(pyval[:self._variable_maxlines+1])
2137 vstr = plaintext_to_html(vstr)
2138 elif type(pyval) is type({}):
2139 try: vstr = repr(pyval)
2140 except: vstr = '...'
2141 if multiline and len(vstr) > self._variable_linelen:
2142 if len(pyval) < self._variable_maxlines+50:
2143 vstr = pprint.pformat(pyval)
2144 else:
2145 shortval = {}
2146 for (k,v) in pyval.items()[:self._variable_maxlines+1]:
2147 shortval[k]=v
2148 vstr = pprint.pformat(shortval)
2149 vstr = plaintext_to_html(vstr)
2150
2151
2152 elif type(pyval).__name__ == 'SRE_Pattern':
2153 try: vstr = colorize_re(pyval)
2154 except TypeError, sre_constants.error:
2155 try: vstr = plaintext_to_html(repr(pyval))
2156 except: vstr = '...'
2157
2158
2159 else:
2160 try: vstr = plaintext_to_html(repr(pyval))
2161 except: vstr = '...'
2162
2163
2164
2165 if not multiline:
2166 vstr = vstr.replace('\n', '')
2167
2168 vstr = vstr.replace(' ', ' ')
2169 vstr = vstr.replace('<span ', '<span ')
2170 vstr = self._linewrap_html(vstr, summary_linelen, 1)
2171 return '<code>%s</code>\n' % vstr
2172
2173
2174 return self._linewrap_html(vstr, self._variable_linelen,
2175 self._variable_maxlines)
2176
2178 """
2179 Add line-wrapping to the HTML string C{s}. Line length is
2180 determined by C{linelen}; and the maximum number of
2181 lines to display is determined by C{maxlines}. This
2182 function treats HTML entities (e.g., C{&}) as single
2183 characters; and ignores HTML tags (e.g., C{<p>}).
2184 """
2185 LINEWRAP_MARKER = r'<span class="variable-linewrap">\</span>'
2186 ELLIPSIS_MARKER = r'<span class="variable-ellipsis">...</span>'
2187
2188 open_elements = []
2189 lines = []
2190 start = end = cnum = 0
2191 while len(lines) <= maxlines and end < len(s):
2192
2193 if s[end] == '<':
2194 newend = s.find('>', end)
2195 tag = s[end+1:newend]
2196 if tag[-1]!="/":
2197
2198 tagname = tag.split(None,1)[0]
2199 if tagname[0] == "/":
2200 open_elements.pop()
2201 else:
2202 open_elements.append(tagname)
2203 end = newend
2204 cnum -= 1
2205
2206
2207 elif s[end] == '&':
2208 end = s.find(';', end)
2209
2210
2211 cnum += 1
2212 end += 1
2213
2214
2215 if s[end-1] == '\n':
2216 lines.append(s[start:end-1])
2217 cnum = 0
2218 start = end
2219
2220
2221 if cnum == linelen and end<len(s) and s[end] != '\n':
2222 if maxlines == 1:
2223 closing_tags = ""
2224 for tag in open_elements:
2225 closing_tags += "</%s>" % (tag,)
2226 return s[start:end]+closing_tags+ELLIPSIS_MARKER
2227 lines.append(s[start:end]+LINEWRAP_MARKER)
2228 cnum = 0
2229 start = end
2230
2231
2232 if end == len(s):
2233 lines.append(s[start:end])
2234
2235
2236 if len(lines) > maxlines:
2237 closing_tags = ""
2238 for tag in open_elements:
2239 closing_tags += "</%s>" % (tag,)
2240 lines[-1] = closing_tags+ELLIPSIS_MARKER
2241 cnum = 3
2242
2243
2244 lines[-1] += ' '*(linelen-cnum+1)
2245
2246 return ('\n').join(lines)
2247
2248
2249
2250
2251
2252 - def base_tree(self, doc, width=None, postfix='', context=None):
2253 """
2254 @return: The HTML code for a class's base tree. The tree is
2255 drawn 'upside-down' and right justified, to allow for
2256 multiple inheritance.
2257 @rtype: C{string}
2258 """
2259 if context is None:
2260 context = doc
2261 if width == None: width = self.find_tree_width(doc, context)
2262 if isinstance(doc, ClassDoc) and doc.bases != UNKNOWN:
2263 bases = doc.bases
2264 else:
2265 bases = []
2266
2267 if postfix == '':
2268
2269 s = (' '*(width-2) + '<strong class="uidshort">'+
2270 self.contextual_label(doc, context)+'</strong>\n')
2271 else: s = ''
2272 for i in range(len(bases)-1, -1, -1):
2273 base = bases[i]
2274 label = self.contextual_label(base, context)
2275 s = (' '*(width-4-len(label)) + self.href(base, label)
2276 +' --+'+postfix+'\n' +
2277 ' '*(width-4) +
2278 ' |'+postfix+'\n' +
2279 s)
2280 if i != 0:
2281 s = (self.base_tree(base, width-4, ' |'+postfix)+s)
2282 else:
2283 s = (self.base_tree(base, width-4, ' '+postfix)+s)
2284 return s
2285
2287 """
2288 Helper function for L{base_tree}.
2289 @return: The width of a base tree, when drawn
2290 right-justified. This is used by L{base_tree} to
2291 determine how far to indent lines of the base tree.
2292 @rtype: C{int}
2293 """
2294 if not isinstance(doc, ClassDoc): return 2
2295 if doc.bases == UNKNOWN: return 2
2296 width = 2
2297 for base in doc.bases:
2298 width = max(width, len(self.contextual_label(base, context))+4,
2299 self.find_tree_width(base, context)+4)
2300 return width
2301
2302 - def contextual_label(self, doc, context):
2303 """
2304 Return the label for L{doc} to be shown in C{context}.
2305 """
2306 context = context.canonical_name
2307 if context is not UNKNOWN:
2308 context = context.container()
2309
2310 return str(doc.canonical_name.contextualize(context))
2311
2312
2313
2314
2315
2318
2319 if isinstance(api_doc, VariableDoc):
2320 func_doc = api_doc.value
2321
2322 if api_doc.value in (None, UNKNOWN):
2323 return (('<span class="%s"><span class="%s-name">%s'+
2324 '</span>(...)</span>') %
2325 (css_class, css_class, api_doc.name))
2326
2327 if link_name:
2328 name = self.href(api_doc, css_class=css_class+'-name')
2329 else:
2330 name = ('<span class="%s-name">%s</span>' %
2331 (css_class, api_doc.name))
2332 else:
2333 func_doc = api_doc
2334 name = self.href(api_doc, css_class=css_class+'-name')
2335
2336 if func_doc.posargs == UNKNOWN:
2337 args = ['...']
2338 else:
2339 args = [self.func_arg(n, d, css_class) for (n, d)
2340 in zip(func_doc.posargs, func_doc.posarg_defaults)]
2341 if func_doc.vararg:
2342 if func_doc.vararg == '...':
2343 args.append('<span class="%s-arg">...</span>' % css_class)
2344 else:
2345 args.append('<span class="%s-arg">*%s</span>' %
2346 (css_class, func_doc.vararg))
2347 if func_doc.kwarg:
2348 args.append('<span class="%s-arg">**%s</span>' %
2349 (css_class, func_doc.kwarg))
2350
2351 return ('<span class="%s">%s(%s)</span>' %
2352 (css_class, name, ',\n '.join(args)))
2353
2354
2355 - def func_arg(self, name, default, css_class):
2370
2372 if isinstance(arg, basestring):
2373 return arg
2374 elif len(arg) == 1:
2375 return '(%s,)' % self._arg_name(arg[0])
2376 else:
2377 return '(%s)' % (', '.join([self._arg_name(a) for a in arg]))
2378
2379
2380
2381
2382
2383
2384
2385
2387 assert isinstance(doc, NamespaceDoc)
2388 imports = doc.select_variables(imported=True,
2389 public=self._public_filter)
2390 if not imports: return
2391
2392 out('<p class="imports">')
2393 out('<span class="varlist-header">Imports:</span>\n ')
2394 out(',\n '.join([self._import(v, doc) for v in imports]))
2395 out('\n</p>\n')
2396
2397 - def _import(self, var_doc, context):
2405
2406
2407
2408
2409
2410
2411
2412
2413
2415 if not self.module_list: return
2416
2417
2418 out('<ul>\n')
2419 for doc in self.module_list:
2420 if (doc.package in (None, UNKNOWN) or
2421 doc.package not in self.module_set):
2422 self.write_module_tree_item(out, doc)
2423 out('</ul>\n')
2424
2439
2459
2460
2461
2462
2463
2464
2466 """
2467 Write HTML code for a nested list showing the base/subclass
2468 relationships between all documented classes. Each element of
2469 the top-level list is a class with no (documented) bases; and
2470 under each class is listed all of its subclasses. Note that
2471 in the case of multiple inheritance, a class may appear
2472 multiple times. This is used by L{write_trees} to write
2473 the class hierarchy.
2474
2475 @todo: For multiple inheritance, don't repeat subclasses the
2476 second time a class is mentioned; instead, link to the
2477 first mention.
2478 """
2479
2480 if not self.class_list: return
2481
2482
2483
2484
2485 class_set = self.class_set.copy()
2486 for doc in self.class_list:
2487 if doc.bases != UNKNOWN:
2488 for base in doc.bases:
2489 if base not in class_set:
2490 if isinstance(base, ClassDoc):
2491 class_set.update(base.mro())
2492 else:
2493
2494 pass
2495
2496
2497 out('<ul>\n')
2498 for doc in sorted(class_set):
2499 if doc.bases != UNKNOWN and len(doc.bases)==0:
2500 self.write_class_tree_item(out, doc, class_set)
2501 out('</ul>\n')
2502
2503 write_class_tree_item = compile_template(
2504 '''
2505 write_class_tree_item(self, out, doc, class_set)
2506 ''',
2507
2508 '''
2509 >>> if doc.summary in (None, UNKNOWN):
2510 <li> <strong class="uidlink">$self.href(doc)$</strong>
2511 >>> else:
2512 <li> <strong class="uidlink">$self.href(doc)$</strong>:
2513 <em class="summary">$self.description(doc.summary, doc, 8)$</em>
2514 >>> # endif
2515 >>> if doc.subclasses:
2516 <ul>
2517 >>> for subclass in set(doc.subclasses):
2518 >>> if subclass in class_set:
2519 >>> self.write_class_tree_item(out, subclass, class_set)
2520 >>> #endif
2521 >>> #endfor
2522 </ul>
2523 >>> #endif
2524 </li>
2525 ''')
2526
2527
2528
2529
2530
2531
2533 """
2534 Write HTML code containing descriptions of any standard markup
2535 fields that are defined by the given L{APIDoc} object (such as
2536 C{@author} and C{@todo} fields).
2537
2538 @param doc: The L{APIDoc} object containing the API documentation
2539 for the object whose standard markup fields should be
2540 described.
2541 """
2542 fields = []
2543 field_values = {}
2544
2545
2546
2547 for (field, arg, descr) in doc.metadata:
2548 if field not in field_values:
2549 fields.append(field)
2550 if field.takes_arg:
2551 subfields = field_values.setdefault(field,{})
2552 subfields.setdefault(arg,[]).append(descr)
2553 else:
2554 field_values.setdefault(field,[]).append(descr)
2555
2556 for field in fields:
2557 if field.takes_arg:
2558 for arg, descrs in field_values[field].items():
2559 self.write_standard_field(out, doc, field, descrs, arg)
2560
2561 else:
2562 self.write_standard_field(out, doc, field, field_values[field])
2563
2564 write_standard_field = compile_template(
2565 """
2566 write_standard_field(self, out, doc, field, descrs, arg='')
2567
2568 """,
2569
2570 """
2571 >>> if arg: arglabel = ' (%s)' % arg
2572 >>> else: arglabel = ''
2573 >>> if len(descrs) == 1:
2574 <p><strong>$field.singular+arglabel$:</strong>
2575 $self.description(descrs[0], doc, 8)$
2576 </p>
2577 >>> elif field.short:
2578 <dl><dt>$field.plural+arglabel$:</dt>
2579 <dd>
2580 >>> for descr in descrs[:-1]:
2581 $self.description(descr, doc, 10)$,
2582 >>> # end for
2583 $self.description(descrs[-1], doc, 10)$
2584 </dd>
2585 </dl>
2586 >>> else:
2587 <p><strong>$field.plural+arglabel$:</strong>
2588 <ul>
2589 >>> for descr in descrs:
2590 <li>
2591 $self.description(descr, doc, 8)$
2592 </li>
2593 >>> # end for
2594 </ul>
2595 >>> # end else
2596 >>> # end for
2597 """)
2598
2599
2600
2601
2602
2603
2605 """
2606 A helper function for L{_extract_term_index}.
2607
2608 For each index term M{t} with key M{k} in C{parsed_docstring},
2609 modify C{terms} and C{links} as follows:
2610 - Set C{terms[M{k}] = t} (if C{terms[M{k}]} doesn't exist).
2611 - Append C{link} to C{links[M{k}]}.
2612 """
2613 if parsed_docstring in (None, UNKNOWN): return
2614 for term in parsed_docstring.index_terms():
2615 key = self._term_index_to_anchor(term)
2616 if not terms.has_key(key):
2617 terms[key] = term
2618 links[key] = []
2619 links[key].append(link)
2620
2622 """
2623 Given the name of an inline index item, construct a URI anchor.
2624 These anchors are used to create links from the index page to each
2625 index item.
2626 """
2627
2628
2629 s = re.sub(r'\s\s+', '-', term.to_plaintext(None))
2630 return "index-"+re.sub("[^a-zA-Z0-9]", "_", s)
2631
2633 """
2634 Extract the set of terms that should be indexed from all
2635 documented docstrings. Return the extracted set as a
2636 list of tuples of the form C{(key, term, [links])}.
2637 This list is used by L{write_indices()} to construct the
2638 term index.
2639 @rtype: C{list} of C{(string, ParsedDocstring, list of ValueDoc)}
2640 """
2641 terms = {}
2642 links = {}
2643 for doc in self.valdocs:
2644 self._get_index_terms(doc.descr, doc, terms, links)
2645 if doc.metadata not in (None, UNKNOWN):
2646 for (field, arg, descr) in doc.metadata:
2647 self._get_index_terms(descr, doc, terms, links)
2648
2649 if isinstance(doc, NamespaceDoc):
2650 for var in doc.variables.values():
2651 self._get_index_terms(var.descr, var, terms, links)
2652 for (field, arg, descr) in var.metadata:
2653 self._get_index_terms(descr, var, terms, links)
2654 elif isinstance(doc, RoutineDoc):
2655 self._get_index_terms(doc.return_descr, doc, terms, links)
2656 self._get_index_terms(doc.return_type, doc, terms, links)
2657 if doc.arg_descrs not in (None, UNKNOWN):
2658 for arg, descr in doc.arg_descrs:
2659 self._get_index_terms(descr, doc, terms, links)
2660 if doc.arg_types not in (None, UNKNOWN):
2661 for arg, descr in doc.arg_types.items():
2662 self._get_index_terms(descr, doc, terms, links)
2663 if doc.exception_descrs not in (None, UNKNOWN):
2664 for excname, descr in doc.exception_descrs:
2665 self._get_index_terms(descr, doc, terms, links)
2666 elif isinstance(doc, PropertyDoc):
2667 self._get_index_terms(doc.type_descr, doc, terms, links)
2668
2669
2670 keys = terms.keys()
2671 keys.sort()
2672 return [(k, terms[k], links[k]) for k in keys]
2673
2674
2675
2676
2677
2678
2679
2680
2681 write_table_header = compile_template(
2682 '''
2683 write_table_header(self, out, css_class, heading=None, \
2684 private_link=True)
2685 ''',
2686
2687 '''
2688 >>> if heading is not None:
2689 >>> anchor = "section-%s" % re.sub("\W", "", heading)
2690 <!-- ==================== $heading.upper()$ ==================== -->
2691 <a name="$anchor$"></a>
2692 >>> #endif
2693 <table class="$css_class$" border="1" cellpadding="3"
2694 cellspacing="0" width="100%" bgcolor="white">
2695 >>> if heading is not None:
2696 <tr bgcolor="#70b0f0" class="$css_class$">
2697 >>> if private_link:
2698 <td colspan="2">
2699 <table border="0" cellpadding="0" cellspacing="0" width="100%">
2700 <tr valign="top">
2701 <th align="left" class="$css_class$">$heading$</th>
2702 <td align="right" valign="top"
2703 ><span class="options">[<a href="#$anchor$"
2704 class="privatelink" onclick="toggle_private();"
2705 >hide private</a>]</span></td>
2706 </tr>
2707 </table>
2708 </td>
2709 >>> else:
2710 <th align="left" colspan="2" class="$css_class$">$heading$</th>
2711 >>> #endif
2712 </tr>
2713 >>> #endif
2714 ''')
2715
2716
2717 TABLE_FOOTER = '</table>\n'
2718
2719 PRIVATE_LINK = '''
2720 <span class="options">[<a href="javascript: void(0);" class="privatelink"
2721 onclick="toggle_private();">hide private</a>]</span>
2722 '''.strip()
2723
2724 write_group_header = compile_template(
2725 '''
2726 write_group_header(self, out, group, tr_class='')
2727 ''',
2728
2729 '''
2730 <tr bgcolor="#e8f0f8" $tr_class$>
2731 <th colspan="2" class="group"
2732 > $group$</th></tr>
2733 ''')
2734
2735
2736 - def url(self, obj):
2737 """
2738 Return the URL for the given object, which can be a
2739 C{VariableDoc}, a C{ValueDoc}, or a C{DottedName}.
2740 """
2741
2742 if isinstance(obj, ModuleDoc):
2743 if obj not in self.module_set: return None
2744 return urllib.quote('%s'%obj.canonical_name) + '-module.html'
2745
2746 elif isinstance(obj, ClassDoc):
2747 if obj not in self.class_set: return None
2748 return urllib.quote('%s'%obj.canonical_name) + '-class.html'
2749
2750 elif isinstance(obj, VariableDoc):
2751 val_doc = obj.value
2752 if isinstance(val_doc, (ModuleDoc, ClassDoc)):
2753 return self.url(val_doc)
2754 elif obj.container in (None, UNKNOWN):
2755 if val_doc in (None, UNKNOWN): return None
2756 return self.url(val_doc)
2757 elif obj.is_imported == True:
2758 if obj.imported_from is not UNKNOWN:
2759 return self.url(obj.imported_from)
2760 else:
2761 return None
2762 else:
2763 container_url = self.url(obj.container)
2764 if container_url is None: return None
2765 return '%s#%s' % (container_url, urllib.quote('%s'%obj.name))
2766
2767 elif isinstance(obj, ValueDoc):
2768 container = self.docindex.container(obj)
2769 if container is None:
2770 return None
2771 else:
2772 container_url = self.url(container)
2773 if container_url is None: return None
2774 anchor = urllib.quote('%s'%obj.canonical_name[-1])
2775 return '%s#%s' % (container_url, anchor)
2776
2777 elif isinstance(obj, DottedName):
2778 val_doc = self.docindex.get_valdoc(obj)
2779 if val_doc is None: return None
2780 return self.url(val_doc)
2781
2782 elif obj == 'indices':
2783 return 'indices.html'
2784 elif obj == 'help':
2785 return 'help.html'
2786 elif obj == 'trees':
2787 return 'trees.html'
2788 else:
2789 raise ValueError, "Don't know what to do with %r" % obj
2790
2792 if not self._incl_sourcecode:
2793 return ''
2794 url = self.pysrc_url(api_doc)
2795 if url is not None:
2796 return ('<span class="codelink"><a href="%s">source '
2797 'code</a></span>' % url)
2798 else:
2799 return ''
2800
2802 if isinstance(api_doc, VariableDoc):
2803 if api_doc.value not in (None, UNKNOWN):
2804 return pysrc_link(api_doc.value)
2805 else:
2806 return None
2807 elif isinstance(api_doc, ModuleDoc):
2808 if api_doc in self.modules_with_sourcecode:
2809 return ('%s-pysrc.html' %
2810 urllib.quote('%s' % api_doc.canonical_name))
2811 else:
2812 return None
2813 else:
2814 module = api_doc.defining_module
2815 if module == UNKNOWN: return None
2816 module_pysrc_url = self.pysrc_url(module)
2817 if module_pysrc_url is None: return None
2818 module_name = module.canonical_name
2819 if not module_name.dominates(api_doc.canonical_name, True):
2820 log.debug('%r is in %r but name does not dominate' %
2821 (api_doc, module))
2822 return module_pysrc_url
2823 mname_len = len(module.canonical_name)
2824 anchor = '%s' % api_doc.canonical_name[mname_len:]
2825 return '%s#%s' % (module_pysrc_url, urllib.quote(anchor))
2826
2827
2828 return None
2829
2830
2831 - def href(self, target, label=None, css_class=None, context=None):
2832 """
2833 Return the HTML code for an HREF link to the given target
2834 (which can be a C{VariableDoc}, a C{ValueDoc}, or a
2835 C{DottedName}.
2836 If a C{NamespaceDoc} C{context} is specified, the target label is
2837 contextualized to it.
2838 """
2839 assert isinstance(target, (APIDoc, DottedName))
2840
2841
2842 if label is None:
2843 if isinstance(target, VariableDoc):
2844 label = target.name
2845 elif (isinstance(target, ValueDoc) and
2846 target.canonical_name is not UNKNOWN):
2847 label = target.canonical_name
2848 elif isinstance(target, DottedName):
2849 label = target
2850 else:
2851 raise ValueError("Unable to find a label for %r" % target)
2852
2853 if context is not None and isinstance(label, DottedName):
2854 label = label.contextualize(context.canonical_name.container())
2855
2856 label = plaintext_to_html(str(label))
2857
2858
2859 if label.startswith('script-'):
2860 label = label[7:] + ' (script)'
2861 if label.startswith('??'):
2862 label = '<i>unreachable</i>' + label[2:]
2863 label = re.sub(r'-\d+$', '', label)
2864
2865
2866 url = self.url(target)
2867 if url is None: return label
2868
2869
2870 if css_class is None:
2871 css = ''
2872 else:
2873 css = ' class="%s"' % css_class
2874
2875 return '<a href="%s"%s>%s</a>' % (url, css, label)
2876
2877 - def summary(self, api_doc, indent=0):
2885
2886 - def descr(self, api_doc, indent=0):
2894
2903
2904 - def rtype(self, api_doc, indent=0):
2911
2919
2921 if parsed_docstring in (None, UNKNOWN): return ''
2922 linker = _HTMLDocstringLinker(self, where)
2923 s = parsed_docstring.to_html(linker, indent=indent,
2924 directory=self._directory,
2925 docindex=self.docindex,
2926 context=where).strip()
2927 if self._mark_docstrings:
2928 s = '<span class="docstring">%s</span><!--end docstring-->' % s
2929 return s
2930
2931
2932 - def description(self, parsed_docstring, where=None, indent=0):
2933 assert isinstance(where, (APIDoc, type(None)))
2934 if parsed_docstring in (None, UNKNOWN): return ''
2935 linker = _HTMLDocstringLinker(self, where)
2936 descr = parsed_docstring.to_html(linker, indent=indent,
2937 directory=self._directory,
2938 docindex=self.docindex,
2939 context=where).strip()
2940 if descr == '': return ' '
2941 return descr
2942
2943
2945 if isinstance(doc, ModuleDoc) and doc.is_package == True:
2946 return 'Package'
2947 elif (isinstance(doc, ModuleDoc) and
2948 doc.canonical_name[0].startswith('script')):
2949 return 'Script'
2950 elif isinstance(doc, ModuleDoc):
2951 return 'Module'
2952 elif isinstance(doc, ClassDoc):
2953 return 'Class'
2954 elif isinstance(doc, ClassMethodDoc):
2955 return 'Class Method'
2956 elif isinstance(doc, StaticMethodDoc):
2957 return 'Static Method'
2958 elif isinstance(doc, RoutineDoc):
2959 if isinstance(self.docindex.container(doc), ClassDoc):
2960 return 'Method'
2961 else:
2962 return 'Function'
2963 else:
2964 return 'Variable'
2965
2973
2975 - def __init__(self, htmlwriter, container):
2976 self.htmlwriter = htmlwriter
2977 self.docindex = htmlwriter.docindex
2978 self.container = container
2979
2984
2986
2987 if label is None: label = plaintext_to_html(identifier)
2988
2989
2990 doc = self.docindex.find(identifier, self.container)
2991
2992
2993 if doc is None:
2994 return '<code class="link">%s</code>' % label
2995 else:
2996 return self.htmlwriter.href(doc, label, 'link')
2997
2998
3000 if isinstance(identifier, (basestring, DottedName)):
3001 doc = self.docindex.find(identifier, self.container)
3002 if doc:
3003 return self.htmlwriter.url(doc)
3004 else:
3005
3006
3007 failed_xrefs = self.htmlwriter._failed_xrefs
3008 context = self.container.canonical_name
3009 failed_xrefs.setdefault(identifier,{})[context] = 1
3010
3011 elif isinstance(identifier, APIDoc):
3012 return self.htmlwriter.url(identifier)
3013 doc = identifier
3014
3015 else:
3016 raise TypeError('Expected string or APIDoc')
3017