0001"""Python source code colorizer.
0002
0003This module is derived from MoinMoin's [1] python source parser, described
0004in the following recipe:
0005
0006<http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52298>
0007
0008.. [1] http://moin.sourceforge.net/
0009
0010"""
0011
0012
0013import cgi, string, sys, cStringIO
0014import keyword, token, tokenize
0015import re
0016
0017
0018
0019
0020
0021_KEYWORD = token.NT_OFFSET + 1
0022_TEXT = token.NT_OFFSET + 2
0023
0024_styles = {
0025 token.NUMBER: 'number',
0026 token.OP: 'op',
0027 token.STRING: 'string',
0028 tokenize.COMMENT: 'comment',
0029 token.NAME: 'name',
0030 token.ERRORTOKEN: 'error',
0031 _KEYWORD: 'keyword',
0032 _TEXT: 'text',
0033}
0034
0035class Parser:
0036 """ Send colored python source."""
0037
0038 def __init__(self, filename, out = sys.stdout):
0039 """ Store the source text.
0040 """
0041 self.filename = filename
0042 self.raw = open(filename, 'r').read().expandtabs().strip()
0043 self.out = out
0044
0045 def format(self):
0046 """ Parse and send the colored source."""
0047
0048 self.lines = [0, 0]
0049 pos = 0
0050 while 1:
0051 pos = self.raw.find('\n', pos) + 1
0052 if not pos: break
0053 self.lines.append(pos)
0054 self.lines.append(len(self.raw))
0055
0056
0057 self.out.write('''<html><head><title>%s</title><style>
0058 div.python {
0059 color: #333
0060 }
0061 div.python a.lnum {
0062 color: #555;
0063 background-color: #eee;
0064 border-right: 1px solid #999;
0065 padding-right: 2px;
0066 margin-right: 4px;
0067 }
0068 div.python span.comment { color: #933 }
0069 div.python span.keyword { color: #a3e; font-weight: bold }
0070 div.python span.op { color: #c96 }
0071 div.python span.string { color: #6a6 }
0072 div.python span.name { }
0073 div.python span.text { color: #333 }
0074
0075 </style></head><body>''' % cgi.escape(self.filename))
0076 self.out.write('<div class="python"><code>')
0077 self.write_line(1, br='')
0078 self.pos = 0
0079 text = cStringIO.StringIO(self.raw)
0080 self.run_tokens(tokenize.generate_tokens(text.readline))
0081 self.out.write('</code></div>')
0082 self.out.write('</body></html>')
0083 self.out.flush()
0084 self.out.close()
0085
0086 def write_line(self, line_num, br='<br />\n'):
0087 fmt = str(line_num).rjust(4,'0')
0088 self.out.write(
0089 '%s<a class="lnum" href="#%d" name="%d">%s</a>' % (br, line_num, line_num, fmt))
0091
0092 def run_tokens(self, it):
0093 """ Token handler."""
0094 for tok in it:
0095 (toktype, toktext, (srow,scol), (erow,ecol), line) = tok
0096 if 0:
0097 print repr((toktype, token.tok_name[toktype], toktext,
0098 srow,scol, erow,ecol))
0099
0100
0101 oldpos = self.pos
0102 newpos = self.lines[srow] + scol
0103 self.pos = newpos + len(toktext)
0104
0105
0106 if toktype in [token.NEWLINE, tokenize.NL]:
0107 self.write_line(srow+1)
0108 continue
0109
0110
0111 if newpos > oldpos:
0112 ws = self.raw[oldpos:newpos]
0113 self.out.write(' ' * len(ws))
0114
0115
0116 if toktype in [token.INDENT, token.DEDENT]:
0117 self.pos = newpos
0118 continue
0119
0120
0121 if token.LPAR <= toktype and toktype <= token.OP:
0122 toktype = token.OP
0123 elif toktype == token.NAME and keyword.iskeyword(toktext):
0124 toktype = _KEYWORD
0125 style = _styles.get(toktype, _styles[_TEXT])
0126
0127
0128 self.runlines(toktext, srow, '<span class="%s">%%s</span>' % style)
0129
0130
0131 def runlines(self, text, line_num, interpolate):
0132 if '\n' in text:
0133 lines = text.split('\n')
0134 for i, line in zip(range(len(lines)), lines):
0135 if i > 0: self.write_line(line_num + i)
0136 self.out.write(interpolate % cgi.escape(line).replace(' ', ' '))
0137 elif text:
0138 self.out.write(interpolate % cgi.escape(text).replace(' ', ' '))
0139
0140__all__ = ['Parser']
0141
0142
0143__author__ = "Ryan Tomayko <rtomayko@gmail.com>"
0144__date__ = "$Date: 2005-05-28 04:58:44 -0400 (Sat, 28 May 2005) $"
0145__revision__ = "$Revision: 12 $"
0146__url__ = "$URL: svn://lesscode.org/pudge/trunk/pudge/colorizer.py $"
0147__copyright__ = "Copyright 2005, Ryan Tomayko"
0148__license__ = "MIT <http://www.opensource.org/licenses/mit-license.php>"