1
2
3
4
5
6
7
8
9 """
10 Functions to produce colorized HTML code for various objects.
11 Currently, C{html_colorize} defines functions to colorize
12 Python source code.
13 """
14 __docformat__ = 'epytext en'
15
16 import re, codecs
17 from epydoc import log
18 from epydoc.util import py_src_filename
19 from epydoc.apidoc import *
20 import tokenize, token, cgi, keyword
21 try: from cStringIO import StringIO
22 except: from StringIO import StringIO
23
24
25
26
27 """
28 Goals:
29 - colorize tokens appropriately (using css)
30 - optionally add line numbers
31 -
32 """
33
34
35 PYSRC_JAVASCRIPTS = '''\
36 function expand(id) {
37 var elt = document.getElementById(id+"-expanded");
38 if (elt) elt.style.display = "block";
39 var elt = document.getElementById(id+"-expanded-linenums");
40 if (elt) elt.style.display = "block";
41 var elt = document.getElementById(id+"-collapsed");
42 if (elt) { elt.innerHTML = ""; elt.style.display = "none"; }
43 var elt = document.getElementById(id+"-collapsed-linenums");
44 if (elt) { elt.innerHTML = ""; elt.style.display = "none"; }
45 var elt = document.getElementById(id+"-toggle");
46 if (elt) { elt.innerHTML = "-"; }
47 }
48
49 function collapse(id) {
50 var elt = document.getElementById(id+"-expanded");
51 if (elt) elt.style.display = "none";
52 var elt = document.getElementById(id+"-expanded-linenums");
53 if (elt) elt.style.display = "none";
54 var elt = document.getElementById(id+"-collapsed-linenums");
55 if (elt) { elt.innerHTML = "<br />"; elt.style.display="block"; }
56 var elt = document.getElementById(id+"-toggle");
57 if (elt) { elt.innerHTML = "+"; }
58 var elt = document.getElementById(id+"-collapsed");
59 if (elt) {
60 elt.style.display = "block";
61
62 var indent = elt.getAttribute("indent");
63 var pad = elt.getAttribute("pad");
64 var s = "<tt class=\'py-lineno\'>";
65 for (var i=0; i<pad.length; i++) { s += " " }
66 s += "</tt>";
67 s += " <tt class=\'py-line\'>";
68 for (var i=0; i<indent.length; i++) { s += " " }
69 s += "<a href=\'#\' onclick=\'expand(\\"" + id;
70 s += "\\");return false\'>...</a></tt><br />";
71 elt.innerHTML = s;
72 }
73 }
74
75 function toggle(id) {
76 elt = document.getElementById(id+"-toggle");
77 if (elt.innerHTML == "-")
78 collapse(id);
79 else
80 expand(id);
81 return false;
82 }
83
84 function highlight(id) {
85 var elt = document.getElementById(id+"-def");
86 if (elt) elt.className = "py-highlight-hdr";
87 var elt = document.getElementById(id+"-expanded");
88 if (elt) elt.className = "py-highlight";
89 var elt = document.getElementById(id+"-collapsed");
90 if (elt) elt.className = "py-highlight";
91 }
92
93 function num_lines(s) {
94 var n = 1;
95 var pos = s.indexOf("\\n");
96 while ( pos > 0) {
97 n += 1;
98 pos = s.indexOf("\\n", pos+1);
99 }
100 return n;
101 }
102
103 // Collapse all blocks that mave more than `min_lines` lines.
104 function collapse_all(min_lines) {
105 var elts = document.getElementsByTagName("div");
106 for (var i=0; i<elts.length; i++) {
107 var elt = elts[i];
108 var split = elt.id.indexOf("-");
109 if (split > 0)
110 if (elt.id.substring(split, elt.id.length) == "-expanded")
111 if (num_lines(elt.innerHTML) > min_lines)
112 collapse(elt.id.substring(0, split));
113 }
114 }
115
116 function expandto(href) {
117 var start = href.indexOf("#")+1;
118 if (start != 0 && start != href.length) {
119 if (href.substring(start, href.length) != "-") {
120 collapse_all(4);
121 pos = href.indexOf(".", start);
122 while (pos != -1) {
123 var id = href.substring(start, pos);
124 expand(id);
125 pos = href.indexOf(".", pos+1);
126 }
127 var id = href.substring(start, href.length);
128 expand(id);
129 highlight(id);
130 }
131 }
132 }
133
134 function kill_doclink(id) {
135 var parent = document.getElementById(id);
136 parent.removeChild(parent.childNodes.item(0));
137 }
138 function auto_kill_doclink(ev) {
139 if (!ev) var ev = window.event;
140 if (!this.contains(ev.toElement)) {
141 var parent = document.getElementById(this.parentID);
142 parent.removeChild(parent.childNodes.item(0));
143 }
144 }
145
146 function doclink(id, name, targets_id) {
147 var elt = document.getElementById(id);
148
149 // If we already opened the box, then destroy it.
150 // (This case should never occur, but leave it in just in case.)
151 if (elt.childNodes.length > 1) {
152 elt.removeChild(elt.childNodes.item(0));
153 }
154 else {
155 // The outer box: relative + inline positioning.
156 var box1 = document.createElement("div");
157 box1.style.position = "relative";
158 box1.style.display = "inline";
159 box1.style.top = 0;
160 box1.style.left = 0;
161
162 // A shadow for fun
163 var shadow = document.createElement("div");
164 shadow.style.position = "absolute";
165 shadow.style.left = "-1.3em";
166 shadow.style.top = "-1.3em";
167 shadow.style.background = "#404040";
168
169 // The inner box: absolute positioning.
170 var box2 = document.createElement("div");
171 box2.style.position = "relative";
172 box2.style.border = "1px solid #a0a0a0";
173 box2.style.left = "-.2em";
174 box2.style.top = "-.2em";
175 box2.style.background = "white";
176 box2.style.padding = ".3em .4em .3em .4em";
177 box2.style.fontStyle = "normal";
178 box2.onmouseout=auto_kill_doclink;
179 box2.parentID = id;
180
181 // Get the targets
182 var targets_elt = document.getElementById(targets_id);
183 var targets = targets_elt.getAttribute("targets");
184 var links = "";
185 target_list = targets.split(",");
186 for (var i=0; i<target_list.length; i++) {
187 var target = target_list[i].split("=");
188 links += "<li><a href=\'" + target[1] +
189 "\' style=\'text-decoration:none\'>" +
190 target[0] + "</a></li>";
191 }
192
193 // Put it all together.
194 elt.insertBefore(box1, elt.childNodes.item(0));
195 //box1.appendChild(box2);
196 box1.appendChild(shadow);
197 shadow.appendChild(box2);
198 box2.innerHTML =
199 "Which <b>"+name+"</b> do you want to see documentation for?" +
200 "<ul style=\'margin-bottom: 0;\'>" +
201 links +
202 "<li><a href=\'#\' style=\'text-decoration:none\' " +
203 "onclick=\'kill_doclink(\\""+id+"\\");return false;\'>"+
204 "<i>None of the above</i></a></li></ul>";
205 }
206 return false;
207 }
208 '''
209
210 PYSRC_EXPANDTO_JAVASCRIPT = '''\
211 <script type="text/javascript">
212 <!--
213 expandto(location.href);
214 // -->
215 </script>
216 '''
217
219 """
220 A class that renders a python module's source code into HTML
221 pages. These HTML pages are intended to be provided along with
222 the API documentation for a module, in case a user wants to learn
223 more about a particular object by examining its source code.
224 Links are therefore generated from the API documentation to the
225 source code pages, and from the source code pages back into the
226 API documentation.
227
228 The HTML generated by C{PythonSourceColorizer} has several notable
229 features:
230
231 - CSS styles are used to color tokens according to their type.
232 (See L{CSS_CLASSES} for a list of the different token types
233 that are identified).
234
235 - Line numbers are included to the left of each line.
236
237 - The first line of each class and function definition includes
238 a link to the API source documentation for that object.
239
240 - The first line of each class and function definition includes
241 an anchor that can be used to link directly to that class or
242 function.
243
244 - If javascript is enabled, and the page is loaded using the
245 anchor for a class or function (i.e., if the url ends in
246 C{'#I{<name>}'}), then that class or function will automatically
247 be highlighted; and all other classes and function definition
248 blocks will be 'collapsed'. These collapsed blocks can be
249 expanded by clicking on them.
250
251 - Unicode input is supported (including automatic detection
252 of C{'coding:'} declarations).
253
254 """
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270 CSS_CLASSES = {
271 'NUMBER': 'py-number',
272 'STRING': 'py-string',
273 'COMMENT': 'py-comment',
274 'NAME': 'py-name',
275 'KEYWORD': 'py-keyword',
276 'DEFNAME': 'py-def-name',
277 'BASECLASS': 'py-base-class',
278 'PARAM': 'py-param',
279 'DOCSTRING': 'py-docstring',
280 'DECORATOR': 'py-decorator',
281 'OP': 'py-op',
282 '@': 'py-decorator',
283 }
284
285
286
287
288
289
290
291
292
293
294
295
296 START_DEF_BLOCK = (
297 '<div id="%s-collapsed" style="display:none;" '
298 'pad="%s" indent="%s"></div>'
299 '<div id="%s-expanded">')
300
301
302
303 END_DEF_BLOCK = '</div>'
304
305
306
307 UNICODE_CODING_RE = re.compile(r'.*?\n?.*?coding[:=]\s*([-\w.]+)')
308
309
310
311 ADD_DEF_BLOCKS = True
312
313
314
315 ADD_LINE_NUMBERS = True
316
317
318
319 ADD_TOOLTIPS = True
320
321
322
323
324 GUESS_LINK_TARGETS = False
325
326 - def __init__(self, module_filename, module_name,
327 docindex=None, url_func=None, name_to_docs=None,
328 tab_width=8):
329 """
330 Create a new HTML colorizer for the specified module.
331
332 @param module_filename: The name of the file containing the
333 module; its text will be loaded from this file.
334 @param module_name: The dotted name of the module; this will
335 be used to create links back into the API source
336 documentation.
337 """
338
339 try: module_filename = py_src_filename(module_filename)
340 except: pass
341
342
343 self.module_filename = module_filename
344
345
346 self.module_name = module_name
347
348
349
350 self.docindex = docindex
351
352
353
354
355 self.name_to_docs = name_to_docs
356
357
358
359
360 self.url_func = url_func
361
362
363
364 self.pos = 0
365
366
367
368
369
370 self.line_offsets = []
371
372
373
374
375
376 self.cur_line = []
377
378
379
380
381
382
383
384 self.context = []
385
386
387
388
389 self.context_types = []
390
391
392
393
394 self.indents = []
395
396
397 self.lineno = 0
398
399
400
401
402 self.def_name = None
403
404
405
406
407
408 self.def_type = None
409
410
411 self.tab_width = tab_width
412
413
415 """
416 Construct the L{line_offsets} table from C{self.text}.
417 """
418
419 self.line_offsets = [None, 0]
420
421
422 pos = self.text.find('\n')
423 while pos != -1:
424 self.line_offsets.append(pos+1)
425 pos = self.text.find('\n', pos+1)
426
427 self.line_offsets.append(len(self.text))
428
430 template = '%%%ds' % self.linenum_size
431 n = template % self.lineno
432 return '<a name="L%s"></a><tt class="py-lineno">%s</tt>' \
433 % (self.lineno, n)
434
436 """
437 Return an HTML string that renders the source code for the
438 module that was specified in the constructor.
439 """
440
441 self.pos = 0
442 self.cur_line = []
443 self.context = []
444 self.context_types = []
445 self.indents = []
446 self.lineno = 1
447 self.def_name = None
448 self.def_type = None
449 self.has_decorators = False
450
451
452
453 self.doclink_targets_cache = {}
454
455
456 self.text = open(self.module_filename).read()
457 self.text = self.text.expandtabs(self.tab_width).rstrip()+'\n'
458
459
460 self.find_line_offsets()
461
462 num_lines = self.text.count('\n')+1
463 self.linenum_size = len(`num_lines+1`)
464
465
466
467
468 try:
469 output = StringIO()
470 self.out = output.write
471 tokenize.tokenize(StringIO(self.text).readline, self.tokeneater)
472 html = output.getvalue()
473 if self.has_decorators:
474 html = self._FIX_DECORATOR_RE.sub(r'\2\1', html)
475 except tokenize.TokenError, ex:
476 html = self.text
477
478
479 m = self.UNICODE_CODING_RE.match(self.text)
480 if m: coding = m.group(1)
481 else: coding = 'iso-8859-1'
482
483
484
485
486 try:
487 html = html.decode(coding).encode('ascii', 'xmlcharrefreplace')
488 except LookupError:
489 coding = 'iso-8859-1'
490 html = html.decode(coding).encode('ascii', 'xmlcharrefreplace')
491
492
493 html += PYSRC_EXPANDTO_JAVASCRIPT
494
495 return html
496
497 - def tokeneater(self, toktype, toktext, (srow,scol), (erow,ecol), line):
498 """
499 A callback function used by C{tokenize.tokenize} to handle
500 each token in the module. C{tokeneater} collects tokens into
501 the C{self.cur_line} list until a complete logical line has
502 been formed; and then calls L{handle_line} to process that line.
503 """
504
505 if toktype == token.ERRORTOKEN:
506 raise tokenize.TokenError, toktype
507
508
509
510
511
512 startpos = self.line_offsets[srow] + scol
513 if startpos > self.pos:
514 skipped = self.text[self.pos:startpos]
515 self.cur_line.append( (None, skipped) )
516
517
518 self.pos = startpos + len(toktext)
519
520
521 self.cur_line.append( (toktype, toktext) )
522
523
524 if toktype == token.NEWLINE or toktype == token.ENDMARKER:
525 self.handle_line(self.cur_line)
526 self.cur_line = []
527
528 _next_uid = 0
529
530
531
532
534 """
535 Render a single logical line from the module, and write the
536 generated HTML to C{self.out}.
537
538 @param line: A single logical line, encoded as a list of
539 C{(toktype,tokttext)} pairs corresponding to the tokens in
540 the line.
541 """
542
543
544 def_name = None
545
546
547
548 def_type = None
549
550
551 starting_def_block = False
552
553 in_base_list = False
554 in_param_list = False
555 in_param_default = 0
556 at_module_top = (self.lineno == 1)
557
558 ended_def_blocks = 0
559
560
561 if self.ADD_LINE_NUMBERS:
562 s = self.lineno_to_html()
563 self.lineno += 1
564 else:
565 s = ''
566 s += ' <tt class="py-line">'
567
568
569 for i, (toktype, toktext) in enumerate(line):
570 if type(s) is not str:
571 if type(s) is unicode:
572 log.error('While colorizing %s -- got unexpected '
573 'unicode string' % self.module_name)
574 s = s.encode('ascii', 'xmlcharrefreplace')
575 else:
576 raise ValueError('Unexpected value for s -- %s' %
577 type(s).__name__)
578
579
580
581 css_class = None
582 url = None
583 tooltip = None
584 onclick = uid = targets = None
585
586
587
588 if i>=2 and line[i-2][1] == 'class':
589 in_base_list = True
590 css_class = self.CSS_CLASSES['DEFNAME']
591 def_name = toktext
592 def_type = 'class'
593 if 'func' not in self.context_types:
594 cls_name = self.context_name(def_name)
595 url = self.name2url(cls_name)
596 s = self.mark_def(s, cls_name)
597 starting_def_block = True
598
599
600
601 elif i>=2 and line[i-2][1] == 'def':
602 in_param_list = True
603 css_class = self.CSS_CLASSES['DEFNAME']
604 def_name = toktext
605 def_type = 'func'
606 if 'func' not in self.context_types:
607 cls_name = self.context_name()
608 func_name = self.context_name(def_name)
609 url = self.name2url(cls_name, def_name)
610 s = self.mark_def(s, func_name)
611 starting_def_block = True
612
613
614
615
616
617
618 elif toktype == token.INDENT:
619 self.indents.append(toktext)
620 self.context.append(self.def_name)
621 self.context_types.append(self.def_type)
622
623
624
625
626
627 elif toktype == token.DEDENT:
628 self.indents.pop()
629 self.context_types.pop()
630 if self.context.pop():
631 ended_def_blocks += 1
632
633
634
635 elif toktype in (None, tokenize.NL, token.NEWLINE,
636 token.ENDMARKER):
637 css_class = None
638
639
640 elif toktype == token.NAME and keyword.iskeyword(toktext):
641 css_class = self.CSS_CLASSES['KEYWORD']
642
643 elif in_base_list and toktype == token.NAME:
644 css_class = self.CSS_CLASSES['BASECLASS']
645
646 elif (in_param_list and toktype == token.NAME and
647 not in_param_default):
648 css_class = self.CSS_CLASSES['PARAM']
649
650
651 elif (self.def_name and line[i-1][0] == token.INDENT and
652 self.is_docstring(line, i)):
653 css_class = self.CSS_CLASSES['DOCSTRING']
654
655
656 elif at_module_top and self.is_docstring(line, i):
657 css_class = self.CSS_CLASSES['DOCSTRING']
658
659
660 elif (toktype == token.NAME and
661 ((i>0 and line[i-1][1]=='@') or
662 (i>1 and line[i-1][0]==None and line[i-2][1] == '@'))):
663 css_class = self.CSS_CLASSES['DECORATOR']
664 self.has_decorators = True
665
666
667 elif toktype == token.NAME:
668 css_class = self.CSS_CLASSES['NAME']
669
670
671
672
673
674 if (self.GUESS_LINK_TARGETS and self.docindex is not None
675 and self.url_func is not None):
676 context = [n for n in self.context if n is not None]
677 container = self.docindex.get_vardoc(
678 DottedName(self.module_name, *context))
679 if isinstance(container, NamespaceDoc):
680 doc = container.variables.get(toktext)
681 if doc is not None:
682 url = self.url_func(doc)
683 tooltip = str(doc.canonical_name)
684
685
686 if (url is None and self.name_to_docs is not None
687 and self.url_func is not None):
688 docs = self.name_to_docs.get(toktext)
689 if docs:
690 tooltip='\n'.join([str(d.canonical_name)
691 for d in docs])
692 if len(docs) == 1 and self.GUESS_LINK_TARGETS:
693 url = self.url_func(docs[0])
694 else:
695 uid, onclick, targets = self.doclink(toktext, docs)
696
697
698
699 else:
700 if toktype == token.OP and toktext in self.CSS_CLASSES:
701 css_class = self.CSS_CLASSES[toktext]
702 elif token.tok_name[toktype] in self.CSS_CLASSES:
703 css_class = self.CSS_CLASSES[token.tok_name[toktype]]
704 else:
705 css_class = None
706
707
708 if toktext == ':':
709 in_base_list = False
710 in_param_list = False
711 if toktext == '=' and in_param_list:
712 in_param_default = True
713 if in_param_default:
714 if toktext in ('(','[','{'): in_param_default += 1
715 if toktext in (')',']','}'): in_param_default -= 1
716 if toktext == ',' and in_param_default == 1:
717 in_param_default = 0
718
719
720 if tooltip and self.ADD_TOOLTIPS:
721 tooltip_html = ' title="%s"' % tooltip
722 else: tooltip_html = ''
723 if css_class: css_class_html = ' class="%s"' % css_class
724 else: css_class_html = ''
725 if onclick:
726 if targets: targets_html = ' targets="%s"' % targets
727 else: targets_html = ''
728 s += ('<tt id="%s"%s%s><a%s%s href="#" onclick="%s">' %
729 (uid, css_class_html, targets_html, tooltip_html,
730 css_class_html, onclick))
731 elif url:
732 if isinstance(url, unicode):
733 url = url.encode('ascii', 'xmlcharrefreplace')
734 s += ('<a%s%s href="%s">' %
735 (tooltip_html, css_class_html, url))
736 elif css_class_html or tooltip_html:
737 s += '<tt%s%s>' % (tooltip_html, css_class_html)
738 if i == len(line)-1:
739 s += ' </tt>'
740 s += cgi.escape(toktext)
741 else:
742 try:
743 s += self.add_line_numbers(cgi.escape(toktext), css_class)
744 except Exception, e:
745 print (toktext, css_class, toktext.encode('ascii'))
746 raise
747
748 if onclick: s += "</a></tt>"
749 elif url: s += '</a>'
750 elif css_class_html or tooltip_html: s += '</tt>'
751
752 if self.ADD_DEF_BLOCKS:
753 for i in range(ended_def_blocks):
754 self.out(self.END_DEF_BLOCK)
755
756
757 s = re.sub(r'<tt class="[\w+]"></tt>', '', s)
758
759
760 self.out(s)
761
762 if def_name and starting_def_block:
763 self.out('</div>')
764
765
766 if (self.ADD_DEF_BLOCKS and def_name and starting_def_block and
767 (line[-2][1] == ':')):
768 indentation = (''.join(self.indents)+' ').replace(' ', '+')
769 linenum_padding = '+'*self.linenum_size
770 name=self.context_name(def_name)
771 self.out(self.START_DEF_BLOCK % (name, linenum_padding,
772 indentation, name))
773
774 self.def_name = def_name
775 self.def_type = def_type
776
777 - def context_name(self, extra=None):
778 pieces = [n for n in self.context if n is not None]
779 if extra is not None: pieces.append(extra)
780 return '.'.join(pieces)
781
783 uid = 'link-%s' % self._next_uid
784 self._next_uid += 1
785 context = [n for n in self.context if n is not None]
786 container = DottedName(self.module_name, *context)
787
788
789 targets = ','.join(['%s=%s' % (str(self.doc_descr(d,container)),
790 str(self.url_func(d)))
791 for d in docs])
792
793 if targets in self.doclink_targets_cache:
794 onclick = ("return doclink('%s', '%s', '%s');" %
795 (uid, name, self.doclink_targets_cache[targets]))
796 return uid, onclick, None
797 else:
798 self.doclink_targets_cache[targets] = uid
799 onclick = ("return doclink('%s', '%s', '%s');" %
800 (uid, name, uid))
801 return uid, onclick, targets
802
809
810
811
813 if isinstance(doc, ModuleDoc) and doc.is_package == True:
814 return 'Package'
815 elif (isinstance(doc, ModuleDoc) and
816 doc.canonical_name[0].startswith('script')):
817 return 'Script'
818 elif isinstance(doc, ModuleDoc):
819 return 'Module'
820 elif isinstance(doc, ClassDoc):
821 return 'Class'
822 elif isinstance(doc, ClassMethodDoc):
823 return 'Class Method'
824 elif isinstance(doc, StaticMethodDoc):
825 return 'Static Method'
826 elif isinstance(doc, RoutineDoc):
827 if (self.docindex is not None and
828 isinstance(self.docindex.container(doc), ClassDoc)):
829 return 'Method'
830 else:
831 return 'Function'
832 else:
833 return 'Variable'
834
836 replacement = ('<a name="%s"></a><div id="%s-def">\\1'
837 '<a class="py-toggle" href="#" id="%s-toggle" '
838 'onclick="return toggle(\'%s\');">-</a>\\2' %
839 (name, name, name, name))
840 return re.sub('(.*) (<tt class="py-line">.*)\Z', replacement, s)
841
843 if line[i][0] != token.STRING: return False
844 for toktype, toktext in line[i:]:
845 if toktype not in (token.NEWLINE, tokenize.COMMENT,
846 tokenize.NL, token.STRING, None):
847 return False
848 return True
849
851 result = ''
852 start = 0
853 end = s.find('\n')+1
854 while end:
855 result += s[start:end-1]
856 if css_class: result += '</tt>'
857 result += ' </tt>'
858 result += '\n'
859 if self.ADD_LINE_NUMBERS:
860 result += self.lineno_to_html()
861 result += ' <tt class="py-line">'
862 if css_class: result += '<tt class="%s">' % css_class
863 start = end
864 end = s.find('\n', end)+1
865 self.lineno += 1
866 result += s[start:]
867 return result
868
869 - def name2url(self, class_name, func_name=None):
870 if class_name:
871 class_name = '%s.%s' % (self.module_name, class_name)
872 if func_name:
873 return '%s-class.html#%s' % (class_name, func_name)
874 else:
875 return '%s-class.html' % class_name
876 else:
877 return '%s-module.html#%s' % (self.module_name, func_name)
878
879
880
881 _FIX_DECORATOR_RE = re.compile(
882 r'((?:^<a name="L\d+"></a><tt class="py-lineno">\s*\d+</tt>'
883 r'\s*<tt class="py-line">(?:<tt class="py-decorator">.*|\s*</tt>|'
884 r'\s*<tt class="py-comment">.*)\n)+)'
885 r'(<a name="\w+"></a><div id="\w+-def">)', re.MULTILINE)
886
887 _HDR = '''\
888 <?xml version="1.0" encoding="ascii"?>
889 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
890 "DTD/xhtml1-transitional.dtd">
891 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
892 <head>
893 <title>$title$</title>
894 <link rel="stylesheet" href="epydoc.css" type="text/css" />
895 <script type="text/javascript" src="epydoc.js"></script>
896 </head>
897
898 <body bgcolor="white" text="black" link="blue" vlink="#204080"
899 alink="#204080">
900 '''
901 _FOOT = '</body></html>'
902 if __name__=='__main__':
903
904 s = PythonSourceColorizer('/tmp/fo.py', 'epydoc.apidoc').colorize()
905
906 import codecs
907 f = codecs.open('/home/edloper/public_html/color3.html', 'w', 'ascii', 'xmlcharrefreplace')
908 f.write(_HDR+'<pre id="py-src-top" class="py-src">'+s+'</pre>'+_FOOT)
909 f.close()
910