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 except UnicodeDecodeError, e:
492 log.warning("Unicode error while generating syntax-highlighted "
493 "source code: %s (%s)" % (e, self.module_filename))
494 html = html.decode(coding, 'ignore').encode(
495 'ascii', 'xmlcharrefreplace')
496
497
498
499 html += PYSRC_EXPANDTO_JAVASCRIPT
500
501 return html
502
503 - def tokeneater(self, toktype, toktext, (srow,scol), (erow,ecol), line):
504 """
505 A callback function used by C{tokenize.tokenize} to handle
506 each token in the module. C{tokeneater} collects tokens into
507 the C{self.cur_line} list until a complete logical line has
508 been formed; and then calls L{handle_line} to process that line.
509 """
510
511 if toktype == token.ERRORTOKEN:
512 raise tokenize.TokenError, toktype
513
514
515
516
517
518 startpos = self.line_offsets[srow] + scol
519 if startpos > self.pos:
520 skipped = self.text[self.pos:startpos]
521 self.cur_line.append( (None, skipped) )
522
523
524 self.pos = startpos + len(toktext)
525
526
527 self.cur_line.append( (toktype, toktext) )
528
529
530 if toktype == token.NEWLINE or toktype == token.ENDMARKER:
531 self.handle_line(self.cur_line)
532 self.cur_line = []
533
534 _next_uid = 0
535
536
537
538
540 """
541 Render a single logical line from the module, and write the
542 generated HTML to C{self.out}.
543
544 @param line: A single logical line, encoded as a list of
545 C{(toktype,tokttext)} pairs corresponding to the tokens in
546 the line.
547 """
548
549
550 def_name = None
551
552
553
554 def_type = None
555
556
557 starting_def_block = False
558
559 in_base_list = False
560 in_param_list = False
561 in_param_default = 0
562 at_module_top = (self.lineno == 1)
563
564 ended_def_blocks = 0
565
566
567 if self.ADD_LINE_NUMBERS:
568 s = self.lineno_to_html()
569 self.lineno += 1
570 else:
571 s = ''
572 s += ' <tt class="py-line">'
573
574
575 for i, (toktype, toktext) in enumerate(line):
576 if type(s) is not str:
577 if type(s) is unicode:
578 log.error('While colorizing %s -- got unexpected '
579 'unicode string' % self.module_name)
580 s = s.encode('ascii', 'xmlcharrefreplace')
581 else:
582 raise ValueError('Unexpected value for s -- %s' %
583 type(s).__name__)
584
585
586
587 css_class = None
588 url = None
589 tooltip = None
590 onclick = uid = targets = None
591
592
593
594 if i>=2 and line[i-2][1] == 'class':
595 in_base_list = True
596 css_class = self.CSS_CLASSES['DEFNAME']
597 def_name = toktext
598 def_type = 'class'
599 if 'func' not in self.context_types:
600 cls_name = self.context_name(def_name)
601 url = self.name2url(cls_name)
602 s = self.mark_def(s, cls_name)
603 starting_def_block = True
604
605
606
607 elif i>=2 and line[i-2][1] == 'def':
608 in_param_list = True
609 css_class = self.CSS_CLASSES['DEFNAME']
610 def_name = toktext
611 def_type = 'func'
612 if 'func' not in self.context_types:
613 cls_name = self.context_name()
614 func_name = self.context_name(def_name)
615 url = self.name2url(cls_name, def_name)
616 s = self.mark_def(s, func_name)
617 starting_def_block = True
618
619
620
621
622
623
624 elif toktype == token.INDENT:
625 self.indents.append(toktext)
626 self.context.append(self.def_name)
627 self.context_types.append(self.def_type)
628
629
630
631
632
633 elif toktype == token.DEDENT:
634 self.indents.pop()
635 self.context_types.pop()
636 if self.context.pop():
637 ended_def_blocks += 1
638
639
640
641 elif toktype in (None, tokenize.NL, token.NEWLINE,
642 token.ENDMARKER):
643 css_class = None
644
645
646 elif toktype == token.NAME and keyword.iskeyword(toktext):
647 css_class = self.CSS_CLASSES['KEYWORD']
648
649 elif in_base_list and toktype == token.NAME:
650 css_class = self.CSS_CLASSES['BASECLASS']
651
652 elif (in_param_list and toktype == token.NAME and
653 not in_param_default):
654 css_class = self.CSS_CLASSES['PARAM']
655
656
657 elif (self.def_name and line[i-1][0] == token.INDENT and
658 self.is_docstring(line, i)):
659 css_class = self.CSS_CLASSES['DOCSTRING']
660
661
662 elif at_module_top and self.is_docstring(line, i):
663 css_class = self.CSS_CLASSES['DOCSTRING']
664
665
666 elif (toktype == token.NAME and
667 ((i>0 and line[i-1][1]=='@') or
668 (i>1 and line[i-1][0]==None and line[i-2][1] == '@'))):
669 css_class = self.CSS_CLASSES['DECORATOR']
670 self.has_decorators = True
671
672
673 elif toktype == token.NAME:
674 css_class = self.CSS_CLASSES['NAME']
675
676
677
678
679
680 if (self.GUESS_LINK_TARGETS and self.docindex is not None
681 and self.url_func is not None):
682 context = [n for n in self.context if n is not None]
683 container = self.docindex.get_vardoc(
684 DottedName(self.module_name, *context))
685 if isinstance(container, NamespaceDoc):
686 doc = container.variables.get(toktext)
687 if doc is not None:
688 url = self.url_func(doc)
689 tooltip = str(doc.canonical_name)
690
691
692 if (url is None and self.name_to_docs is not None
693 and self.url_func is not None):
694 docs = self.name_to_docs.get(toktext)
695 if docs:
696 tooltip='\n'.join([str(d.canonical_name)
697 for d in docs])
698 if len(docs) == 1 and self.GUESS_LINK_TARGETS:
699 url = self.url_func(docs[0])
700 else:
701 uid, onclick, targets = self.doclink(toktext, docs)
702
703
704
705 else:
706 if toktype == token.OP and toktext in self.CSS_CLASSES:
707 css_class = self.CSS_CLASSES[toktext]
708 elif token.tok_name[toktype] in self.CSS_CLASSES:
709 css_class = self.CSS_CLASSES[token.tok_name[toktype]]
710 else:
711 css_class = None
712
713
714 if toktext == ':':
715 in_base_list = False
716 in_param_list = False
717 if toktext == '=' and in_param_list:
718 in_param_default = True
719 if in_param_default:
720 if toktext in ('(','[','{'): in_param_default += 1
721 if toktext in (')',']','}'): in_param_default -= 1
722 if toktext == ',' and in_param_default == 1:
723 in_param_default = 0
724
725
726 if tooltip and self.ADD_TOOLTIPS:
727 tooltip_html = ' title="%s"' % tooltip
728 else: tooltip_html = ''
729 if css_class: css_class_html = ' class="%s"' % css_class
730 else: css_class_html = ''
731 if onclick:
732 if targets: targets_html = ' targets="%s"' % targets
733 else: targets_html = ''
734 s += ('<tt id="%s"%s%s><a%s%s href="#" onclick="%s">' %
735 (uid, css_class_html, targets_html, tooltip_html,
736 css_class_html, onclick))
737 elif url:
738 if isinstance(url, unicode):
739 url = url.encode('ascii', 'xmlcharrefreplace')
740 s += ('<a%s%s href="%s">' %
741 (tooltip_html, css_class_html, url))
742 elif css_class_html or tooltip_html:
743 s += '<tt%s%s>' % (tooltip_html, css_class_html)
744 if i == len(line)-1:
745 s += ' </tt>'
746 s += cgi.escape(toktext)
747 else:
748 try:
749 s += self.add_line_numbers(cgi.escape(toktext), css_class)
750 except Exception, e:
751 print (toktext, css_class, toktext.encode('ascii'))
752 raise
753
754 if onclick: s += "</a></tt>"
755 elif url: s += '</a>'
756 elif css_class_html or tooltip_html: s += '</tt>'
757
758 if self.ADD_DEF_BLOCKS:
759 for i in range(ended_def_blocks):
760 self.out(self.END_DEF_BLOCK)
761
762
763 s = re.sub(r'<tt class="[\w+]"></tt>', '', s)
764
765
766 self.out(s)
767
768 if def_name and starting_def_block:
769 self.out('</div>')
770
771
772 if (self.ADD_DEF_BLOCKS and def_name and starting_def_block and
773 (line[-2][1] == ':')):
774 indentation = (''.join(self.indents)+' ').replace(' ', '+')
775 linenum_padding = '+'*self.linenum_size
776 name=self.context_name(def_name)
777 self.out(self.START_DEF_BLOCK % (name, linenum_padding,
778 indentation, name))
779
780 self.def_name = def_name
781 self.def_type = def_type
782
783 - def context_name(self, extra=None):
784 pieces = [n for n in self.context if n is not None]
785 if extra is not None: pieces.append(extra)
786 return '.'.join(pieces)
787
789 uid = 'link-%s' % self._next_uid
790 self._next_uid += 1
791 context = [n for n in self.context if n is not None]
792 container = DottedName(self.module_name, *context)
793
794
795 targets = ','.join(['%s=%s' % (str(self.doc_descr(d,container)),
796 str(self.url_func(d)))
797 for d in docs])
798
799 if targets in self.doclink_targets_cache:
800 onclick = ("return doclink('%s', '%s', '%s');" %
801 (uid, name, self.doclink_targets_cache[targets]))
802 return uid, onclick, None
803 else:
804 self.doclink_targets_cache[targets] = uid
805 onclick = ("return doclink('%s', '%s', '%s');" %
806 (uid, name, uid))
807 return uid, onclick, targets
808
815
816
817
819 if isinstance(doc, ModuleDoc) and doc.is_package == True:
820 return 'Package'
821 elif (isinstance(doc, ModuleDoc) and
822 doc.canonical_name[0].startswith('script')):
823 return 'Script'
824 elif isinstance(doc, ModuleDoc):
825 return 'Module'
826 elif isinstance(doc, ClassDoc):
827 return 'Class'
828 elif isinstance(doc, ClassMethodDoc):
829 return 'Class Method'
830 elif isinstance(doc, StaticMethodDoc):
831 return 'Static Method'
832 elif isinstance(doc, RoutineDoc):
833 if (self.docindex is not None and
834 isinstance(self.docindex.container(doc), ClassDoc)):
835 return 'Method'
836 else:
837 return 'Function'
838 else:
839 return 'Variable'
840
842 replacement = ('<a name="%s"></a><div id="%s-def">\\1'
843 '<a class="py-toggle" href="#" id="%s-toggle" '
844 'onclick="return toggle(\'%s\');">-</a>\\2' %
845 (name, name, name, name))
846 return re.sub('(.*) (<tt class="py-line">.*)\Z', replacement, s)
847
849 if line[i][0] != token.STRING: return False
850 for toktype, toktext in line[i:]:
851 if toktype not in (token.NEWLINE, tokenize.COMMENT,
852 tokenize.NL, token.STRING, None):
853 return False
854 return True
855
857 result = ''
858 start = 0
859 end = s.find('\n')+1
860 while end:
861 result += s[start:end-1]
862 if css_class: result += '</tt>'
863 result += ' </tt>'
864 result += '\n'
865 if self.ADD_LINE_NUMBERS:
866 result += self.lineno_to_html()
867 result += ' <tt class="py-line">'
868 if css_class: result += '<tt class="%s">' % css_class
869 start = end
870 end = s.find('\n', end)+1
871 self.lineno += 1
872 result += s[start:]
873 return result
874
875 - def name2url(self, class_name, func_name=None):
876 if class_name:
877 class_name = '%s.%s' % (self.module_name, class_name)
878 if func_name:
879 return '%s-class.html#%s' % (class_name, func_name)
880 else:
881 return '%s-class.html' % class_name
882 else:
883 return '%s-module.html#%s' % (self.module_name, func_name)
884
885
886
887 _FIX_DECORATOR_RE = re.compile(
888 r'((?:^<a name="L\d+"></a><tt class="py-lineno">\s*\d+</tt>'
889 r'\s*<tt class="py-line">(?:<tt class="py-decorator">.*|\s*</tt>|'
890 r'\s*<tt class="py-comment">.*)\n)+)'
891 r'(<a name="\w+"></a><div id="\w+-def">)', re.MULTILINE)
892
893 _HDR = '''\
894 <?xml version="1.0" encoding="ascii"?>
895 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
896 "DTD/xhtml1-transitional.dtd">
897 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
898 <head>
899 <title>$title$</title>
900 <link rel="stylesheet" href="epydoc.css" type="text/css" />
901 <script type="text/javascript" src="epydoc.js"></script>
902 </head>
903
904 <body bgcolor="white" text="black" link="blue" vlink="#204080"
905 alink="#204080">
906 '''
907 _FOOT = '</body></html>'
908 if __name__=='__main__':
909
910 s = PythonSourceColorizer('/tmp/fo.py', 'epydoc.apidoc').colorize()
911
912 import codecs
913 f = codecs.open('/home/edloper/public_html/color3.html', 'w', 'ascii', 'xmlcharrefreplace')
914 f.write(_HDR+'<pre id="py-src-top" class="py-src">'+s+'</pre>'+_FOOT)
915 f.close()
916