Back to index

plone3  3.1.7
python.py
Go to the documentation of this file.
00001 """
00002 Original code from active state recipe
00003         'Colorize Python source using the built-in tokenizer'
00004 
00005 ----------------------------------------------------------------------------
00006      MoinMoin - Python Source Parser
00007 
00008  This code is part of MoinMoin (http://moin.sourceforge.net/) and converts
00009  Python source code to HTML markup, rendering comments, keywords, operators,
00010  numeric and string literals in different colors.
00011 
00012  It shows how to use the built-in keyword, token and tokenize modules
00013  to scan Python source code and re-emit it with no changes to its
00014  original formatting (which is the hard part).
00015 """
00016 __revision__ = '$Id: python.py 3661 2005-02-23 17:05:31Z tiran $'
00017 
00018 import string
00019 import keyword, token, tokenize
00020 from cStringIO import StringIO
00021 
00022 from Products.PortalTransforms.interfaces import itransform
00023 from DocumentTemplate.DT_Util import html_quote
00024 
00025 ## Python Source Parser #####################################################
00026 
00027 _KEYWORD = token.NT_OFFSET + 1
00028 _TEXT    = token.NT_OFFSET + 2
00029 
00030 class Parser:
00031     """ Send colored python source.
00032     """
00033 
00034     def __init__(self, raw, tags, out):
00035         """ Store the source text.
00036         """
00037         self.raw = string.strip(string.expandtabs(raw))
00038         self.out = out
00039         self.tags = tags
00040 
00041     def format(self):
00042         """ Parse and send the colored source.
00043         """
00044         # store line offsets in self.lines
00045         self.lines = [0, 0]
00046         pos = 0
00047         while 1:
00048             pos = string.find(self.raw, '\n', pos) + 1
00049             if not pos: break
00050             self.lines.append(pos)
00051         self.lines.append(len(self.raw))
00052 
00053         # parse the source and write it
00054         self.pos = 0
00055         text = StringIO(self.raw)
00056         self.out.write('<pre class="python">\n')
00057         try:
00058             tokenize.tokenize(text.readline, self)
00059         except tokenize.TokenError, ex:
00060             msg = ex[0]
00061             line = ex[1][0]
00062             self.out.write("<h5 class='error>'ERROR: %s%s</h5>" % (
00063                 msg, self.raw[self.lines[line]:]))
00064         self.out.write('\n</pre>\n')
00065 
00066     def __call__(self, toktype, toktext, (srow,scol), (erow,ecol), line):
00067         """ Token handler.
00068         """
00069         #print "type", toktype, token.tok_name[toktype], "text", toktext,
00070         #print "start", srow,scol, "end", erow,ecol, "<br>"
00071 
00072         ## calculate new positions
00073         oldpos = self.pos
00074         newpos = self.lines[srow] + scol
00075         self.pos = newpos + len(toktext)
00076 
00077         ## handle newlines
00078         if toktype in [token.NEWLINE, tokenize.NL]:
00079             self.out.write('\n')
00080             return
00081 
00082         ## send the original whitespace, if needed
00083         if newpos > oldpos:
00084             self.out.write(self.raw[oldpos:newpos])
00085 
00086         ## skip indenting tokens
00087         if toktype in [token.INDENT, token.DEDENT]:
00088             self.pos = newpos
00089             return
00090 
00091         ## map token type to a group
00092         if token.LPAR <= toktype and toktype <= token.OP:
00093             toktype = 'OP'
00094         elif toktype == token.NAME and keyword.iskeyword(toktext):
00095             toktype = 'KEYWORD'
00096         else:
00097             toktype = tokenize.tok_name[toktype]
00098 
00099         open_tag = self.tags.get('OPEN_'+toktype, self.tags['OPEN_TEXT'])
00100         close_tag = self.tags.get('CLOSE_'+toktype, self.tags['CLOSE_TEXT'])
00101 
00102         ## send text
00103         self.out.write(open_tag)
00104         self.out.write(html_quote(toktext))
00105         self.out.write(close_tag)
00106 
00107 
00108 
00109 class PythonTransform:
00110     """Colorize Python source files
00111     """
00112     __implements__ = itransform
00113 
00114     __name__ = "python_to_html"
00115     inputs  = ("text/x-python",)
00116     output = "text/html"
00117 
00118     config = {
00119         'OPEN_NUMBER':       '<span style="color: #0080C0;">',
00120         'CLOSE_NUMBER':      '</span>',
00121         'OPEN_OP':           '<span style="color: #0000C0;">',
00122         'CLOSE_OP':          '</span>',
00123         'OPEN_STRING':       '<span style="color: #004080;">',
00124         'CLOSE_STRING':      '</span>',
00125         'OPEN_COMMENT':      '<span style="color: #008000;">',
00126         'CLOSE_COMMENT':      '</span>',
00127         'OPEN_NAME':         '<span style="color: #000000;">',
00128         'CLOSE_NAME':        '</span>',
00129         'OPEN_ERRORTOKEN':   '<span style="color: #FF8080;">',
00130         'CLOSE_ERRORTOKEN':  '</span>',
00131         'OPEN_KEYWORD':      '<span style="color: #C00000;">',
00132         'CLOSE_KEYWORD':     '</span>',
00133         'OPEN_TEXT':         '',
00134         'CLOSE_TEXT':        '',
00135         }
00136 
00137     def name(self):
00138         return self.__name__
00139 
00140     def convert(self, orig, data, **kwargs):
00141         dest = StringIO()
00142         Parser(orig, self.config, dest).format()
00143         data.setData(dest.getvalue())
00144         return data
00145 
00146 
00147 def register():
00148     return PythonTransform()