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 v:len(v.canonical_name))
514 top = root[0]
515 for doc in root[1:]:
516 if not top.canonical_name.dominates(doc.canonical_name):
517 return self._trees_url
518 else:
519 return self.url(top)
520
521
522
523
524
525 - def write(self, directory=None):
526 """
527 Write the documentation to the given directory.
528
529 @type directory: C{string}
530 @param directory: The directory to which output should be
531 written. If no directory is specified, output will be
532 written to the current directory. If the directory does
533 not exist, it will be created.
534 @rtype: C{None}
535 @raise OSError: If C{directory} cannot be created.
536 @raise OSError: If any file cannot be created or written to.
537 """
538
539 self._files_written = 0.
540
541
542 orig_valdoc_defaults = (ValueDoc.SUMMARY_REPR_LINELEN,
543 ValueDoc.REPR_LINELEN,
544 ValueDoc.REPR_MAXLINES)
545 ValueDoc.SUMMARY_REPR_LINELEN = self._variable_summary_linelen
546 ValueDoc.REPR_LINELEN = self._variable_linelen
547 ValueDoc.REPR_MAXLINES = self._variable_maxlines
548
549
550 from epydoc.markup.epytext import ParsedEpytextDocstring
551 orig_crarr_html = ParsedEpytextDocstring.SYMBOL_TO_HTML['crarr']
552 ParsedEpytextDocstring.SYMBOL_TO_HTML['crarr'] = (
553 r'<span class="variable-linewrap">'
554 r'<img src="crarr.png" alt="\" /></span>')
555
556
557 self._failed_xrefs = {}
558
559
560 if not directory: directory = os.curdir
561 self._mkdir(directory)
562 self._directory = directory
563
564
565 self._files_written += 1
566 log.progress(self._files_written/self._num_files, 'epydoc.css')
567 self.write_css(directory, self._css)
568
569
570 self._files_written += 1
571 log.progress(self._files_written/self._num_files, 'epydoc.js')
572 self.write_javascript(directory)
573
574
575 self.write_images(directory)
576
577
578 indices = {'ident': self.build_identifier_index(),
579 'term': self.build_term_index()}
580 for (name, label, label2) in self.METADATA_INDICES:
581 indices[name] = self.build_metadata_index(name)
582
583
584
585 ident_by_letter = self._group_by_letter(indices['ident'])
586 if not self._split_ident_index:
587 self._write(self.write_link_index, directory,
588 'identifier-index.html', indices,
589 'Identifier Index', 'identifier-index.html',
590 ident_by_letter)
591 else:
592
593 for letter in self.LETTERS:
594 filename = 'identifier-index-%s.html' % letter
595 self._write(self.write_link_index, directory, filename,
596 indices, 'Identifier Index', filename,
597 ident_by_letter, [letter],
598 'identifier-index-%s.html')
599
600 for letter in self.LETTERS:
601 if letter in ident_by_letter:
602 filename = 'identifier-index.html'
603 self._write(self.write_link_index, directory, filename,
604 indices, 'Identifier Index', filename,
605 ident_by_letter, [letter],
606 'identifier-index-%s.html')
607 break
608
609
610 if indices['term']:
611 term_by_letter = self._group_by_letter(indices['term'])
612 self._write(self.write_link_index, directory, 'term-index.html',
613 indices, 'Term Definition Index',
614 'term-index.html', term_by_letter)
615 else:
616 self._files_written += 1
617
618
619 for (name, label, label2) in self.METADATA_INDICES:
620 if indices[name]:
621 self._write(self.write_metadata_index, directory,
622 '%s-index.html' % name, indices, name,
623 label, label2)
624 else:
625 self._files_written += 1
626
627
628 if self.module_list:
629 self._write(self.write_module_tree, directory, 'module-tree.html')
630 else:
631 self._files_written += 1
632 if self.class_list:
633 self._write(self.write_class_tree, directory, 'class-tree.html')
634 else:
635 self._files_written += 1
636
637
638 self._write(self.write_help, directory,'help.html')
639
640
641 if self._frames_index:
642 self._write(self.write_frames_index, directory, 'frames.html')
643 self._write(self.write_toc, directory, 'toc.html')
644 self._write(self.write_project_toc, directory, 'toc-everything.html')
645 for doc in self.module_list:
646 filename = 'toc-%s' % urllib.unquote(self.url(doc))
647 self._write(self.write_module_toc, directory, filename, doc)
648
649
650 for doc in self.module_list:
651 filename = urllib.unquote(self.url(doc))
652 self._write(self.write_module, directory, filename, doc)
653 for doc in self.class_list:
654 filename = urllib.unquote(self.url(doc))
655 self._write(self.write_class, directory, filename, doc)
656
657
658 if self._incl_sourcecode:
659
660
661 name_to_docs = {}
662 for api_doc in self.indexed_docs:
663 if (api_doc.canonical_name is not None and
664 self.url(api_doc) is not None):
665 name = api_doc.canonical_name[-1]
666 name_to_docs.setdefault(name, []).append(api_doc)
667
668 for doc_list in name_to_docs.values():
669 doc_list.sort()
670
671 for doc in self.modules_with_sourcecode:
672 filename = urllib.unquote(self.pysrc_url(doc))
673 self._write(self.write_sourcecode, directory, filename, doc,
674 name_to_docs)
675
676
677 self._write(self.write_redirect_page, directory, 'redirect.html')
678
679
680 self._write(self.write_api_list, directory, 'api-objects.txt')
681
682
683
684 self._files_written += 1
685 log.progress(self._files_written/self._num_files, 'index.html')
686 self.write_homepage(directory)
687
688
689 for k in self._failed_xrefs.keys():
690 if hasattr(__builtin__, k):
691 del self._failed_xrefs[k]
692
693
694 if self._failed_xrefs:
695 estr = 'Failed identifier crossreference targets:\n'
696 failed_identifiers = self._failed_xrefs.keys()
697 failed_identifiers.sort()
698 for identifier in failed_identifiers:
699 names = self._failed_xrefs[identifier].keys()
700 names.sort()
701 estr += '- %s' % identifier
702 estr += '\n'
703 for name in names:
704 estr += ' (from %s)\n' % name
705 log.docstring_warning(estr)
706
707
708 if self._num_files != int(self._files_written):
709 log.debug("Expected to write %d files, but actually "
710 "wrote %d files" %
711 (self._num_files, int(self._files_written)))
712
713
714 (ValueDoc.SUMMARY_REPR_LINELEN, ValueDoc.REPR_LINELEN,
715 ValueDoc.REPR_MAXLINES) = orig_valdoc_defaults
716 ParsedEpytextDocstring.SYMBOL_TO_HTML['crarr'] = orig_crarr_html
717
718 - def _write(self, write_func, directory, filename, *args):
727
729 """
730 If the given directory does not exist, then attempt to create it.
731 @rtype: C{None}
732 """
733 if not os.path.isdir(directory):
734 if os.path.exists(directory):
735 raise OSError('%r is not a directory' % directory)
736 os.mkdir(directory)
737
738
739
740
741
743 """
744 Write an HTML page containing the API documentation for the
745 given module to C{out}.
746
747 @param doc: A L{ModuleDoc} containing the API documentation
748 for the module that should be described.
749 """
750 longname = doc.canonical_name
751 shortname = doc.canonical_name[-1]
752
753
754 self.write_header(out, str(longname))
755 self.write_navbar(out, doc)
756 self.write_breadcrumbs(out, doc, self.url(doc))
757
758
759 if doc.is_package is True: typ = 'Package'
760 else: typ = 'Module'
761 if longname[0].startswith('script-'):
762 shortname = str(longname)[7:]
763 typ = 'Script'
764 out('<!-- ==================== %s ' % typ.upper() +
765 'DESCRIPTION ==================== -->\n')
766 out('<h1 class="epydoc">%s %s</h1>' % (typ, shortname))
767 out('<p class="nomargin-top">%s</p>\n' % self.pysrc_link(doc))
768
769
770 if doc.descr not in (None, UNKNOWN):
771 out(self.descr(doc, 2)+'\n\n')
772
773
774 if doc.metadata is not UNKNOWN and doc.metadata:
775 out('<hr />\n')
776 self.write_standard_fields(out, doc)
777
778
779 if doc.is_package is True and self._show_submodule_list:
780 self.write_module_list(out, doc)
781
782
783
784 self.write_summary_table(out, "Classes", doc, "class")
785 self.write_summary_table(out, "Functions", doc, "function")
786 self.write_summary_table(out, "Variables", doc, "other")
787
788
789 if self._show_imports:
790 self.write_imports(out, doc)
791
792
793
794 self.write_details_list(out, "Function Details", doc, "function")
795 self.write_details_list(out, "Variables Details", doc, "other")
796
797
798 self.write_navbar(out, doc)
799 self.write_footer(out)
800
801
802
803
804
806
807
808 filename = doc.filename
809 name = str(doc.canonical_name)
810
811
812 self.write_header(out, name)
813 self.write_navbar(out, doc)
814 self.write_breadcrumbs(out, doc, self.pysrc_url(doc))
815
816
817 out('<h1 class="epydoc">Source Code for %s</h1>\n' %
818 self.href(doc, label='%s %s' % (self.doc_kind(doc), name)))
819 out('<pre class="py-src">\n')
820 out(PythonSourceColorizer(filename, name, self.docindex,
821 self.url, name_to_docs,
822 self._src_code_tab_width).colorize())
823 out('</pre>\n<br />\n')
824
825
826 self.write_navbar(out, doc)
827 self.write_footer(out)
828
829
830
831
832
833
834
835
837 """
838 Write an HTML page containing the API documentation for the
839 given class to C{out}.
840
841 @param doc: A L{ClassDoc} containing the API documentation
842 for the class that should be described.
843 """
844 longname = doc.canonical_name
845 shortname = doc.canonical_name[-1]
846
847
848 self.write_header(out, str(longname))
849 self.write_navbar(out, doc)
850 self.write_breadcrumbs(out, doc, self.url(doc))
851
852
853 if (doc.metaclass is not UNKNOWN and
854 doc.metaclass.canonical_name not in (None, UNKNOWN) and
855 doc.metaclass.canonical_name != 'type'):
856 typ = self.href(doc.metaclass, doc.metaclass.canonical_name[-1])
857 elif doc.is_type(): typ = 'Type'
858 elif doc.is_exception(): typ = 'Exception'
859 else: typ = 'Class'
860 out('<!-- ==================== %s ' % typ.upper() +
861 'DESCRIPTION ==================== -->\n')
862 out('<h1 class="epydoc">%s %s</h1>' % (typ, shortname))
863 out('<p class="nomargin-top">%s</p>\n' % self.pysrc_link(doc))
864
865 if ((doc.bases not in (UNKNOWN, None) and len(doc.bases) > 0) or
866 (doc.subclasses not in (UNKNOWN,None) and len(doc.subclasses)>0)):
867
868 if 'umlclasstree' in self._graph_types:
869 self.write_class_tree_graph(out, doc, uml_class_tree_graph)
870 elif 'classtree' in self._graph_types:
871 self.write_class_tree_graph(out, doc, class_tree_graph)
872
873
874 else:
875
876 if doc.bases not in (UNKNOWN, None) and len(doc.bases) > 0:
877 out('<pre class="base-tree">\n%s</pre>\n\n' %
878 self.base_tree(doc))
879
880
881 if (doc.subclasses not in (UNKNOWN, None) and
882 len(doc.subclasses) > 0):
883 out('<dl><dt>Known Subclasses:</dt>\n<dd>\n ')
884 out(' <ul class="subclass-list">\n')
885 for i, subclass in enumerate(doc.subclasses):
886 href = self.href(subclass, context=doc)
887 if self._val_is_public(subclass): css = ''
888 else: css = ' class="private"'
889 if i > 0: href = ', '+href
890 out('<li%s>%s</li>' % (css, href))
891 out(' </ul>\n')
892 out('</dd></dl>\n\n')
893
894 out('<hr />\n')
895
896
897 if doc.descr not in (None, UNKNOWN):
898 out(self.descr(doc, 2)+'\n\n')
899
900
901 if doc.metadata is not UNKNOWN and doc.metadata:
902 out('<hr />\n')
903 self.write_standard_fields(out, doc)
904
905
906
907 self.write_summary_table(out, "Nested Classes", doc, "class")
908 self.write_summary_table(out, "Instance Methods", doc,
909 "instancemethod")
910 self.write_summary_table(out, "Class Methods", doc, "classmethod")
911 self.write_summary_table(out, "Static Methods", doc, "staticmethod")
912 self.write_summary_table(out, "Class Variables", doc,
913 "classvariable")
914 self.write_summary_table(out, "Instance Variables", doc,
915 "instancevariable")
916 self.write_summary_table(out, "Properties", doc, "property")
917
918
919 if self._show_imports:
920 self.write_imports(out, doc)
921
922
923
924
925
926
927 self.write_details_list(out, "Method Details", doc, "method")
928 self.write_details_list(out, "Class Variable Details", doc,
929 "classvariable")
930 self.write_details_list(out, "Instance Variable Details", doc,
931 "instancevariable")
932 self.write_details_list(out, "Property Details", doc, "property")
933
934
935 self.write_navbar(out, doc)
936 self.write_footer(out)
937
939 """
940 Write HTML code for a class tree graph of C{doc} (a classdoc),
941 using C{graphmaker} to draw the actual graph. C{graphmaker}
942 should be L{class_tree_graph()}, or L{uml_class_tree_graph()},
943 or any other function with a compatible signature.
944
945 If the given class has any private sublcasses (including
946 recursive subclasses), then two graph images will be generated
947 -- one to display when private values are shown, and the other
948 to display when private values are hidden.
949 """
950 linker = _HTMLDocstringLinker(self, doc)
951 private_subcls = self._private_subclasses(doc)
952 if private_subcls:
953 out('<center>\n'
954 ' <div class="private">%s</div>\n'
955 ' <div class="public" style="display:none">%s</div>\n'
956 '</center>\n' %
957 (self.render_graph(graphmaker(doc, linker, doc)),
958 self.render_graph(graphmaker(doc, linker, doc,
959 exclude=private_subcls))))
960 else:
961 out('<center>\n%s\n</center>\n' %
962 self.render_graph(graphmaker(doc, linker, doc)))
963
964
965
966
967
984
986 """
987 Write HTML code for a nested list showing the base/subclass
988 relationships between all documented classes. Each element of
989 the top-level list is a class with no (documented) bases; and
990 under each class is listed all of its subclasses. Note that
991 in the case of multiple inheritance, a class may appear
992 multiple times.
993
994 @todo: For multiple inheritance, don't repeat subclasses the
995 second time a class is mentioned; instead, link to the
996 first mention.
997 """
998
999
1000 self.write_treepage_header(out, 'Class Hierarchy', 'class-tree.html')
1001 out('<h1 class="epydoc">Class Hierarchy</h1>\n')
1002
1003
1004
1005
1006 class_set = set()
1007 for doc in self.class_list:
1008 class_set.update([base for base in doc.mro()
1009 if isinstance(base, ClassDoc) and
1010 base.canonical_name != DottedName('object')])
1011
1012
1013 out('<ul class="nomargin-top">\n')
1014 for doc in sorted(class_set, key=lambda c:c.canonical_name[-1]):
1015
1016
1017
1018 for base in doc.mro()[1:]:
1019 if base in class_set: break
1020 else:
1021
1022 self.write_class_tree_item(out, doc, class_set)
1023 out('</ul>\n')
1024
1025
1026 self.write_navbar(out, 'trees')
1027 self.write_footer(out)
1028
1030
1031 self.write_header(out, title)
1032 self.write_navbar(out, 'trees')
1033 self.write_breadcrumbs(out, 'trees', url)
1034 if self.class_list and self.module_list:
1035 out('<center><b>\n')
1036 out(' [ <a href="module-tree.html">Module Hierarchy</a>\n')
1037 out(' | <a href="class-tree.html">Class Hierarchy</a> ]\n')
1038 out('</b></center><br />\n')
1039
1040
1041
1042
1043
1044
1045 SPLIT_IDENT_INDEX_SIZE = 3000
1046 """If the identifier index has more than this number of entries,
1047 then it will be split into separate pages, one for each
1048 alphabetical section."""
1049
1050 LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ_'
1051 """The alphabetical sections that are used for link index pages."""
1052
1053 - def write_link_index(self, out, indices, title, url, index_by_section,
1054 sections=LETTERS, section_url='#%s'):
1055
1056
1057 self.write_indexpage_header(out, indices, title, url)
1058
1059
1060 out('<table border="0" width="100%">\n'
1061 '<tr valign="bottom"><td>\n')
1062 out('<h1 class="epydoc">%s</h1>\n</td><td>\n[\n' % title)
1063 for sec in self.LETTERS:
1064 if sec in index_by_section:
1065 out(' <a href="%s">%s</a>\n' % (section_url % sec, sec))
1066 else:
1067 out(' %s\n' % sec)
1068 out(']\n')
1069 out('</td></table>\n')
1070
1071
1072 sections = [s for s in sections if s in index_by_section]
1073 if sections:
1074 out('<table border="0" width="100%">\n')
1075 for section in sorted(sections):
1076 out('<tr valign="top"><td valign="top" width="1%">')
1077 out('<h2 class="epydoc"><a name="%s">%s</a></h2></td>\n' %
1078 (section, section))
1079 out('<td valign="top">\n')
1080 self.write_index_section(out, index_by_section[section], True)
1081 out('</td></tr>\n')
1082 out('</table>\n<br />')
1083
1084
1085 out('<br />')
1086 self.write_navbar(out, 'indices')
1087 self.write_footer(out)
1088
1089
1136
1138 """
1139 A helper for the index page generation functions, which
1140 generates a header that can be used to navigate between the
1141 different indices.
1142 """
1143 self.write_header(out, title)
1144 self.write_navbar(out, 'indices')
1145 self.write_breadcrumbs(out, 'indices', url)
1146
1147 if (indices['term'] or
1148 [1 for (name,l,l2) in self.METADATA_INDICES if indices[name]]):
1149 out('<center><b>[\n')
1150 out(' <a href="identifier-index.html">Identifiers</a>\n')
1151 if indices['term']:
1152 out('| <a href="term-index.html">Term Definitions</a>\n')
1153 for (name, label, label2) in self.METADATA_INDICES:
1154 if indices[name]:
1155 out('| <a href="%s-index.html">%s</a>\n' %
1156 (name, label2))
1157 out(']</b></center><br />\n')
1158
1160 out('<table class="link-index" width="100%" border="1">\n')
1161 num_rows = (len(items)+2)/3
1162 for row in range(num_rows):
1163 out('<tr>\n')
1164 for col in range(3):
1165 out('<td width="33%" class="link-index">')
1166 i = col*num_rows+row
1167 if i < len(items):
1168 name, url, container = items[col*num_rows+row]
1169 out('<a href="%s">%s</a>' % (url, name))
1170 if container is not None:
1171 out('<br />\n')
1172 if isinstance(container, ModuleDoc):
1173 label = container.canonical_name
1174 else:
1175 label = container.canonical_name[-1]
1176 out('<span class="index-where">(in %s)'
1177 '</span>' % self.href(container, label))
1178 else:
1179 out(' ')
1180 out('</td>\n')
1181 out('</tr>\n')
1182 if add_blankline and num_rows == 1:
1183 blank_cell = '<td class="link-index"> </td>'
1184 out('<tr>'+3*blank_cell+'</tr>\n')
1185 out('</table>\n')
1186
1187
1188
1189
1190
1192 """
1193 Write an HTML help file to the given stream. If
1194 C{self._helpfile} contains a help file, then use it;
1195 otherwise, use the default helpfile from
1196 L{epydoc.docwriter.html_help}.
1197 """
1198
1199
1200
1201 if self._helpfile:
1202 if os.path.exists(self._helpfile):
1203 try: help = open(self._helpfile).read()
1204 except: raise IOError("Can't open help file: %r" %
1205 self._helpfile)
1206 else:
1207 raise IOError("Can't find help file: %r" % self._helpfile)
1208 else:
1209 if self._prj_name: thisprj = self._prj_name
1210 else: thisprj = 'this project'
1211 help = HTML_HELP % {'this_project':thisprj}
1212
1213
1214 self.write_header(out, 'Help')
1215 self.write_navbar(out, 'help')
1216 self.write_breadcrumbs(out, 'help', 'help.html')
1217 out(help)
1218 self.write_navbar(out, 'help')
1219 self.write_footer(out)
1220
1221
1222
1223
1224
1225 write_frames_index = compile_template(
1226 """
1227 write_frames_index(self, out)
1228
1229 Write the frames index file for the frames-based table of
1230 contents to the given streams.
1231 """,
1232
1233 '''
1234 <?xml version="1.0" encoding="iso-8859-1"?>
1235 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
1236 "DTD/xhtml1-frameset.dtd">
1237 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
1238 <head>
1239 <title> $self._prj_name or "API Documentation"$ </title>
1240 </head>
1241 <frameset cols="20%,80%">
1242 <frameset rows="30%,70%">
1243 <frame src="toc.html" name="moduleListFrame"
1244 id="moduleListFrame" />
1245 <frame src="toc-everything.html" name="moduleFrame"
1246 id="moduleFrame" />
1247 </frameset>
1248 <frame src="$self._top_page_url$" name="mainFrame" id="mainFrame" />
1249 </frameset>
1250 </html>
1251 ''')
1252
1253
1254 write_toc = compile_template(
1255 """
1256 write_toc(self, out)
1257 """,
1258
1259 '''
1260 >>> self.write_header(out, "Table of Contents")
1261 <h1 class="toc">Table of Contents</h1>
1262 <hr />
1263 <a target="moduleFrame" href="toc-everything.html">Everything</a>
1264 <br />
1265 >>> self.write_toc_section(out, "Modules", self.module_list)
1266 <hr />
1267 >>> if self._show_private:
1268 $self.PRIVATE_LINK$
1269 >>> #endif
1270 >>> self.write_footer(out, short=True)
1271 ''')
1272
1273
1275 if not docs: return
1276
1277
1278 if fullname:
1279 docs = [(str(d.canonical_name), d) for d in docs]
1280 else:
1281 docs = [(str(d.canonical_name[-1]), d) for d in docs]
1282 docs.sort()
1283
1284 out(' <h2 class="toc">%s</h2>\n' % name)
1285 for label, doc in docs:
1286 doc_url = self.url(doc)
1287 toc_url = 'toc-%s' % doc_url
1288 is_private = self._doc_or_ancestor_is_private(doc)
1289 if is_private:
1290 if not self._show_private: continue
1291 out(' <div class="private">\n')
1292
1293 if isinstance(doc, ModuleDoc):
1294 out(' <a target="moduleFrame" href="%s"\n'
1295 ' onclick="setFrame(\'%s\',\'%s\');"'
1296 ' >%s</a><br />' % (toc_url, toc_url, doc_url, label))
1297 else:
1298 out(' <a target="mainFrame" href="%s"\n'
1299 ' >%s</a><br />' % (doc_url, label))
1300 if is_private:
1301 out(' </div>\n')
1302
1304 self.write_header(out, "Everything")
1305 out('<h1 class="toc">Everything</h1>\n')
1306 out('<hr />\n')
1307
1308
1309 self.write_toc_section(out, "All Classes", self.class_list)
1310
1311
1312 funcs = [d for d in self.routine_list
1313 if not isinstance(self.docindex.container(d),
1314 (ClassDoc, types.NoneType))]
1315 self.write_toc_section(out, "All Functions", funcs)
1316
1317
1318 vars = []
1319 for doc in self.module_list:
1320 vars += doc.select_variables(value_type='other',
1321 imported=False,
1322 public=self._public_filter)
1323 self.write_toc_section(out, "All Variables", vars)
1324
1325
1326 out('<hr />\n')
1327 if self._show_private:
1328 out(self.PRIVATE_LINK+'\n')
1329 self.write_footer(out, short=True)
1330
1332 """
1333 Write an HTML page containing the table of contents page for
1334 the given module to the given streams. This page lists the
1335 modules, classes, exceptions, functions, and variables defined
1336 by the module.
1337 """
1338 name = doc.canonical_name[-1]
1339 self.write_header(out, name)
1340 out('<h1 class="toc">Module %s</h1>\n' % name)
1341 out('<hr />\n')
1342
1343
1344
1345 classes = doc.select_variables(value_type='class', imported=False,
1346 public=self._public_filter)
1347 self.write_toc_section(out, "Classes", classes, fullname=False)
1348
1349
1350 funcs = doc.select_variables(value_type='function', imported=False,
1351 public=self._public_filter)
1352 self.write_toc_section(out, "Functions", funcs, fullname=False)
1353
1354
1355 variables = doc.select_variables(value_type='other', imported=False,
1356 public=self._public_filter)
1357 self.write_toc_section(out, "Variables", variables, fullname=False)
1358
1359
1360 out('<hr />\n')
1361 if self._show_private:
1362 out(self.PRIVATE_LINK+'\n')
1363 self.write_footer(out, short=True)
1364
1365
1366
1367
1368
1369 - def write_homepage(self, directory):
1370 """
1371 Write an C{index.html} file in the given directory. The
1372 contents of this file are copied or linked from an existing
1373 page, so this method must be called after all pages have been
1374 written. The page used is determined by L{_frames_index} and
1375 L{_top_page}:
1376 - If L{_frames_index} is true, then C{frames.html} is
1377 copied.
1378 - Otherwise, the page specified by L{_top_page} is
1379 copied.
1380 """
1381 filename = os.path.join(directory, 'index.html')
1382 if self._frames_index: top = 'frames.html'
1383 else: top = self._top_page_url
1384
1385
1386 if top[:5] != 'http:' and '/' not in top:
1387 try:
1388
1389 topfile = os.path.join(directory, top)
1390 s = open(topfile, 'r').read()
1391
1392
1393 open(filename, 'w').write(s)
1394 return
1395 except:
1396 log.error('Warning: error copying index; '
1397 'using a redirect page')
1398
1399
1400 name = self._prj_name or 'this project'
1401 f = open(filename, 'w')
1402 self.write_redirect_index(f.write, top, name)
1403 f.close()
1404
1405 write_redirect_index = compile_template(
1406 """
1407 write_redirect_index(self, out, top, name)
1408 """,
1409
1410 '''
1411 <?xml version="1.0" encoding="iso-8859-1"?>
1412 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
1413 "DTD/xhtml1-strict.dtd">
1414 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
1415 <head>
1416 <title> Redirect </title>
1417 <meta http-equiv="refresh" content="1;url=$top$" />
1418 <link rel="stylesheet" href="epydoc.css" type="text/css"></link>
1419 </head>
1420 <body>
1421 <p>Redirecting to the API documentation for
1422 <a href="$top$">$self._prj_name or "this project"$</a>...</p>
1423 </body>
1424 </html>
1425 ''')
1426
1427
1428
1429
1430
1431
1433 """
1434 Write the CSS stylesheet in the given directory. If
1435 C{cssname} contains a stylesheet file or name (from
1436 L{epydoc.docwriter.html_css}), then use that stylesheet;
1437 otherwise, use the default stylesheet.
1438
1439 @rtype: C{None}
1440 """
1441 filename = os.path.join(directory, 'epydoc.css')
1442
1443
1444 if cssname is None:
1445 css = STYLESHEETS['default'][0]
1446 else:
1447 if os.path.exists(cssname):
1448 try: css = open(cssname).read()
1449 except: raise IOError("Can't open CSS file: %r" % cssname)
1450 elif cssname in STYLESHEETS:
1451 css = STYLESHEETS[cssname][0]
1452 else:
1453 raise IOError("Can't find CSS file: %r" % cssname)
1454
1455
1456 cssfile = open(filename, 'w')
1457 cssfile.write(css)
1458 cssfile.close()
1459
1460
1461
1462
1463
1465 jsfile = open(os.path.join(directory, 'epydoc.js'), 'w')
1466 print >> jsfile, self.TOGGLE_PRIVATE_JS
1467 print >> jsfile, self.SHOW_PRIVATE_JS
1468 print >> jsfile, self.GET_COOKIE_JS
1469 print >> jsfile, self.SET_FRAME_JS
1470 print >> jsfile, self.HIDE_PRIVATE_JS
1471 print >> jsfile, self.TOGGLE_CALLGRAPH_JS
1472 print >> jsfile, html_colorize.PYSRC_JAVASCRIPTS
1473 print >> jsfile, self.GET_ANCHOR_JS
1474 print >> jsfile, self.REDIRECT_URL_JS
1475 jsfile.close()
1476
1477
1478
1479
1480
1481 TOGGLE_PRIVATE_JS = '''
1482 function toggle_private() {
1483 // Search for any private/public links on this page. Store
1484 // their old text in "cmd," so we will know what action to
1485 // take; and change their text to the opposite action.
1486 var cmd = "?";
1487 var elts = document.getElementsByTagName("a");
1488 for(var i=0; i<elts.length; i++) {
1489 if (elts[i].className == "privatelink") {
1490 cmd = elts[i].innerHTML;
1491 elts[i].innerHTML = ((cmd && cmd.substr(0,4)=="show")?
1492 "hide private":"show private");
1493 }
1494 }
1495 // Update all DIVs containing private objects.
1496 var elts = document.getElementsByTagName("div");
1497 for(var i=0; i<elts.length; i++) {
1498 if (elts[i].className == "private") {
1499 elts[i].style.display = ((cmd && cmd.substr(0,4)=="hide")?"none":"block");
1500 }
1501 else if (elts[i].className == "public") {
1502 elts[i].style.display = ((cmd && cmd.substr(0,4)=="hide")?"block":"none");
1503 }
1504 }
1505 // Update all table rows containing private objects. Note, we
1506 // use "" instead of "block" becaue IE & firefox disagree on what
1507 // this should be (block vs table-row), and "" just gives the
1508 // default for both browsers.
1509 var elts = document.getElementsByTagName("tr");
1510 for(var i=0; i<elts.length; i++) {
1511 if (elts[i].className == "private") {
1512 elts[i].style.display = ((cmd && cmd.substr(0,4)=="hide")?"none":"");
1513 }
1514 }
1515 // Update all list items containing private objects.
1516 var elts = document.getElementsByTagName("li");
1517 for(var i=0; i<elts.length; i++) {
1518 if (elts[i].className == "private") {
1519 elts[i].style.display = ((cmd && cmd.substr(0,4)=="hide")?
1520 "none":"");
1521 }
1522 }
1523 // Update all list items containing private objects.
1524 var elts = document.getElementsByTagName("ul");
1525 for(var i=0; i<elts.length; i++) {
1526 if (elts[i].className == "private") {
1527 elts[i].style.display = ((cmd && cmd.substr(0,4)=="hide")?"none":"block");
1528 }
1529 }
1530 // Set a cookie to remember the current option.
1531 document.cookie = "EpydocPrivate="+cmd;
1532 }
1533 '''.strip()
1534
1535
1536
1537
1538 GET_COOKIE_JS = '''
1539 function getCookie(name) {
1540 var dc = document.cookie;
1541 var prefix = name + "=";
1542 var begin = dc.indexOf("; " + prefix);
1543 if (begin == -1) {
1544 begin = dc.indexOf(prefix);
1545 if (begin != 0) return null;
1546 } else
1547 { begin += 2; }
1548 var end = document.cookie.indexOf(";", begin);
1549 if (end == -1)
1550 { end = dc.length; }
1551 return unescape(dc.substring(begin + prefix.length, end));
1552 }
1553 '''.strip()
1554
1555
1556
1557
1558
1559 SET_FRAME_JS = '''
1560 function setFrame(url1, url2) {
1561 parent.frames[1].location.href = url1;
1562 parent.frames[2].location.href = url2;
1563 }
1564 '''.strip()
1565
1566
1567
1568
1569 HIDE_PRIVATE_JS = '''
1570 function checkCookie() {
1571 var cmd=getCookie("EpydocPrivate");
1572 if (cmd && cmd.substr(0,4)!="show" && location.href.indexOf("#_") < 0)
1573 toggle_private();
1574 }
1575 '''.strip()
1576
1577 TOGGLE_CALLGRAPH_JS = '''
1578 function toggleCallGraph(id) {
1579 var elt = document.getElementById(id);
1580 if (elt.style.display == "none")
1581 elt.style.display = "block";
1582 else
1583 elt.style.display = "none";
1584 }
1585 '''.strip()
1586
1587 SHOW_PRIVATE_JS = '''
1588 function show_private() {
1589 var elts = document.getElementsByTagName("a");
1590 for(var i=0; i<elts.length; i++) {
1591 if (elts[i].className == "privatelink") {
1592 cmd = elts[i].innerHTML;
1593 if (cmd && cmd.substr(0,4)=="show")
1594 toggle_private();
1595 }
1596 }
1597 }
1598 '''.strip()
1599
1600 GET_ANCHOR_JS = '''
1601 function get_anchor() {
1602 var href = location.href;
1603 var start = href.indexOf("#")+1;
1604 if ((start != 0) && (start != href.length))
1605 return href.substring(start, href.length);
1606 }
1607 '''.strip()
1608
1609
1610
1611
1612
1613
1614
1615 REDIRECT_URL_JS = '''
1616 function redirect_url(dottedName) {
1617 // Scan through each element of the "pages" list, and check
1618 // if "name" matches with any of them.
1619 for (var i=0; i<pages.length; i++) {
1620
1621 // Each page has the form "<pagename>-m" or "<pagename>-c";
1622 // extract the <pagename> portion & compare it to dottedName.
1623 var pagename = pages[i].substring(0, pages[i].length-2);
1624 if (pagename == dottedName.substring(0,pagename.length)) {
1625
1626 // We\'ve found a page that matches `dottedName`;
1627 // construct its URL, using leftover `dottedName`
1628 // content to form an anchor.
1629 var pagetype = pages[i].charAt(pages[i].length-1);
1630 var url = pagename + ((pagetype=="m")?"-module.html":
1631 "-class.html");
1632 if (dottedName.length > pagename.length)
1633 url += "#" + dottedName.substring(pagename.length+1,
1634 dottedName.length);
1635 return url;
1636 }
1637 }
1638 }
1639 '''.strip()
1640
1641
1642
1643
1644
1645
1650
1651 RE_CALLGRAPH_ID = re.compile(r"""["'](.+-div)['"]""")
1652
1654 """Render the HTML chunk of a callgraph.
1655
1656 If C{callgraph} is a string, use the L{_callgraph_cache} to return
1657 a pre-rendered HTML chunk. This mostly avoids to run C{dot} twice for
1658 the same callgraph. Else, run the graph and store its HTML output in
1659 the cache.
1660
1661 @param callgraph: The graph to render or its L{uid<DotGraph.uid>}.
1662 @type callgraph: L{DotGraph} or C{str}
1663 @param token: A string that can be used to make the C{<div>} id
1664 unambiguous, if the callgraph is used more than once in a page.
1665 @type token: C{str}
1666 @return: The HTML representation of the graph.
1667 @rtype: C{str}
1668 """
1669 if callgraph is None: return ""
1670
1671 if isinstance(callgraph, basestring):
1672 uid = callgraph
1673 graph_html = self._callgraph_cache.get(callgraph, "")
1674 elif callgraph.uid in self._callgraph_cache:
1675 uid = callgraph.uid
1676 graph_html = self._callgraph_cache.get(callgraph, "")
1677 else:
1678 uid = callgraph.uid
1679 graph_html = self.render_graph(callgraph)
1680 self._callgraph_cache[uid] = graph_html
1681
1682 if graph_html:
1683 return ('<div style="display:none" id="%s-div"><center>\n'
1684 '<table border="0" cellpadding="0" cellspacing="0">\n'
1685 ' <tr><td>%s</td></tr>\n'
1686 ' <tr><th>Call Graph</th></tr>\n'
1687 '</table><br />\n</center></div>\n' %
1688 (uid+token, graph_html))
1689 else:
1690 return ''
1691
1693 """Render the HTML chunk of a callgraph link.
1694
1695 The link can toggles the visibility of the callgraph rendered using
1696 L{render_callgraph} with matching parameters.
1697
1698 @param callgraph: The graph to render or its L{uid<DotGraph.uid>}.
1699 @type callgraph: L{DotGraph} or C{str}
1700 @param token: A string that can be used to make the C{<div>} id
1701 unambiguous, if the callgraph is used more than once in a page.
1702 @type token: C{str}
1703 @return: The HTML representation of the graph link.
1704 @rtype: C{str}
1705 """
1706
1707 if callgraph is None: return ''
1708
1709 if isinstance(callgraph, basestring):
1710 uid = callgraph
1711 else:
1712 uid = callgraph.uid
1713
1714 return ('<br /><span class="codelink"><a href="javascript:void(0);" '
1715 'onclick="toggleCallGraph(\'%s-div\');return false;">'
1716 'call graph</a></span> ' % (uid + token))
1717
1718
1719
1720
1721
1722 IMAGES = {'crarr.png':
1723 'iVBORw0KGgoAAAANSUhEUgAAABEAAAAKCAMAAABlokWQAAAALHRFWHRD'
1724 'cmVhdGlvbiBUaW1lAFR1\nZSAyMiBBdWcgMjAwNiAwMDo0MzoxMCAtMD'
1725 'UwMGAMEFgAAAAHdElNRQfWCBYFASkQ033WAAAACXBI\nWXMAAB7CAAAe'
1726 'wgFu0HU+AAAABGdBTUEAALGPC/xhBQAAAEVQTFRF////zcOw18/AgGY0'
1727 'c1cg4dvQ\ninJEYEAAYkME3NXI6eTcloFYe2Asr5+AbE4Uh29A9fPwqp'
1728 'l4ZEUI8O3onopk0Ma0lH5U1nfFdgAA\nAAF0Uk5TAEDm2GYAAABNSURB'
1729 'VHjaY2BAAbzsvDAmK5oIlxgfioiwCAe7KJKIgKAQOzsLLwTwA0VY\n+d'
1730 'iRAT8T0AxuIIMHqoaXCWIPGzsHJ6orGJiYWRjQASOcBQAocgMSPKMTIg'
1731 'AAAABJRU5ErkJggg==\n',
1732 }
1733
1739
1740
1741
1742
1743
1744 write_header = compile_template(
1745 """
1746 write_header(self, out, title)
1747
1748 Generate HTML code for the standard page header, and write it
1749 to C{out}. C{title} is a string containing the page title.
1750 It should be appropriately escaped/encoded.
1751 """,
1752
1753 '''
1754 <?xml version="1.0" encoding="ascii"?>
1755 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
1756 "DTD/xhtml1-transitional.dtd">
1757 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
1758 <head>
1759 <title>$title$</title>
1760 <link rel="stylesheet" href="epydoc.css" type="text/css" />
1761 <script type="text/javascript" src="epydoc.js"></script>
1762 </head>
1763
1764 <body bgcolor="white" text="black" link="blue" vlink="#204080"
1765 alink="#204080">
1766 ''')
1767
1768
1769
1770
1771
1772
1773 write_footer = compile_template(
1774 """
1775 write_footer(self, out, short=False)
1776
1777 Generate HTML code for the standard page footer, and write it
1778 to C{out}.
1779 """,
1780
1781 '''
1782 >>> if not short:
1783 <table border="0" cellpadding="0" cellspacing="0" width="100%%">
1784 <tr>
1785 <td align="left" class="footer">
1786 >>> if self._include_log:
1787 <a href="epydoc-log.html">Generated by Epydoc
1788 $epydoc.__version__$
1789 >>> if self._include_timestamp:
1790 on $time.asctime()$</a>
1791 >>> else:
1792 Generated by Epydoc $epydoc.__version__$
1793 >>> if self._include_timestamp:
1794 on $time.asctime()$
1795 >>> #endif
1796 </td>
1797 <td align="right" class="footer">
1798 <a target="mainFrame" href="http://epydoc.sourceforge.net"
1799 >http://epydoc.sourceforge.net</a>
1800 </td>
1801 </tr>
1802 </table>
1803 >>> #endif
1804
1805 <script type="text/javascript">
1806 <!--
1807 // Private objects are initially displayed (because if
1808 // javascript is turned off then we want them to be
1809 // visible); but by default, we want to hide them. So hide
1810 // them unless we have a cookie that says to show them.
1811 checkCookie();
1812 // -->
1813 </script>
1814 >>> if self._google_analytics:
1815 <!-- Begin google analytics tracking code -->
1816 <script type="text/javascript">
1817 var gaJsHost = (("https:" == document.location.protocol) ?
1818 "https://ssl." : "http://www.");
1819 document.write(unescape("%3Cscript src='" + gaJsHost +
1820 "google-analytics.com/ga.js' " +
1821 "type='text/javascript'%3E%3C/script%3E"));
1822 </script>
1823 <script type="text/javascript">
1824 var pageTracker = _gat._getTracker("$self._google_analytics$");
1825 pageTracker._initData();
1826 pageTracker._trackPageview();
1827 </script>
1828 <!-- End google analytics tracking code -->
1829 >>> #endif
1830 </body>
1831 </html>
1832 ''')
1833
1834
1835
1836
1837
1838
1839 write_navbar = compile_template(
1840 """
1841 write_navbar(self, out, context)
1842
1843 Generate HTML code for the navigation bar, and write it to
1844 C{out}. The navigation bar typically looks like::
1845
1846 [ Home Trees Index Help Project ]
1847
1848 @param context: A value indicating what page we're generating
1849 a navigation bar for. If we're generating an API
1850 documentation page for an object, then C{context} is a
1851 L{ValueDoc} containing the documentation for that object;
1852 otherwise, C{context} is a string name for the page. The
1853 following string names are recognized: C{'tree'}, C{'index'},
1854 and C{'help'}.
1855 """,
1856
1857 '''
1858 <!-- ==================== NAVIGATION BAR ==================== -->
1859 <table class="navbar" border="0" width="100%" cellpadding="0"
1860 bgcolor="#a0c0ff" cellspacing="0">
1861 <tr valign="middle">
1862 >>> if self._top_page_url not in (self._trees_url, "identifier-index.html", "help.html"):
1863 <!-- Home link -->
1864 >>> if (isinstance(context, ValueDoc) and
1865 >>> self._top_page_url == self.url(context.canonical_name)):
1866 <th bgcolor="#70b0f0" class="navbar-select"
1867 > Home </th>
1868 >>> else:
1869 <th> <a
1870 href="$self._top_page_url$">Home</a> </th>
1871 >>> #endif
1872
1873 <!-- Tree link -->
1874 >>> if context == "trees":
1875 <th bgcolor="#70b0f0" class="navbar-select"
1876 > Trees </th>
1877 >>> else:
1878 <th> <a
1879 href="$self._trees_url$">Trees</a> </th>
1880 >>> #endif
1881
1882 <!-- Index link -->
1883 >>> if context == "indices":
1884 <th bgcolor="#70b0f0" class="navbar-select"
1885 > Indices </th>
1886 >>> else:
1887 <th> <a
1888 href="identifier-index.html">Indices</a> </th>
1889 >>> #endif
1890
1891 <!-- Help link -->
1892 >>> if context == "help":
1893 <th bgcolor="#70b0f0" class="navbar-select"
1894 > Help </th>
1895 >>> else:
1896 <th> <a
1897 href="help.html">Help</a> </th>
1898 >>> #endif
1899
1900 >>> if self._prj_link:
1901 <!-- Project homepage -->
1902 <th class="navbar" align="right" width="100%">
1903 <table border="0" cellpadding="0" cellspacing="0">
1904 <tr><th class="navbar" align="center"
1905 >$self._prj_link.strip()$</th>
1906 </tr></table></th>
1907 >>> else:
1908 <th class="navbar" width="100%"></th>
1909 >>> #endif
1910 </tr>
1911 </table>
1912 ''')
1913
1914
1915
1916
1917
1918
1919 write_breadcrumbs = compile_template(
1920 """
1921 write_breadcrumbs(self, out, context, context_url)
1922
1923 Generate HTML for the breadcrumbs line, and write it to
1924 C{out}. The breadcrumbs line is an invisible table with a
1925 list of pointers to the current object's ancestors on the
1926 left; and the show/hide private selector and the
1927 frames/noframes selector on the right.
1928
1929 @param context: The API documentation for the object whose
1930 breadcrumbs we should generate.
1931 @type context: L{ValueDoc}
1932 """,
1933
1934 '''
1935 <table width="100%" cellpadding="0" cellspacing="0">
1936 <tr valign="top">
1937 >>> if isinstance(context, APIDoc):
1938 <td width="100%">
1939 <span class="breadcrumbs">
1940 >>> crumbs = self.breadcrumbs(context)
1941 >>> for crumb in crumbs[:-1]:
1942 $crumb$ ::
1943 >>> #endfor
1944 $crumbs[-1]$
1945 </span>
1946 </td>
1947 >>> else:
1948 <td width="100%"> </td>
1949 >>> #endif
1950 <td>
1951 <table cellpadding="0" cellspacing="0">
1952 <!-- hide/show private -->
1953 >>> if self._show_private:
1954 <tr><td align="right">$self.PRIVATE_LINK$</td></tr>
1955 >>> #endif
1956 >>> if self._frames_index:
1957 <tr><td align="right"><span class="options"
1958 >[<a href="frames.html" target="_top">frames</a
1959 >] | <a href="$context_url$"
1960 target="_top">no frames</a>]</span></td></tr>
1961 >>> #endif
1962 </table>
1963 </td>
1964 </tr>
1965 </table>
1966 ''')
1967
1968
1989
1995
1996
1997
1998
1999
2001 """
2002 Generate HTML code for a summary table, and write it to
2003 C{out}. A summary table is a table that includes a one-row
2004 description for each variable (of a given type) in a module
2005 or class.
2006
2007 @param heading: The heading for the summary table; typically,
2008 this indicates what kind of value the table describes
2009 (e.g., functions or classes).
2010 @param doc: A L{ValueDoc} object containing the API
2011 documentation for the module or class whose variables
2012 we should summarize.
2013 @param value_type: A string indicating what type of value
2014 should be listed in this summary table. This value
2015 is passed on to C{doc}'s C{select_variables()} method.
2016 """
2017
2018
2019
2020
2021 grouped_inh_vars = {}
2022
2023
2024 groups = [(plaintext_to_html(group_name),
2025 doc.select_variables(group=group_name, imported=False,
2026 value_type=value_type,
2027 public=self._public_filter))
2028 for group_name in doc.group_names()]
2029
2030
2031 groups = [(g,vars) for (g,vars) in groups if vars]
2032 if not groups: return
2033
2034
2035 self.write_table_header(out, "summary", heading)
2036
2037
2038 for name, var_docs in groups:
2039 self.write_summary_group(out, doc, name,
2040 var_docs, grouped_inh_vars)
2041
2042
2043
2044 if grouped_inh_vars:
2045 for base in doc.mro():
2046 if base in grouped_inh_vars:
2047 hdr = 'Inherited from %s' % self.href(base, context=doc)
2048 tr_class = ''
2049 if len([v for v in grouped_inh_vars[base]
2050 if v.is_public]) == 0:
2051 tr_class = ' class="private"'
2052 self.write_group_header(out, hdr, tr_class)
2053 for var_doc in grouped_inh_vars[base]:
2054 self.write_summary_line(out, var_doc, doc)
2055
2056
2057 out(self.TABLE_FOOTER)
2058
2060
2061
2062
2063
2064
2065 listed_inh_vars = {}
2066 normal_vars = []
2067 for var_doc in var_docs:
2068 if var_doc.container != doc:
2069 base = var_doc.container
2070 if not isinstance(base, ClassDoc):
2071
2072 log.warning("%s's container is not a class!" % var_doc)
2073 normal_vars.append(var_doc)
2074 elif (base not in self.class_set or
2075 self._inheritance == 'listed'):
2076 listed_inh_vars.setdefault(base,[]).append(var_doc)
2077 elif self._inheritance == 'grouped':
2078 grouped_inh_vars.setdefault(base,[]).append(var_doc)
2079 elif self._inheritance == 'hidden':
2080 pass
2081 else:
2082 normal_vars.append(var_doc)
2083 else:
2084 normal_vars.append(var_doc)
2085
2086
2087 if name != '':
2088 tr_class = ''
2089 if len([v for v in var_docs if v.is_public]) == 0:
2090 tr_class = ' class="private"'
2091 self.write_group_header(out, name, tr_class)
2092
2093
2094 for var_doc in normal_vars:
2095 self.write_summary_line(out, var_doc, doc)
2096
2097 if listed_inh_vars:
2098 self.write_inheritance_list(out, doc, listed_inh_vars)
2099
2101 out(' <tr>\n <td colspan="2" class="summary">\n')
2102 for base in doc.mro():
2103 if base not in listed_inh_vars: continue
2104 public_vars = [v for v in listed_inh_vars[base]
2105 if v.is_public]
2106 private_vars = [v for v in listed_inh_vars[base]
2107 if not v.is_public]
2108 if public_vars:
2109 out(' <p class="indent-wrapped-lines">'
2110 '<b>Inherited from <code>%s</code></b>:\n' %
2111 self.href(base, context=doc))
2112 self.write_var_list(out, public_vars)
2113 out(' </p>\n')
2114 if private_vars and self._show_private:
2115 out(' <div class="private">')
2116 out(' <p class="indent-wrapped-lines">'
2117 '<b>Inherited from <code>%s</code></b> (private):\n' %
2118 self.href(base, context=doc))
2119 self.write_var_list(out, private_vars)
2120 out(' </p></div>\n')
2121 out(' </td>\n </tr>\n')
2122
2124 out(' ')
2125 out(',\n '.join(['<code>%s</code>' % self.href(v,v.name)
2126 for v in vardocs])+'\n')
2127
2129 """
2130 Generate HTML code for a single line of a summary table, and
2131 write it to C{out}. See L{write_summary_table} for more
2132 information.
2133
2134 @param var_doc: The API documentation for the variable that
2135 should be described by this line of the summary table.
2136 @param container: The API documentation for the class or
2137 module whose summary table we're writing.
2138 """
2139 pysrc_link = None
2140 callgraph = None
2141
2142
2143 if var_doc.is_public: tr_class = ''
2144 else: tr_class = ' class="private"'
2145
2146
2147 link_name = self._redundant_details or var_doc.is_detailed()
2148 anchor = not link_name
2149
2150
2151
2152 if isinstance(var_doc.value, RoutineDoc):
2153 typ = self.return_type(var_doc, indent=6)
2154 description = self.function_signature(var_doc, is_summary=True,
2155 link_name=link_name, anchor=anchor)
2156 pysrc_link = self.pysrc_link(var_doc.value)
2157
2158
2159 if 'callgraph' in self._graph_types:
2160 linker = _HTMLDocstringLinker(self, var_doc.value)
2161 callgraph = call_graph([var_doc.value], self.docindex,
2162 linker, var_doc, add_callers=True,
2163 add_callees=True)
2164 if callgraph and callgraph.nodes:
2165 var_doc.value.callgraph_uid = callgraph.uid
2166 else:
2167 callgraph = None
2168 elif isinstance(var_doc.value, ClassDoc):
2169 typ = -1
2170 description = self.summary_name(var_doc,
2171 link_name=link_name, anchor=anchor)
2172 else:
2173 typ = self.type_descr(var_doc, indent=6)
2174 description = self.summary_name(var_doc,
2175 link_name=link_name, anchor=anchor)
2176 if isinstance(var_doc.value, GenericValueDoc):
2177
2178
2179 max_len=self._variable_summary_linelen-3-len(var_doc.name)
2180 val_repr = var_doc.value.summary_pyval_repr(max_len)
2181 tooltip = self.variable_tooltip(var_doc)
2182 description += (' = <code%s>%s</code>' %
2183 (tooltip, val_repr.to_html(None)))
2184
2185
2186 summary = self.summary(var_doc, indent=6)
2187 if summary: description += '<br />\n %s' % summary
2188
2189
2190 if var_doc.container != container and self._inheritance=="included":
2191 description += ("\n <em>(Inherited from " +
2192 self.href(var_doc.container) + ")</em>")
2193
2194
2195 self._write_summary_line(out, typ, description, tr_class, pysrc_link,
2196 callgraph)
2197
2198 _write_summary_line = compile_template(
2199 "_write_summary_line(self, out, typ, description, tr_class, "
2200 "pysrc_link, callgraph)",
2201
2202 '''
2203 <tr$tr_class$>
2204 >>> if typ == -1:
2205 <td class="summary" colspan="2">
2206 >>> else:
2207 <td width="15%" align="right" valign="top" class="summary">
2208 <span class="summary-type">$typ or " "$</span>
2209 </td><td class="summary">
2210 >>> if pysrc_link is not None or callgraph is not None:
2211 <table width="100%" cellpadding="0" cellspacing="0" border="0">
2212 <tr>
2213 <td>$description$</td>
2214 <td align="right" valign="top">
2215 $pysrc_link$
2216 $self.callgraph_link(callgraph, token='-summary')$
2217 </td>
2218 </tr>
2219 </table>
2220 $self.render_callgraph(callgraph, token='-summary')$
2221 >>> #endif
2222 >>> if pysrc_link is None and callgraph is None:
2223 $description$
2224 >>> #endif
2225 </td>
2226 </tr>
2227 ''')
2228
2229
2230
2231
2232
2233
2235
2236 if self._redundant_details:
2237 detailed = None
2238 else:
2239 detailed = True
2240 if isinstance(doc, ClassDoc):
2241 var_docs = doc.select_variables(value_type=value_type,
2242 imported=False, inherited=False,
2243 public=self._public_filter,
2244 detailed=detailed)
2245 else:
2246 var_docs = doc.select_variables(value_type=value_type,
2247 imported=False,
2248 public=self._public_filter,
2249 detailed=detailed)
2250 if not var_docs: return
2251
2252
2253 self.write_table_header(out, "details", heading)
2254 out(self.TABLE_FOOTER)
2255
2256 for var_doc in var_docs:
2257 self.write_details_entry(out, var_doc)
2258
2259 out('<br />\n')
2260
2261 - def write_details_entry(self, out, var_doc):
2262 descr = self.descr(var_doc, indent=2) or ''
2263 if var_doc.is_public: div_class = ''
2264 else: div_class = ' class="private"'
2265
2266
2267 if isinstance(var_doc.value, RoutineDoc):
2268 rtype = self.return_type(var_doc, indent=10)
2269 rdescr = self.return_descr(var_doc, indent=10)
2270 arg_descrs = []
2271 args = set()
2272
2273
2274 for (arg_names, arg_descr) in var_doc.value.arg_descrs:
2275 args.update(arg_names)
2276 lhs = ', '.join([self.arg_name_to_html(var_doc.value, n)
2277 for n in arg_names])
2278 rhs = self.docstring_to_html(arg_descr, var_doc.value, 10)
2279 arg_descrs.append( (lhs, rhs) )
2280
2281
2282 for arg in var_doc.value.arg_types:
2283 if arg not in args:
2284 argname = self.arg_name_to_html(var_doc.value, arg)
2285 arg_descrs.append( (argname,'') )
2286
2287 self.write_function_details_entry(out, var_doc, descr,
2288 var_doc.value.callgraph_uid,
2289 rtype, rdescr, arg_descrs,
2290 div_class)
2291
2292
2293 elif isinstance(var_doc.value, PropertyDoc):
2294 prop_doc = var_doc.value
2295 accessors = [ (name,
2296 self.property_accessor_to_html(val_doc, prop_doc),
2297 self.summary(val_doc))
2298 for (name, val_doc) in
2299 [('Get', prop_doc.fget), ('Set', prop_doc.fset),
2300 ('Delete', prop_doc.fdel)]
2301 if val_doc not in (None, UNKNOWN)
2302
2303 and val_doc.pyval is not None
2304
2305 and not val_doc.canonical_name[0].startswith('??')]
2306
2307 self.write_property_details_entry(out, var_doc, descr,
2308 accessors, div_class)
2309
2310
2311 else:
2312 self.write_variable_details_entry(out, var_doc, descr, div_class)
2313
2315
2316
2317
2318 m = re.match(r'^<p( [^>]+)?>', rhs)
2319 if m:
2320 lhs = m.group() + lhs
2321 rhs = rhs[m.end():]
2322
2323 if rhs:
2324 return '<li>%s - %s</li>' % (lhs, rhs)
2325 else:
2326 return '<li>%s</li>' % (lhs,)
2327
2329 if val_doc not in (None, UNKNOWN):
2330 if isinstance(val_doc, RoutineDoc):
2331 return self.function_signature(val_doc, is_summary=True,
2332 link_name=True, context=context)
2333 elif isinstance(val_doc, GenericValueDoc):
2334 return self.pprint_value(val_doc)
2335 else:
2336 return self.href(val_doc, context=context)
2337 else:
2338 return '??'
2339
2341 """
2342 A helper function used to format an argument name, for use in
2343 the argument description list under a routine's details entry.
2344 This just wraps strong & code tags around the arg name; and if
2345 the arg name is associated with a type, then adds it
2346 parenthetically after the name.
2347 """
2348 s = '<strong class="pname"><code>%s</code></strong>' % arg_name
2349 if arg_name in func_doc.arg_types:
2350 typ = func_doc.arg_types[arg_name]
2351 typ_html = self.docstring_to_html(typ, func_doc, 10)
2352 s += " (%s)" % typ_html
2353 return s
2354
2355 write_function_details_entry = compile_template(
2356 '''
2357 write_function_details_entry(self, out, var_doc, descr, callgraph, \
2358 rtype, rdescr, arg_descrs, div_class)
2359 ''',
2360
2361 '''
2362 >>> func_doc = var_doc.value
2363 <a name="$var_doc.name$"></a>
2364 <div$div_class$>
2365 >>> self.write_table_header(out, "details")
2366 <tr><td>
2367 <table width="100%" cellpadding="0" cellspacing="0" border="0">
2368 <tr valign="top"><td>
2369 <h3 class="epydoc">$self.function_signature(var_doc)$
2370 >>> if var_doc.name in self.SPECIAL_METHODS:
2371 <br /><em class="fname">($self.SPECIAL_METHODS[var_doc.name]$)</em>
2372 >>> #endif
2373 >>> if isinstance(func_doc, ClassMethodDoc):
2374 <br /><em class="fname">Class Method</em>
2375 >>> #endif
2376 >>> if isinstance(func_doc, StaticMethodDoc):
2377 <br /><em class="fname">Static Method</em>
2378 >>> #endif
2379 </h3>
2380 </td><td align="right" valign="top"
2381 >$self.pysrc_link(func_doc)$
2382 $self.callgraph_link(callgraph)$</td>
2383 </tr></table>
2384 $self.render_callgraph(callgraph)$
2385 $descr$
2386 <dl class="fields">
2387 >>> # === parameters ===
2388 >>> if arg_descrs:
2389 <dt>Parameters:</dt>
2390 <dd><ul class="nomargin-top">
2391 >>> for lhs, rhs in arg_descrs:
2392 $self.labelled_list_item(lhs, rhs)$
2393 >>> #endfor
2394 </ul></dd>
2395 >>> #endif
2396 >>> # === return type ===
2397 >>> if rdescr and rtype:
2398 <dt>Returns: $rtype$</dt>
2399 <dd>$rdescr$</dd>
2400 >>> elif rdescr:
2401 <dt>Returns:</dt>
2402 <dd>$rdescr$</dd>
2403 >>> elif rtype:
2404 <dt>Returns: $rtype$</dt>
2405 >>> #endif
2406 >>> # === decorators ===
2407 >>> if func_doc.decorators not in (None, UNKNOWN):
2408 >>> # (staticmethod & classmethod are already shown, above)
2409 >>> decos = filter(lambda deco:
2410 >>> not ((deco=="staticmethod" and
2411 >>> isinstance(func_doc, StaticMethodDoc)) or
2412 >>> (deco=="classmethod" and
2413 >>> isinstance(func_doc, ClassMethodDoc))),
2414 >>> func_doc.decorators)
2415 >>> else:
2416 >>> decos = None
2417 >>> #endif
2418 >>> if decos:
2419 <dt>Decorators:</dt>
2420 <dd><ul class="nomargin-top">
2421 >>> for deco in decos:
2422 <li><code>@$deco$</code></li>
2423 >>> #endfor
2424 </ul></dd>
2425 >>> #endif
2426 >>> # === exceptions ===
2427 >>> if func_doc.exception_descrs not in (None, UNKNOWN, (), []):
2428 <dt>Raises:</dt>
2429 <dd><ul class="nomargin-top">
2430 >>> for name, descr in func_doc.exception_descrs:
2431 >>> exc_name = self.docindex.find(name, func_doc)
2432 >>> if exc_name is not None:
2433 >>> name = self.href(exc_name, label=str(name))
2434 >>> #endif
2435 $self.labelled_list_item(
2436 "<code><strong class=\'fraise\'>" +
2437 str(name) + "</strong></code>",
2438 self.docstring_to_html(descr, func_doc, 8))$
2439 >>> #endfor
2440 </ul></dd>
2441 >>> #endif
2442 >>> # === overrides ===
2443 >>> if var_doc.overrides not in (None, UNKNOWN):
2444 <dt>Overrides:
2445 >>> # Avoid passing GenericValueDoc to href()
2446 >>> if isinstance(var_doc.overrides.value, RoutineDoc):
2447 $self.href(var_doc.overrides.value, context=var_doc)$
2448 >>> else:
2449 >>> # In this case, a less interesting label is generated.
2450 $self.href(var_doc.overrides, context=var_doc)$
2451 >>> #endif
2452 >>> if (func_doc.docstring in (None, UNKNOWN) and
2453 >>> var_doc.overrides.value.docstring not in (None, UNKNOWN)):
2454 <dd><em class="note">(inherited documentation)</em></dd>
2455 >>> #endif
2456 </dt>
2457 >>> #endif
2458 </dl>
2459 >>> # === metadata ===
2460 >>> self.write_standard_fields(out, func_doc)
2461 </td></tr></table>
2462 </div>
2463 ''')
2464
2465
2466
2467 SPECIAL_METHODS ={
2468 '__init__': 'Constructor',
2469 '__del__': 'Destructor',
2470 '__add__': 'Addition operator',
2471 '__sub__': 'Subtraction operator',
2472 '__and__': 'And operator',
2473 '__or__': 'Or operator',
2474 '__xor__': 'Exclusive-Or operator',
2475 '__repr__': 'Representation operator',
2476 '__call__': 'Call operator',
2477 '__getattr__': 'Qualification operator',
2478 '__getitem__': 'Indexing operator',
2479 '__setitem__': 'Index assignment operator',
2480 '__delitem__': 'Index deletion operator',
2481 '__delslice__': 'Slice deletion operator',
2482 '__setslice__': 'Slice assignment operator',
2483 '__getslice__': 'Slicling operator',
2484 '__len__': 'Length operator',
2485 '__cmp__': 'Comparison operator',
2486 '__eq__': 'Equality operator',
2487 '__in__': 'Containership operator',
2488 '__gt__': 'Greater-than operator',
2489 '__lt__': 'Less-than operator',
2490 '__ge__': 'Greater-than-or-equals operator',
2491 '__le__': 'Less-than-or-equals operator',
2492 '__radd__': 'Right-side addition operator',
2493 '__hash__': 'Hashing function',
2494 '__contains__': 'In operator',
2495 '__nonzero__': 'Boolean test operator',
2496 '__str__': 'Informal representation operator',
2497 }
2498
2499 write_property_details_entry = compile_template(
2500 '''
2501 write_property_details_entry(self, out, var_doc, descr, \
2502 accessors, div_class)
2503 ''',
2504
2505 '''
2506 >>> prop_doc = var_doc.value
2507 <a name="$var_doc.name$"></a>
2508 <div$div_class$>
2509 >>> self.write_table_header(out, "details")
2510 <tr><td>
2511 <h3 class="epydoc">$var_doc.name$</h3>
2512 $descr$
2513 <dl class="fields">
2514 >>> for (name, val, summary) in accessors:
2515 <dt>$name$ Method:</dt>
2516 <dd class="value">$val$
2517 >>> if summary:
2518 - $summary$
2519 >>> #endif
2520 </dd>
2521 >>> #endfor
2522 >>> if prop_doc.type_descr not in (None, UNKNOWN):
2523 <dt>Type:</dt>
2524 <dd>$self.type_descr(var_doc, indent=6)$</dd>
2525 >>> #endif
2526 </dl>
2527 >>> self.write_standard_fields(out, prop_doc)
2528 </td></tr></table>
2529 </div>
2530 ''')
2531
2532
2533 write_variable_details_entry = compile_template(
2534 '''
2535 write_variable_details_entry(self, out, var_doc, descr, div_class)
2536 ''',
2537
2538 '''
2539 <a name="$var_doc.name$"></a>
2540 <div$div_class$>
2541 >>> self.write_table_header(out, "details")
2542 <tr><td>
2543 <h3 class="epydoc">$var_doc.name$</h3>
2544 $descr$
2545 <dl class="fields">
2546 >>> if var_doc.type_descr not in (None, UNKNOWN):
2547 <dt>Type:</dt>
2548 <dd>$self.type_descr(var_doc, indent=6)$</dd>
2549 >>> #endif
2550 </dl>
2551 >>> self.write_standard_fields(out, var_doc)
2552 >>> if var_doc.value is not UNKNOWN:
2553 <dl class="fields">
2554 <dt>Value:</dt>
2555 <dd>$self.pprint_value(var_doc.value)$</dd>
2556 </dl>
2557 >>> #endif
2558 </td></tr></table>
2559 </div>
2560 ''')
2561
2562
2570
2572 if val_doc is UNKNOWN:
2573 return '??'
2574 elif isinstance(val_doc, GenericValueDoc):
2575 return ('<table><tr><td><pre class="variable">\n' +
2576 val_doc.pyval_repr().to_html(None) +
2577 '\n</pre></td></tr></table>\n')
2578 else:
2579 return self.href(val_doc)
2580
2581
2582
2583
2584
2585 - def base_tree(self, doc, width=None, postfix='', context=None):
2586 """
2587 @return: The HTML code for a class's base tree. The tree is
2588 drawn 'upside-down' and right justified, to allow for
2589 multiple inheritance.
2590 @rtype: C{string}
2591 """
2592 if context is None:
2593 context = doc.defining_module
2594 if width == None: width = self.find_tree_width(doc, context)
2595 if isinstance(doc, ClassDoc) and doc.bases != UNKNOWN:
2596 bases = doc.bases
2597 else:
2598 bases = []
2599
2600 if postfix == '':
2601
2602 s = (' '*(width-2) + '<strong class="uidshort">'+
2603 self.contextual_label(doc, context)+'</strong>\n')
2604 else: s = ''
2605 for i in range(len(bases)-1, -1, -1):
2606 base = bases[i]
2607 label = self.contextual_label(base, context)
2608 s = (' '*(width-4-len(label)) + self.href(base, label)
2609 +' --+'+postfix+'\n' +
2610 ' '*(width-4) +
2611 ' |'+postfix+'\n' +
2612 s)
2613 if i != 0:
2614 s = (self.base_tree(base, width-4, ' |'+postfix, context)+s)
2615 else:
2616 s = (self.base_tree(base, width-4, ' '+postfix, context)+s)
2617 return s
2618
2620 """
2621 Helper function for L{base_tree}.
2622 @return: The width of a base tree, when drawn
2623 right-justified. This is used by L{base_tree} to
2624 determine how far to indent lines of the base tree.
2625 @rtype: C{int}
2626 """
2627 if not isinstance(doc, ClassDoc): return 2
2628 if doc.bases == UNKNOWN: return 2
2629 width = 2
2630 for base in doc.bases:
2631 width = max(width, len(self.contextual_label(base, context))+4,
2632 self.find_tree_width(base, context)+4)
2633 return width
2634
2635 - def contextual_label(self, doc, context):
2636 """
2637 Return the label for C{doc} to be shown in C{context}.
2638 """
2639 if doc.canonical_name is None:
2640 if doc.parse_repr is not None:
2641 return doc.parse_repr
2642 else:
2643 return '??'
2644 else:
2645 if context is UNKNOWN:
2646 return str(doc.canonical_name)
2647 else:
2648 context_name = context.canonical_name
2649 return str(doc.canonical_name.contextualize(context_name))
2650
2651
2652
2653
2654
2655 - def function_signature(self, api_doc, is_summary=False,
2656 link_name=False, anchor=False, context=None):
2657 """Render a function signature in HTML.
2658
2659 @param api_doc: The object whose name is to be rendered. If a
2660 C{VariableDoc}, its C{value} should be a C{RoutineDoc}
2661 @type api_doc: L{VariableDoc} or L{RoutineDoc}
2662 @param is_summary: True if the fuction is to be rendered in the summary.
2663 type css_class: C{bool}
2664 @param link_name: If True, the name is a link to the object anchor.
2665 @type link_name: C{bool}
2666 @param anchor: If True, the name is the object anchor.
2667 @type anchor: C{bool}
2668 @param context: If set, represent the function name from this context.
2669 Only useful when C{api_doc} is a L{RoutineDoc}.
2670 @type context: L{DottedName}
2671
2672 @return: The HTML code for the object.
2673 @rtype: C{str}
2674 """
2675 if is_summary: css_class = 'summary-sig'
2676 else: css_class = 'sig'
2677
2678
2679 if isinstance(api_doc, VariableDoc):
2680 func_doc = api_doc.value
2681
2682 if api_doc.value in (None, UNKNOWN):
2683 return (('<span class="%s"><span class="%s-name">%s'+
2684 '</span>(...)</span>') %
2685 (css_class, css_class, api_doc.name))
2686
2687 name = self.summary_name(api_doc, css_class=css_class+'-name',
2688 link_name=link_name, anchor=anchor)
2689 else:
2690 func_doc = api_doc
2691 name = self.href(api_doc, css_class=css_class+'-name',
2692 context=context)
2693
2694 if func_doc.posargs == UNKNOWN:
2695 args = ['...']
2696 else:
2697 args = [self.func_arg(n, d, css_class) for (n, d)
2698 in zip(func_doc.posargs, func_doc.posarg_defaults)]
2699 if func_doc.vararg not in (None, UNKNOWN):
2700 if func_doc.vararg == '...':
2701 args.append('<span class="%s-arg">...</span>' % css_class)
2702 else:
2703 args.append('<span class="%s-arg">*%s</span>' %
2704 (css_class, func_doc.vararg))
2705 if func_doc.kwarg not in (None, UNKNOWN):
2706 args.append('<span class="%s-arg">**%s</span>' %
2707 (css_class, func_doc.kwarg))
2708
2709 return ('<span class="%s">%s(%s)</span>' %
2710 (css_class, name, ',\n '.join(args)))
2711
2712 - def summary_name(self, api_doc, css_class='summary-name',
2713 link_name=False, anchor=False):
2714 """Render an object name in HTML.
2715
2716 @param api_doc: The object whose name is to be rendered
2717 @type api_doc: L{APIDoc}
2718 @param css_class: The CSS class to assign to the rendered name
2719 type css_class: C{str}
2720 @param link_name: If True, the name is a link to the object anchor.
2721 @type link_name: C{bool}
2722 @param anchor: If True, the name is the object anchor.
2723 @type anchor: C{bool}
2724
2725 @return: The HTML code for the object.
2726 @rtype: C{str}
2727 """
2728 if anchor:
2729 rv = '<a name="%s"></a>' % api_doc.name
2730 else:
2731 rv = ''
2732
2733 if link_name:
2734 rv += self.href(api_doc, css_class=css_class)
2735 else:
2736 rv += '<span class="%s">%s</span>' % (css_class, api_doc.name)
2737
2738 return rv
2739
2740
2741 - def func_arg(self, name, default, css_class):
2742 name = self._arg_name(name)
2743 s = '<span class="%s-arg">%s</span>' % (css_class, name)
2744 if default is not None:
2745 s += ('=<span class="%s-default">%s</span>' %
2746 (css_class, default.summary_pyval_repr().to_html(None)))
2747 return s
2748
2750 if isinstance(arg, basestring):
2751 return arg
2752 elif len(arg) == 1:
2753 return '(%s,)' % self._arg_name(arg[0])
2754 else:
2755 return '(%s)' % (', '.join([self._arg_name(a) for a in arg]))
2756
2757
2758
2759
2760
2761
2762
2763
2765 assert isinstance(doc, NamespaceDoc)
2766 imports = doc.select_variables(imported=True,
2767 public=self._public_filter)
2768 if not imports: return
2769
2770 out('<p class="indent-wrapped-lines">')
2771 out('<b>Imports:</b>\n ')
2772 out(',\n '.join([self._import(v, doc) for v in imports]))
2773 out('\n</p><br />\n')
2774
2775 - def _import(self, var_doc, context):
2787
2788
2789
2790
2791
2792
2793
2794
2795
2811
2829
2830
2831
2832
2833
2834 write_class_tree_item = compile_template(
2835 '''
2836 write_class_tree_item(self, out, doc, class_set)
2837 ''',
2838
2839 '''
2840 >>> if doc.summary in (None, UNKNOWN):
2841 <li> <strong class="uidlink">$self.href(doc)$</strong>
2842 >>> else:
2843 <li> <strong class="uidlink">$self.href(doc)$</strong>:
2844 <em class="summary">$self.description(doc.summary, doc, 8)$</em>
2845 >>> # endif
2846 >>> if doc.subclasses:
2847 <ul>
2848 >>> for subclass in sorted(set(doc.subclasses), key=lambda c:c.canonical_name[-1]):
2849 >>> if subclass in class_set:
2850 >>> self.write_class_tree_item(out, subclass, class_set)
2851 >>> #endif
2852 >>> #endfor
2853 </ul>
2854 >>> #endif
2855 </li>
2856 ''')
2857
2858
2859
2860
2861
2862
2864 """
2865 Write HTML code containing descriptions of any standard markup
2866 fields that are defined by the given L{APIDoc} object (such as
2867 C{@author} and C{@todo} fields).
2868
2869 @param doc: The L{APIDoc} object containing the API documentation
2870 for the object whose standard markup fields should be
2871 described.
2872 """
2873 fields = []
2874 field_values = {}
2875
2876 for (field, arg, descr) in doc.metadata:
2877 if field not in field_values:
2878 fields.append(field)
2879 if field.takes_arg:
2880 subfields = field_values.setdefault(field,{})
2881 subfields.setdefault(arg,[]).append(descr)
2882 else:
2883 field_values.setdefault(field,[]).append(descr)
2884
2885 if not fields: return
2886
2887 out('<div class="fields">')
2888 for field in fields:
2889 if field.takes_arg:
2890 for arg, descrs in field_values[field].items():
2891 self.write_standard_field(out, doc, field, descrs, arg)
2892
2893 else:
2894 self.write_standard_field(out, doc, field, field_values[field])
2895
2896 out('</div>')
2897
2898 write_standard_field = compile_template(
2899 """
2900 write_standard_field(self, out, doc, field, descrs, arg='')
2901
2902 """,
2903
2904 '''
2905 >>> if arg: arglabel = " (%s)" % arg
2906 >>> else: arglabel = ""
2907 >>> if len(descrs) == 1:
2908 <p><strong>$field.singular+arglabel$:</strong>
2909 $self.description(descrs[0], doc, 8)$
2910 </p>
2911 >>> elif field.short:
2912 <dl><dt>$field.plural+arglabel$:</dt>
2913 <dd>
2914 >>> for descr in descrs[:-1]:
2915 $self.description(descr, doc, 10)$,
2916 >>> # end for
2917 $self.description(descrs[-1], doc, 10)$
2918 </dd>
2919 </dl>
2920 >>> else:
2921 <strong>$field.plural+arglabel$:</strong>
2922 <ul class="nomargin-top">
2923 >>> for descr in descrs:
2924 <li>
2925 $self.description(descr, doc, 8)$
2926 </li>
2927 >>> # end for
2928 </ul>
2929 >>> # end else
2930 >>> # end for
2931 ''')
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943 METADATA_INDICES = [('bug', 'Bug List', 'Bugs'),
2944 ('todo', 'To Do List', 'To Do'),
2945 ('change', 'Change Log', 'Changes'),
2946 ('deprecated', 'Deprecation List', 'Deprecations'),
2947 ('since', 'Introductions List', 'Introductions'),
2948 ]
2949
2960
2962 """Preserves sort order of the input."""
2963 index = {}
2964 for item in items:
2965 first_letter = item[0][0].upper()
2966 if not ("A" <= first_letter <= "Z"):
2967 first_letter = '_'
2968 index.setdefault(first_letter, []).append(item)
2969 return index
2970
2972 items = []
2973 for doc in self.indexed_docs:
2974 url = self.url(doc)
2975 items += self._terms_from_docstring(url, doc, doc.descr)
2976 for (field, arg, descr) in doc.metadata:
2977 items += self._terms_from_docstring(url, doc, descr)
2978 if hasattr(doc, 'type_descr'):
2979 items += self._terms_from_docstring(url, doc,
2980 doc.type_descr)
2981 if hasattr(doc, 'return_descr'):
2982 items += self._terms_from_docstring(url, doc,
2983 doc.return_descr)
2984 if hasattr(doc, 'return_type'):
2985 items += self._terms_from_docstring(url, doc,
2986 doc.return_type)
2987 return sorted(items, key=lambda v:v[0].lower())
2988
2990 if parsed_docstring in (None, UNKNOWN): return []
2991 terms = []
2992
2993 base_url = re.sub('#.*', '', '%s' % (base_url,))
2994 for term in parsed_docstring.index_terms():
2995 anchor = self._term_index_to_anchor(term)
2996 url = '%s#%s' % (base_url, anchor)
2997 terms.append( (term.to_plaintext(None), url, container) )
2998 return terms
2999
3015
3017 """
3018 Given the name of an inline index item, construct a URI anchor.
3019 These anchors are used to create links from the index page to each
3020 index item.
3021 """
3022
3023
3024 s = re.sub(r'\s\s+', '-', term.to_plaintext(None))
3025 return "index-"+re.sub("[^a-zA-Z0-9]", "_", s)
3026
3027
3028
3029
3030
3031 - def write_redirect_page(self, out):
3032 """
3033 Build the auto-redirect page, which translates dotted names to
3034 URLs using javascript. When the user visits
3035 <redirect.html#dotted.name>, they will automatically get
3036 redirected to the page for the object with the given
3037 fully-qualified dotted name. E.g., for epydoc,
3038 <redirect.html#epydoc.apidoc.UNKNOWN> redirects the user to
3039 <epydoc.apidoc-module.html#UNKNOWN>.
3040 """
3041
3042
3043
3044
3045 pages = (['%s-m' % val_doc.canonical_name
3046 for val_doc in self.module_list] +
3047 ['%s-c' % val_doc.canonical_name
3048 for val_doc in self.class_list])
3049
3050
3051 pages = sorted(pages, key=lambda p:-len(p))
3052
3053
3054 self._write_redirect_page(out, pages)
3055
3056 _write_redirect_page = compile_template(
3057 '''
3058 _write_redirect_page(self, out, pages)
3059 ''',
3060
3061 '''
3062 <html><head><title>Epydoc Redirect Page</title>
3063 <meta http-equiv="cache-control" content="no-cache" />
3064 <meta http-equiv="expires" content="0" />
3065 <meta http-equiv="pragma" content="no-cache" />
3066 <script type="text/javascript" src="epydoc.js"></script>
3067 </head>
3068 <body>
3069 <script type="text/javascript">
3070 <!--
3071 var pages = $"[%s]" % ", ".join(['"%s"' % v for v in pages])$;
3072 var dottedName = get_anchor();
3073 if (dottedName) {
3074 var target = redirect_url(dottedName);
3075 if (target) window.location.replace(target);
3076 }
3077 // -->
3078 </script>
3079
3080 <h3>Epydoc Auto-redirect page</h3>
3081
3082 <p>When javascript is enabled, this page will redirect URLs of
3083 the form <tt>redirect.html#<i>dotted.name</i></tt> to the
3084 documentation for the object with the given fully-qualified
3085 dotted name.</p>
3086 <p><a id="message"> </a></p>
3087
3088 <script type="text/javascript">
3089 <!--
3090 if (dottedName) {
3091 var msg = document.getElementById("message");
3092 msg.innerHTML = "No documentation found for <tt>"+
3093 dottedName+"</tt>";
3094 }
3095 // -->
3096 </script>
3097
3098 </body>
3099 </html>
3100 ''')
3101
3102
3103
3104
3105
3106
3126
3131
3132
3133
3134
3135
3145
3146
3147
3148
3149 write_table_header = compile_template(
3150 '''
3151 write_table_header(self, out, css_class, heading=None, \
3152 private_link=True, colspan=2)
3153 ''',
3154
3155 '''
3156 >>> if heading is not None:
3157 >>> anchor = "section-%s" % re.sub("\W", "", heading)
3158 <!-- ==================== $heading.upper()$ ==================== -->
3159 <a name="$anchor$"></a>
3160 >>> #endif
3161 <table class="$css_class$" border="1" cellpadding="3"
3162 cellspacing="0" width="100%" bgcolor="white">
3163 >>> if heading is not None:
3164 <tr bgcolor="#70b0f0" class="table-header">
3165 >>> if private_link and self._show_private:
3166 <td colspan="$colspan$" class="table-header">
3167 <table border="0" cellpadding="0" cellspacing="0" width="100%">
3168 <tr valign="top">
3169 <td align="left"><span class="table-header">$heading$</span></td>
3170 <td align="right" valign="top"
3171 ><span class="options">[<a href="#$anchor$"
3172 class="privatelink" onclick="toggle_private();"
3173 >hide private</a>]</span></td>
3174 </tr>
3175 </table>
3176 </td>
3177 >>> else:
3178 <td align="left" colspan="2" class="table-header">
3179 <span class="table-header">$heading$</span></td>
3180 >>> #endif
3181 </tr>
3182 >>> #endif
3183 ''')
3184
3185
3186 TABLE_FOOTER = '</table>\n'
3187
3188 PRIVATE_LINK = '''
3189 <span class="options">[<a href="javascript:void(0);" class="privatelink"
3190 onclick="toggle_private();">hide private</a>]</span>
3191 '''.strip()
3192
3193 write_group_header = compile_template(
3194 '''
3195 write_group_header(self, out, group, tr_class='')
3196 ''',
3197
3198 '''
3199 <tr bgcolor="#e8f0f8" $tr_class$>
3200 <th colspan="2" class="group-header"
3201 > $group$</th></tr>
3202 ''')
3203
3204
3205 _url_cache = {}
3206 - def url(self, obj):
3207 """
3208 Return the URL for the given object, which can be a
3209 C{VariableDoc}, a C{ValueDoc}, or a C{DottedName}.
3210 """
3211 cached_url = self._url_cache.get(id(obj))
3212 if cached_url is not None:
3213 return cached_url
3214 else:
3215 url = self._url_cache[id(obj)] = self._url(obj)
3216 return url
3217
3218 - def _url(self, obj):
3219 """
3220 Internal helper for L{url}.
3221 """
3222
3223 if isinstance(obj, ModuleDoc):
3224 if obj not in self.module_set: return None
3225 return urllib.quote('%s'%obj.canonical_name) + '-module.html'
3226
3227 elif isinstance(obj, ClassDoc):
3228 if obj not in self.class_set: return None
3229 return urllib.quote('%s'%obj.canonical_name) + '-class.html'
3230
3231 elif isinstance(obj, VariableDoc):
3232 val_doc = obj.value
3233 if isinstance(val_doc, (ModuleDoc, ClassDoc)):
3234 return self.url(val_doc)
3235 elif obj.container in (None, UNKNOWN):
3236 if val_doc in (None, UNKNOWN): return None
3237 return self.url(val_doc)
3238 elif obj.is_imported == True:
3239 if obj.imported_from is not UNKNOWN:
3240 return self.url(obj.imported_from)
3241 else:
3242 return None
3243 else:
3244 container_url = self.url(obj.container)
3245 if container_url is None: return None
3246 return '%s#%s' % (container_url, urllib.quote('%s'%obj.name))
3247
3248 elif isinstance(obj, ValueDoc):
3249 container = self.docindex.container(obj)
3250 if container is None:
3251 return None
3252 else:
3253 container_url = self.url(container)
3254 if container_url is None: return None
3255 anchor = urllib.quote('%s'%obj.canonical_name[-1])
3256 return '%s#%s' % (container_url, anchor)
3257
3258 elif isinstance(obj, DottedName):
3259 val_doc = self.docindex.get_valdoc(obj)
3260 if val_doc is None: return None
3261 return self.url(val_doc)
3262
3263 elif obj == 'indices':
3264 return 'identifier-index.html'
3265 elif obj == 'help':
3266 return 'help.html'
3267 elif obj == 'trees':
3268 return self._trees_url
3269 else:
3270 raise ValueError, "Don't know what to do with %r" % obj
3271
3273 if not self._incl_sourcecode:
3274 return ''
3275 url = self.pysrc_url(api_doc)
3276 if url is not None:
3277 return ('<span class="codelink"><a href="%s">source '
3278 'code</a></span>' % url)
3279 else:
3280 return ''
3281
3283 if isinstance(api_doc, VariableDoc):
3284 if api_doc.value not in (None, UNKNOWN):
3285 return pysrc_url(api_doc.value)
3286 else:
3287 return None
3288 elif isinstance(api_doc, ModuleDoc):
3289 if api_doc in self.modules_with_sourcecode:
3290 return ('%s-pysrc.html' %
3291 urllib.quote('%s' % api_doc.canonical_name))
3292 else:
3293 return None
3294 else:
3295 module = api_doc.defining_module
3296 if module == UNKNOWN: return None
3297 module_pysrc_url = self.pysrc_url(module)
3298 if module_pysrc_url is None: return None
3299 module_name = module.canonical_name
3300 if not module_name.dominates(api_doc.canonical_name, True):
3301 log.debug('%r is in %r but name does not dominate' %
3302 (api_doc, module))
3303 return module_pysrc_url
3304 mname_len = len(module.canonical_name)
3305 anchor = '%s' % api_doc.canonical_name[mname_len:]
3306 return '%s#%s' % (module_pysrc_url, urllib.quote(anchor))
3307
3308
3309 return None
3310
3311
3312 - def href(self, target, label=None, css_class=None, context=None,
3313 tooltip=None):
3314 """
3315 Return the HTML code for an HREF link to the given target
3316 (which can be a C{VariableDoc}, a C{ValueDoc}, or a
3317 C{DottedName}.
3318 If a C{NamespaceDoc} C{context} is specified, the target label is
3319 contextualized to it.
3320 """
3321 assert isinstance(target, (APIDoc, DottedName))
3322
3323
3324 if label is None:
3325 if isinstance(target, VariableDoc):
3326 label = target.name
3327 elif (isinstance(target, ValueDoc) and
3328 target.canonical_name is not UNKNOWN):
3329 label = target.canonical_name
3330 elif isinstance(target, DottedName):
3331 label = target
3332 elif isinstance(target, GenericValueDoc):
3333 raise ValueError("href() should not be called with "
3334 "GenericValueDoc objects (perhaps you "
3335 "meant to use the containing variable?)")
3336 else:
3337 raise ValueError("Unable to find a label for %r" % target)
3338
3339 if context is not None and isinstance(label, DottedName):
3340 label = label.contextualize(context.canonical_name.container())
3341
3342 label = plaintext_to_html(str(label))
3343
3344
3345 if label.startswith('script-'):
3346 label = label[7:] + ' (script)'
3347 if label.startswith('??'):
3348 label = '<i>unreachable</i>' + label[2:]
3349 label = re.sub(r'-\d+$', '', label)
3350
3351
3352 url = self.url(target)
3353 if url is None:
3354 if tooltip: return '<span title="%s">%s</span>' % (tooltip, label)
3355 else: return label
3356
3357
3358 if css_class is None:
3359 css = ''
3360 else:
3361 css = ' class="%s"' % css_class
3362
3363 onclick = ''
3364 if ((isinstance(target, VariableDoc) and not target.is_public) or
3365 (isinstance(target, ValueDoc) and
3366 not isinstance(target, GenericValueDoc) and
3367 not self._val_is_public(target))):
3368 onclick = ' onclick="show_private();"'
3369
3370 if tooltip:
3371 tooltip = ' title="%s"' % tooltip
3372 else:
3373 tooltip = ''
3374
3375 return '<a href="%s"%s%s%s>%s</a>' % (url, css, onclick, tooltip, label)
3376
3385
3386 - def summary(self, api_doc, indent=0):
3388
3389 - def descr(self, api_doc, indent=0):
3391
3394
3397
3400
3402 if parsed_docstring in (None, UNKNOWN): return ''
3403 linker = _HTMLDocstringLinker(self, where)
3404 s = parsed_docstring.to_html(linker, indent=indent,
3405 directory=self._directory,
3406 docindex=self.docindex,
3407 context=where).strip()
3408 if self._mark_docstrings:
3409 s = '<span class="docstring">%s</span><!--end docstring-->' % s
3410 return s
3411
3412 - def description(self, parsed_docstring, where=None, indent=0):
3413 assert isinstance(where, (APIDoc, type(None)))
3414 if parsed_docstring in (None, UNKNOWN): return ''
3415 linker = _HTMLDocstringLinker(self, where)
3416 descr = parsed_docstring.to_html(linker, indent=indent,
3417 directory=self._directory,
3418 docindex=self.docindex,
3419 context=where).strip()
3420 if descr == '': return ' '
3421 return descr
3422
3423
3425 if isinstance(doc, ModuleDoc) and doc.is_package == True:
3426 return 'Package'
3427 elif (isinstance(doc, ModuleDoc) and
3428 doc.canonical_name[0].startswith('script')):
3429 return 'Script'
3430 elif isinstance(doc, ModuleDoc):
3431 return 'Module'
3432 elif isinstance(doc, ClassDoc):
3433 return 'Class'
3434 elif isinstance(doc, ClassMethodDoc):
3435 return 'Class Method'
3436 elif isinstance(doc, StaticMethodDoc):
3437 return 'Static Method'
3438 elif isinstance(doc, RoutineDoc):
3439 if isinstance(self.docindex.container(doc), ClassDoc):
3440 return 'Method'
3441 else:
3442 return 'Function'
3443 else:
3444 return 'Variable'
3445
3447 name = api_doc.canonical_name
3448 for i in range(len(name), 0, -1):
3449
3450 var_doc = self.docindex.get_vardoc(name[:i])
3451 if var_doc is not None and var_doc.is_public == False:
3452 return True
3453
3454 val_doc = self.docindex.get_valdoc(name[:i])
3455 if (val_doc is not None and isinstance(val_doc, ModuleDoc) and
3456 val_doc.canonical_name[-1].startswith('_')):
3457 return True
3458 return False
3459
3461 """Return a list of all subclasses of the given class that are
3462 private, as determined by L{_val_is_public}. Recursive
3463 subclasses are included in this list."""
3464 queue = [class_doc]
3465 private = set()
3466 for cls in queue:
3467 if (isinstance(cls, ClassDoc) and
3468 cls.subclasses not in (None, UNKNOWN)):
3469 queue.extend(cls.subclasses)
3470 private.update([c for c in cls.subclasses if
3471 not self._val_is_public(c)])
3472 return private
3473
3475 - def __init__(self, htmlwriter, container):
3476 self.htmlwriter = htmlwriter
3477 self.docindex = htmlwriter.docindex
3478 self.container = container
3479
3484
3508
3510 if isinstance(identifier, (basestring, DottedName)):
3511 doc = self.docindex.find(identifier, self.container)
3512 if doc:
3513 return self.htmlwriter.url(doc)
3514 else:
3515 return None
3516
3517 elif isinstance(identifier, APIDoc):
3518 return self.htmlwriter.url(identifier)
3519
3520 else:
3521 raise TypeError('Expected string or APIDoc')
3522
3524 """Add an identifier to the htmlwriter's failed crossreference
3525 list."""
3526
3527
3528 if (isinstance(self.container, RoutineDoc) and
3529 identifier in self.container.all_args()):
3530 return
3531
3532 failed_xrefs = self.htmlwriter._failed_xrefs
3533 context = self.container.canonical_name
3534 failed_xrefs.setdefault(identifier,{})[context] = 1
3535