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