From 30823489a0317035a3cad425fc3e24af160d917f Mon Sep 17 00:00:00 2001 From: Ian Bicking Date: Tue, 27 Sep 2005 01:43:23 +0000 Subject: Brought in PySourceColor, changed import to same --- paste/doctest_webapp.py | 2 +- paste/util/PySourceColor.py | 2088 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 2089 insertions(+), 1 deletion(-) create mode 100644 paste/util/PySourceColor.py diff --git a/paste/doctest_webapp.py b/paste/doctest_webapp.py index 5196dfa..ce0c75c 100755 --- a/paste/doctest_webapp.py +++ b/paste/doctest_webapp.py @@ -15,7 +15,7 @@ import re import cgi import rfc822 from cStringIO import StringIO -import PySourceColor +from paste.util import PySourceColor here = os.path.abspath(__file__) diff --git a/paste/util/PySourceColor.py b/paste/util/PySourceColor.py new file mode 100644 index 0000000..09f6cec --- /dev/null +++ b/paste/util/PySourceColor.py @@ -0,0 +1,2088 @@ +#!/usr/bin/env python +# -*- coding: Latin-1 -*- +""" + PySourceColor.py +---------------------------------------------------------------------------- + A python source to colorized html/css/xhtml converter. + Hacked by M.E.Farmer Jr. 2004, 2005 + Python license +---------------------------------------------------------------------------- + - HTML markup does not create w3c valid html, but it works on every + browser i've tried so far.(I.E.,Mozilla/Firefox,Opera,Konqueror,wxHTML). + - CSS markup is w3c validated html 4.01 strict, + but will not render correctly on all browsers. + - XHTML markup is w3c validated xhtml 1.0 strict, + like html 4.01, will not render correctly on all browsers. +---------------------------------------------------------------------------- + Features: + -Three types of markup: + html (default) + css/html 4.01 strict + xhtml 1.0 strict + + -Can tokenize and colorize: + 12 types of strings + 2 comment types + numbers + operators + brackets + math operators + class / name + def / name + decorator / name + keywords + arguments class/def/decorator + linenumbers + names + text + + -Eight colorschemes built-in: + null + mono + lite (default) + dark + dark2 + idle + viewcvs + pythonwin + + -Header and footer + set to '' for builtin header / footer. + give path to a file containing the html + you want added as header or footer. + + -Arbitrary text and html + html markup converts all to raw (TEXT token) + #@# for raw -> send raw text. + #$# for span -> inline html and text. + #%# for div -> block level html and text. + + -Linenumbers + Supports all styles. New token is called LINENUMBER. + Defaults to NAME if not defined. + + Style options + -ALL markups support these text styles: + b = bold + i = italic + u = underline + -CSS and XHTML has limited support for borders: + HTML markup functions will ignore these. + Optional: Border color in RGB hex + Defaults to the text forecolor. + #rrggbb = border color + Border size: + l = thick + m = medium + t = thin + Border type: + - = dashed + . = dotted + s = solid + d = double + g = groove + r = ridge + n = inset + o = outset + You can specify multiple sides, + they will all use the same style. + Optional: Default is full border. + v = bottom + < = left + > = right + ^ = top + NOTE: Specify the styles you want. + The markups will ignore unsupported styles + Also note not all browsers can show these options + + -All tokens default to NAME if not defined + so the only absolutely critical ones to define are: + NAME, ERRORTOKEN, PAGEBACKGROUND + +---------------------------------------------------------------------------- + Example usage: +---------------------------------------------------------------------------- + # import + import PySourceColor as psc + psc.convert('c:/Python22/PySourceColor.py', colors=psc.idle, show=1) +---------------------------------------------------------------------------- + # from module import * + from PySourceColor import * + convert('c:/Python22/Lib', colors=lite, markup="css", + header='#$#This is a simpe heading
') +---------------------------------------------------------------------------- + # How to use a custom colorscheme, and most of the 'features' + from PySourceColor import * + new = { + ERRORTOKEN: ('bui','#FF8080',''), + DECORATOR_NAME: ('s','#AACBBC',''), + DECORATOR: ('n','#333333',''), + NAME: ('t.','#000000','#FFFFFF'), + TRIPLEDOUBLEQUOTE_R: ('tub','#000000','#FFFFFF'), + TRIPLEDOUBLEQUOTE_U: ('-', '#CCAABB','#FFFAFF'), + LINENUMBER: ('ib-','#ff66aa','#7733FF'),] + TEXT: ('','#546634',''), + PAGEBACKGROUND: '#FFFAAA', + } + if __name__ == '__main__': + import sys + convert(sys.argv[1], './xhtml.html', colors=new, markup='xhtml', show=1, + linenumbers=1) + convert(sys.argv[1], './html.html', colors=new, markup='html', show=1, + linenumbers=1) +---------------------------------------------------------------------------- +""" + +__all__ = ['ERRORTOKEN','DECORATOR_NAME', 'DECORATOR', 'ARGS', 'EXTRASPACE', + 'NAME', 'NUMBER', 'OPERATOR', 'COMMENT', 'MATH_OPERATOR', + 'DOUBLECOMMENT', 'CLASS_NAME', 'DEF_NAME', 'KEYWORD', 'BRACKETS', + 'SINGLEQUOTE','SINGLEQUOTE_R','SINGLEQUOTE_U','DOUBLEQUOTE', + 'DOUBLEQUOTE_R', 'DOUBLEQUOTE_U', 'TRIPLESINGLEQUOTE', 'TEXT', + 'TRIPLESINGLEQUOTE_R', 'TRIPLESINGLEQUOTE_U', 'TRIPLEDOUBLEQUOTE', + 'TRIPLEDOUBLEQUOTE_R', 'TRIPLEDOUBLEQUOTE_U', 'PAGEBACKGROUND', + 'LINENUMBER', 'CODESTART', 'CODEEND', 'PY', 'TOKEN_NAMES', 'CSSHOOK', + 'null', 'mono', 'lite', 'dark','dark2', 'pythonwin','idle', + 'viewcvs', 'Usage', 'cli', 'str2stdout', 'path2stdout', 'Parser', + 'str2file', 'str2html', 'str2css', 'str2markup', 'path2file', + 'path2html', 'convert', 'walkdir', 'defaultColors', 'showpage', + 'pageconvert','tagreplace', 'MARKUPDICT'] +__title__ = 'PySourceColor' +__version__ = "2.1a" +__date__ = '25 April 2005' +__author__ = "M.E.Farmer Jr." +__credits__ = '''This was originally based on a python recipe +submitted by Jürgen Hermann to ASPN. Now based on the voices in my head. +M.E.Farmer 2004, 2005 +Python license +''' +import os +import sys +import time +import glob +import getopt +import keyword +import token +import tokenize +import traceback +import webbrowser +try : + import cStringIO as StringIO +except: + import StringIO +# Do not edit +NAME = token.NAME +NUMBER = token.NUMBER +COMMENT = tokenize.COMMENT +OPERATOR = token.OP +ERRORTOKEN = token.ERRORTOKEN +ARGS = token.NT_OFFSET + 1 +DOUBLECOMMENT = token.NT_OFFSET + 2 +CLASS_NAME = token.NT_OFFSET + 3 +DEF_NAME = token.NT_OFFSET + 4 +KEYWORD = token.NT_OFFSET + 5 +SINGLEQUOTE = token.NT_OFFSET + 6 +SINGLEQUOTE_R = token.NT_OFFSET + 7 +SINGLEQUOTE_U = token.NT_OFFSET + 8 +DOUBLEQUOTE = token.NT_OFFSET + 9 +DOUBLEQUOTE_R = token.NT_OFFSET + 10 +DOUBLEQUOTE_U = token.NT_OFFSET + 11 +TRIPLESINGLEQUOTE = token.NT_OFFSET + 12 +TRIPLESINGLEQUOTE_R = token.NT_OFFSET + 13 +TRIPLESINGLEQUOTE_U = token.NT_OFFSET + 14 +TRIPLEDOUBLEQUOTE = token.NT_OFFSET + 15 +TRIPLEDOUBLEQUOTE_R = token.NT_OFFSET + 16 +TRIPLEDOUBLEQUOTE_U = token.NT_OFFSET + 17 +PAGEBACKGROUND = token.NT_OFFSET + 18 +DECORATOR = token.NT_OFFSET + 19 +DECORATOR_NAME = token.NT_OFFSET + 20 +BRACKETS = token.NT_OFFSET + 21 +MATH_OPERATOR = token.NT_OFFSET + 22 +LINENUMBER = token.NT_OFFSET + 23 +TEXT = token.NT_OFFSET + 24 +PY = token.NT_OFFSET + 25 +CODESTART = token.NT_OFFSET + 26 +CODEEND = token.NT_OFFSET + 27 +CSSHOOK = token.NT_OFFSET + 28 +EXTRASPACE = token.NT_OFFSET + 29 + +# markup classname lookup +MARKUPDICT = { + ERRORTOKEN: 'py_err', + DECORATOR_NAME: 'py_decn', + DECORATOR: 'py_dec', + ARGS: 'py_args', + NAME: 'py_name', + NUMBER: 'py_num', + OPERATOR: 'py_op', + COMMENT: 'py_com', + DOUBLECOMMENT: 'py_dcom', + CLASS_NAME: 'py_clsn', + DEF_NAME: 'py_defn', + KEYWORD: 'py_key', + SINGLEQUOTE: 'py_sq', + SINGLEQUOTE_R: 'py_sqr', + SINGLEQUOTE_U: 'py_squ', + DOUBLEQUOTE: 'py_dq', + DOUBLEQUOTE_R: 'py_dqr', + DOUBLEQUOTE_U: 'py_dqu', + TRIPLESINGLEQUOTE: 'py_tsq', + TRIPLESINGLEQUOTE_R: 'py_tsqr', + TRIPLESINGLEQUOTE_U: 'py_tsqu', + TRIPLEDOUBLEQUOTE: 'py_tdq', + TRIPLEDOUBLEQUOTE_R: 'py_tdqr', + TRIPLEDOUBLEQUOTE_U: 'py_tdqu', + BRACKETS: 'py_bra', + MATH_OPERATOR: 'py_mop', + LINENUMBER: 'py_lnum', + TEXT: 'py_text', + } +# might help users that want to create custom schemes +TOKEN_NAMES= { + ERRORTOKEN:'ERRORTOKEN', + DECORATOR_NAME:'DECORATOR_NAME', + DECORATOR:'DECORATOR', + ARGS:'ARGS', + NAME:'NAME', + NUMBER:'NUMBER', + OPERATOR:'OPERATOR', + COMMENT:'COMMENT', + DOUBLECOMMENT:'DOUBLECOMMENT', + CLASS_NAME:'CLASS_NAME', + DEF_NAME:'DEF_NAME', + KEYWORD:'KEYWORD', + SINGLEQUOTE:'SINGLEQUOTE', + SINGLEQUOTE_R:'SINGLEQUOTE_R', + SINGLEQUOTE_U:'SINGLEQUOTE_U', + DOUBLEQUOTE:'DOUBLEQUOTE', + DOUBLEQUOTE_R:'DOUBLEQUOTE_R', + DOUBLEQUOTE_U:'DOUBLEQUOTE_U', + TRIPLESINGLEQUOTE:'TRIPLESINGLEQUOTE', + TRIPLESINGLEQUOTE_R:'TRIPLESINGLEQUOTE_R', + TRIPLESINGLEQUOTE_U:'TRIPLESINGLEQUOTE_U', + TRIPLEDOUBLEQUOTE:'TRIPLEDOUBLEQUOTE', + TRIPLEDOUBLEQUOTE_R:'TRIPLEDOUBLEQUOTE_R', + TRIPLEDOUBLEQUOTE_U:'TRIPLEDOUBLEQUOTE_U', + BRACKETS:'BRACKETS', + MATH_OPERATOR:'MATH_OPERATOR', + LINENUMBER:'LINENUMBER', + TEXT:'TEXT', + PAGEBACKGROUND:'PAGEBACKGROUND', + } + +###################################################################### +# Edit colors and styles to taste +# Create your own scheme, just copy one below , rename and edit. +# Custom styles must at least define NAME, ERRORTOKEN, PAGEBACKGROUND, +# all missing elements will default to NAME. +# See module docstring for details on style attributes. +###################################################################### +# Copy null and use it as a starter colorscheme. +null = {# tokentype: ('tags border_color', 'textforecolor', 'textbackcolor') + ERRORTOKEN: ('','#000000',''),# Error token + DECORATOR_NAME: ('','#000000',''),# Decorator name + DECORATOR: ('','#000000',''),# @ symbol + ARGS: ('','#000000',''),# class,def,deco arguments + NAME: ('','#000000',''),# All other python text + NUMBER: ('','#000000',''),# 0->10 + OPERATOR: ('','#000000',''),# ':','<=',';',',','.','==', etc + MATH_OPERATOR: ('','#000000',''),# '+','-','=','','**',etc + BRACKETS: ('','#000000',''),# '[',']','(',')','{','}' + COMMENT: ('','#000000',''),# Single comment + DOUBLECOMMENT: ('','#000000',''),## Double comment + CLASS_NAME: ('','#000000',''),# Class name + DEF_NAME: ('','#000000',''),# Def name + KEYWORD: ('','#000000',''),# Python keywords + SINGLEQUOTE: ('','#000000',''),# 'SINGLEQUOTE' + SINGLEQUOTE_R: ('','#000000',''),# r'SINGLEQUOTE' + SINGLEQUOTE_U: ('','#000000',''),# u'SINGLEQUOTE' + DOUBLEQUOTE: ('','#000000',''),# "DOUBLEQUOTE" + DOUBLEQUOTE_R: ('','#000000',''),# r"DOUBLEQUOTE" + DOUBLEQUOTE_U: ('','#000000',''),# u"DOUBLEQUOTE" + TRIPLESINGLEQUOTE: ('','#000000',''),# '''TRIPLESINGLEQUOTE''' + TRIPLESINGLEQUOTE_R: ('','#000000',''),# r'''TRIPLESINGLEQUOTE''' + TRIPLESINGLEQUOTE_U: ('','#000000',''),# u'''TRIPLESINGLEQUOTE''' + TRIPLEDOUBLEQUOTE: ('','#000000',''),# """TRIPLEDOUBLEQUOTE""" + TRIPLEDOUBLEQUOTE_R: ('','#000000',''),# r"""TRIPLEDOUBLEQUOTE""" + TRIPLEDOUBLEQUOTE_U: ('','#000000',''),# u"""TRIPLEDOUBLEQUOTE""" + TEXT: ('','#000000',''),# non python text + LINENUMBER: ('>ti#555555','#000000',''),# Linenumbers + PAGEBACKGROUND: '#FFFFFF'# set the page background + } + +mono = { + ERRORTOKEN: ('s#FF0000','#FF8080',''), + DECORATOR_NAME: ('bu','#000000',''), + DECORATOR: ('b','#000000',''), + ARGS: ('b','#555555',''), + NAME: ('','#000000',''), + NUMBER: ('b','#000000',''), + OPERATOR: ('b','#000000',''), + MATH_OPERATOR: ('b','#000000',''), + BRACKETS: ('b','#000000',''), + COMMENT: ('i','#999999',''), + DOUBLECOMMENT: ('b','#999999',''), + CLASS_NAME: ('bu','#000000',''), + DEF_NAME: ('b','#000000',''), + KEYWORD: ('b','#000000',''), + SINGLEQUOTE: ('','#000000',''), + SINGLEQUOTE_R: ('','#000000',''), + SINGLEQUOTE_U: ('','#000000',''), + DOUBLEQUOTE: ('','#000000',''), + DOUBLEQUOTE_R: ('','#000000',''), + DOUBLEQUOTE_U: ('','#000000',''), + TRIPLESINGLEQUOTE: ('','#000000',''), + TRIPLESINGLEQUOTE_R: ('','#000000',''), + TRIPLESINGLEQUOTE_U: ('','#000000',''), + TRIPLEDOUBLEQUOTE: ('i','#000000',''), + TRIPLEDOUBLEQUOTE_R: ('i','#000000',''), + TRIPLEDOUBLEQUOTE_U: ('i','#000000',''), + TEXT: ('','#000000',''), + LINENUMBER: ('>ti#555555','#000000',''), + PAGEBACKGROUND: '#FFFFFF' + } + +dark = { + ERRORTOKEN: ('s#FF0000','#FF8080',''), + DECORATOR_NAME: ('b','#FFBBAA',''), + DECORATOR: ('b','#CC5511',''), + ARGS: ('b','#DDDDFF',''), + NAME: ('','#DDDDDD',''), + NUMBER: ('','#FF0000',''), + OPERATOR: ('b','#FAF785',''), + MATH_OPERATOR: ('b','#FAF785',''), + BRACKETS: ('b','#FAF785',''), + COMMENT: ('','#45FCA0',''), + DOUBLECOMMENT: ('i','#A7C7A9',''), + CLASS_NAME: ('b','#B666FD',''), + DEF_NAME: ('b','#EBAE5C',''), + KEYWORD: ('b','#8680FF',''), + SINGLEQUOTE: ('','#F8BAFE',''), + SINGLEQUOTE_R: ('','#F8BAFE',''), + SINGLEQUOTE_U: ('','#F8BAFE',''), + DOUBLEQUOTE: ('','#FF80C0',''), + DOUBLEQUOTE_R: ('','#FF80C0',''), + DOUBLEQUOTE_U: ('','#FF80C0',''), + TRIPLESINGLEQUOTE: ('','#FF9595',''), + TRIPLESINGLEQUOTE_R: ('','#FF9595',''), + TRIPLESINGLEQUOTE_U: ('','#FF9595',''), + TRIPLEDOUBLEQUOTE: ('','#B3FFFF',''), + TRIPLEDOUBLEQUOTE_R: ('','#B3FFFF',''), + TRIPLEDOUBLEQUOTE_U: ('','#B3FFFF',''), + TEXT: ('','#FFFFFF',''), + LINENUMBER: ('>mi#555555','#bbccbb','#333333'), + PAGEBACKGROUND: '#000000' + } + +dark2 = { + ERRORTOKEN: ('','#FF0000',''), + DECORATOR_NAME: ('b','#FFBBAA',''), + DECORATOR: ('b','#CC5511',''), + ARGS: ('b','#DDDDDD',''), + NAME: ('','#C0C0C0',''), + NUMBER: ('b','#00FF00',''), + OPERATOR: ('b','#FF090F',''), + MATH_OPERATOR: ('b','#EE7020',''), + BRACKETS: ('b','#FFB90F',''), + COMMENT: ('i','#D0D000','#522000'),#'#88AA88','#11111F'), + DOUBLECOMMENT: ('i','#D0D000','#522000'),#'#77BB77','#11111F'), + CLASS_NAME: ('b','#DD4080',''), + DEF_NAME: ('b','#FF8040',''), + KEYWORD: ('b','#4726d1',''), + SINGLEQUOTE: ('','#8080C0',''), + SINGLEQUOTE_R: ('','#8080C0',''), + SINGLEQUOTE_U: ('','#8080C0',''), + DOUBLEQUOTE: ('','#ADB9F1',''), + DOUBLEQUOTE_R: ('','#ADB9F1',''), + DOUBLEQUOTE_U: ('','#ADB9F1',''), + TRIPLESINGLEQUOTE: ('','#00C1C1',''),#A050C0 + TRIPLESINGLEQUOTE_R: ('','#00C1C1',''),#A050C0 + TRIPLESINGLEQUOTE_U: ('','#00C1C1',''),#A050C0 + TRIPLEDOUBLEQUOTE: ('','#33E3E3',''),#B090E0 + TRIPLEDOUBLEQUOTE_R: ('','#33E3E3',''),#B090E0 + TRIPLEDOUBLEQUOTE_U: ('','#33E3E3',''),#B090E0 + TEXT: ('','#C0C0C0',''), + LINENUMBER: ('>mi#555555','#bbccbb','#333333'), + PAGEBACKGROUND: '#000000' + } + +lite = { + ERRORTOKEN: ('s#FF0000','#FF8080',''), + DECORATOR_NAME: ('b','#BB4422',''), + DECORATOR: ('b','#3333AF',''), + ARGS: ('b','#000000',''), + NAME: ('','#333333',''), + NUMBER: ('b','#DD2200',''), + OPERATOR: ('b','#000000',''), + MATH_OPERATOR: ('b','#000000',''), + BRACKETS: ('b','#000000',''), + COMMENT: ('','#007F00',''), + DOUBLECOMMENT: ('','#608060',''), + CLASS_NAME: ('b','#0000DF',''), + DEF_NAME: ('b','#9C7A00',''),#f09030 + KEYWORD: ('b','#0000AF',''), + SINGLEQUOTE: ('','#600080',''), + SINGLEQUOTE_R: ('','#600080',''), + SINGLEQUOTE_U: ('','#600080',''), + DOUBLEQUOTE: ('','#A0008A',''), + DOUBLEQUOTE_R: ('','#A0008A',''), + DOUBLEQUOTE_U: ('','#A0008A',''), + TRIPLESINGLEQUOTE: ('','#337799',''), + TRIPLESINGLEQUOTE_R: ('','#337799',''), + TRIPLESINGLEQUOTE_U: ('','#337799',''), + TRIPLEDOUBLEQUOTE: ('','#1166AA',''), + TRIPLEDOUBLEQUOTE_R: ('','#1166AA',''), + TRIPLEDOUBLEQUOTE_U: ('','#1166AA',''), + TEXT: ('','#000000',''), + LINENUMBER: ('>ti#555555','#000000',''), + PAGEBACKGROUND: '#FFFFFF' + } + +idle = { + ERRORTOKEN: ('s#FF0000','#FF8080',''), + DECORATOR_NAME: ('','#900090',''), + DECORATOR: ('','#FF7700',''), + NAME: ('','#000000',''), + NUMBER: ('','#000000',''), + OPERATOR: ('','#000000',''), + MATH_OPERATOR: ('','#000000',''), + BRACKETS: ('','#000000',''), + COMMENT: ('','#DD0000',''), + DOUBLECOMMENT: ('','#DD0000',''), + CLASS_NAME: ('','#0000FF',''), + DEF_NAME: ('','#0000FF',''), + KEYWORD: ('','#FF7700',''), + SINGLEQUOTE: ('','#00AA00',''), + SINGLEQUOTE_R: ('','#00AA00',''), + SINGLEQUOTE_U: ('','#00AA00',''), + DOUBLEQUOTE: ('','#00AA00',''), + DOUBLEQUOTE_R: ('','#00AA00',''), + DOUBLEQUOTE_U: ('','#00AA00',''), + TRIPLESINGLEQUOTE: ('','#00AA00',''), + TRIPLESINGLEQUOTE_R: ('','#00AA00',''), + TRIPLESINGLEQUOTE_U: ('','#00AA00',''), + TRIPLEDOUBLEQUOTE: ('','#00AA00',''), + TRIPLEDOUBLEQUOTE_R: ('','#00AA00',''), + TRIPLEDOUBLEQUOTE_U: ('','#00AA00',''), + TEXT: ('','#000000',''), + LINENUMBER: ('>ti#555555','#000000',''), + PAGEBACKGROUND: '#FFFFFF' + } + +pythonwin = { + ERRORTOKEN: ('s#FF0000','#FF8080',''), + DECORATOR_NAME: ('b','#DD0080',''), + DECORATOR: ('b','#000080',''), + ARGS: ('','#000000',''), + NAME: ('','#303030',''), + NUMBER: ('','#008080',''), + OPERATOR: ('','#000000',''), + MATH_OPERATOR: ('','#000000',''), + BRACKETS: ('','#000000',''), + COMMENT: ('','#007F00',''), + DOUBLECOMMENT: ('','#7F7F7F',''), + CLASS_NAME: ('b','#0000FF',''), + DEF_NAME: ('b','#007F7F',''), + KEYWORD: ('b','#000080',''), + SINGLEQUOTE: ('','#808000',''), + SINGLEQUOTE_R: ('','#808000',''), + SINGLEQUOTE_U: ('','#808000',''), + DOUBLEQUOTE: ('','#808000',''), + DOUBLEQUOTE_R: ('','#808000',''), + DOUBLEQUOTE_U: ('','#808000',''), + TRIPLESINGLEQUOTE: ('','#808000',''), + TRIPLESINGLEQUOTE_R: ('','#808000',''), + TRIPLESINGLEQUOTE_U: ('','#808000',''), + TRIPLEDOUBLEQUOTE: ('','#808000',''), + TRIPLEDOUBLEQUOTE_R: ('','#808000',''), + TRIPLEDOUBLEQUOTE_U: ('','#808000',''), + TEXT: ('','#303030',''), + LINENUMBER: ('>ti#555555','#000000',''), + PAGEBACKGROUND: '#FFFFFF' + } + +viewcvs = { + ERRORTOKEN: ('s#FF0000','#FF8080',''), + DECORATOR_NAME: ('','#000000',''), + DECORATOR: ('','#000000',''), + ARGS: ('','#000000',''), + NAME: ('','#000000',''), + NUMBER: ('','#000000',''), + OPERATOR: ('','#000000',''), + MATH_OPERATOR: ('','#000000',''), + BRACKETS: ('','#000000',''), + COMMENT: ('i','#b22222',''), + DOUBLECOMMENT: ('i','#b22222',''), + CLASS_NAME: ('','#000000',''), + DEF_NAME: ('b','#0000ff',''), + KEYWORD: ('b','#a020f0',''), + SINGLEQUOTE: ('b','#bc8f8f',''), + SINGLEQUOTE_R: ('b','#bc8f8f',''), + SINGLEQUOTE_U: ('b','#bc8f8f',''), + DOUBLEQUOTE: ('b','#bc8f8f',''), + DOUBLEQUOTE_R: ('b','#bc8f8f',''), + DOUBLEQUOTE_U: ('b','#bc8f8f',''), + TRIPLESINGLEQUOTE: ('b','#bc8f8f',''), + TRIPLESINGLEQUOTE_R: ('b','#bc8f8f',''), + TRIPLESINGLEQUOTE_U: ('b','#bc8f8f',''), + TRIPLEDOUBLEQUOTE: ('b','#bc8f8f',''), + TRIPLEDOUBLEQUOTE_R: ('b','#bc8f8f',''), + TRIPLEDOUBLEQUOTE_U: ('b','#bc8f8f',''), + TEXT: ('','#000000',''), + LINENUMBER: ('>ti#555555','#000000',''), + PAGEBACKGROUND: '#FFFFFF' + } + +defaultColors = lite + +def Usage(): + """ + ----------------------------------------------------------------------------- + PySourceColor.py ver: %s + ----------------------------------------------------------------------------- + Module summary: + This module is designed to colorize python source code. + Input--->python source + Output-->colorized (html, html4.01/css, xhtml1.0) + Standalone: + This module will work from the command line with options. + This module will work with redirected stdio. + Imported: + This module can be imported and used directly in your code. + ----------------------------------------------------------------------------- + Command line options: + -h, --help + Optional-> Display this help message. + -t, --test + Optional-> Will ignore all others flags but --profile + test all schemes and markup combinations + -p, --profile + Optional-> Works only with --test or -t + runs profile.py and makes the test work in quiet mode. + -i, --in, --input + Optional-> If you give input on stdin. + Use any of these for the current dir (.,cwd) + Input can be file or dir. + Input from stdin use one of the following (-,stdin) + If stdin is used as input stdout is output unless specified. + -o, --out, --output + Optional-> output dir for the colorized source. + default: output dir is the input dir. + To output html to stdout use one of the following (-,stdout) + Stdout can be used without stdin if you give a file as input. + -c, --color + Optional-> null, mono, dark, dark2, lite, idle, pythonwin, viewcvs + default: dark + -s, --show + Optional-> Show page after creation. + default: no show + -m, --markup + Optional-> html, css, xhtml + css, xhtml also support external stylesheets (-e,--external) + default: HTML + -e, --external + Optional-> use with css, xhtml + Writes an style sheet instead of embedding it in the page + saves it as pystyle.css in the same directory. + html markup will silently ignore this flag. + -H, --header + Opional-> add a page header to the top of the output + -H + Builtin header (name,date,hrule) + --header + You must specify a filename. + The header file must be valid html + and must handle its own font colors. + ex. --header c:/tmp/header.txt + -F, --footer + Opional-> add a page footer to the bottom of the output + -F + Builtin footer (hrule,name,date) + --footer + You must specify a filename. + The footer file must be valid html + and must handle its own font colors. + ex. --footer c:/tmp/footer.txt + -l, --linenumbers + Optional-> default is no linenumbers + Adds line numbers to the start of each line in the code. + --convertpage + Given a webpage that has code embedded in tags it will + convert embedded code to colorized html. + (see pageconvert for details) + ----------------------------------------------------------------------------- + Option usage: + # Test and show pages + python PySourceColor.py -t -s + # Test and only show profile results + python PySourceColor.py -t -p + # Colorize all .py,.pyw files in cwdir you can also use: (.,cwd) + python PySourceColor.py -i . + # Using long options w/ = + python PySourceColor.py --in=c:/myDir/my.py --color=lite --show + # Using short options w/out = + python PySourceColor.py -i c:/myDir/ -c idle -m css -e + # Using any mix + python PySourceColor.py --in . -o=c:/myDir --show + # Place a custom header on your files + python PySourceColor.py -i . -o c:/tmp -m xhtml --header c:/header.txt + ----------------------------------------------------------------------------- + Stdio usage: + # Stdio using no options + python PySourceColor.py < c:/MyFile.py > c:/tmp/MyFile.html + # Using stdin alone automatically uses stdout for output: (stdin,-) + python PySourceColor.py -i- < c:/MyFile.py > c:/tmp/myfile.html + # Stdout can also be written to directly from a file instead of stdin + python PySourceColor.py -i c:/MyFile.py -m css -o- > c:/tmp/myfile.html + # Stdin can be used as input , but output can still be specified + python PySourceColor.py -i- -o c:/pydoc.py.html -s < c:/Python22/my.py + _____________________________________________________________________________ + """ + print Usage.__doc__% (__version__) + sys.exit(1) + +###################################################### Command line interface + +def cli(): + """Handle command line args and redirections""" + try: + # try to get command line args + opts, args = getopt.getopt(sys.argv[1:], + "hseqtplHFi:o:c:m:h:f:",["help", "show", "quiet", + "test", "external", "linenumbers", "convertpage", "profile", + "input=", "output=", "color=", "markup=","header=", "footer="]) + except getopt.GetoptError: + # on error print help information and exit: + Usage() + # init some names + input = None + output = None + colorscheme = None + markup = 'html' + header = None + footer = None + linenumbers = 0 + show = 0 + quiet = 0 + test = 0 + profile = 0 + convertpage = 0 + form = None + # if we have args then process them + for o, a in opts: + if o in ["-h", "--help"]: + Usage() + sys.exit() + if o in ["-o", "--output", "--out"]: + output = a + if o in ["-i", "--input", "--in"]: + input = a + if input in [".", "cwd"]: + input = os.getcwd() + if o in ["-s", "--show"]: + show = 1 + if o in ["-q", "--quiet"]: + quiet = 1 + if o in ["-t", "--test"]: + test = 1 + if o in ["--convertpage"]: + convertpage = 1 + if o in ["-p", "--profile"]: + profile = 1 + if o in ["-e", "--external"]: + form = 'external' + if o in ["-m", "--markup"]: + markup = str(a) + if o in ["-l", "--linenumbers"]: + linenumbers = 1 + if o in ["--header"]: + header = str(a) + elif o == "-H": + header = '' + if o in ["--footer"]: + footer = str(a) + elif o == "-F": + footer = '' + if o in ["-c", "--color"]: + try: + colorscheme = globals().get(a.lower()) + except: + traceback.print_exc() + Usage() + if test: + if profile: + import profile + profile.run('_test(show=%s, quiet=%s)'%(show,quiet)) + else: + # Parse this script in every possible colorscheme and markup + _test(show,quiet) + elif input in [None, "-", "stdin"] or output in ["-", "stdout"]: + # determine if we are going to use stdio + if input not in [None, "-", "stdin"]: + if os.path.isfile(input) : + path2stdout(input, colors=colorscheme, markup=markup, + linenumbers=linenumbers, header=header, + footer=footer, form=form) + else: + raise PathError, 'File does not exists!' + else: + try: + if sys.stdin.isatty(): + raise InputError, 'Please check input!' + else: + if output in [None,"-","stdout"]: + str2stdout(sys.stdin.read(), colors=colorscheme, + markup=markup, header=header, + footer=footer, linenumbers=linenumbers, + form=form) + else: + str2file(sys.stdin.read(), outfile=output, show=show, + markup=markup, header=header, footer=footer, + linenumbers=linenumbers, form=form) + except: + traceback.print_exc() + Usage() + else: + if os.path.exists(input): + if convertpage: + # if there was at least an input given we can proceed + pageconvert(input, out=output, colors=colorscheme, + show=show, markup=markup,linenumbers=linenumbers) + else: + # if there was at least an input given we can proceed + convert(source=input, outdir=output, colors=colorscheme, + show=show, markup=markup, quiet=quiet, header=header, + footer=footer, linenumbers=linenumbers, form=form) + else: + raise PathError, 'File does not exists!' + Usage() + +######################################################### Simple markup tests + +def _test(show=0, quiet=0): + """Test the parser and most of the functions. + + There are 19 test total(eight colorschemes in three diffrent markups, + and a str2file test. Most functions are tested by this. + """ + fi = sys.argv[0] + if not fi.endswith('.exe'):# Do not test if frozen as an archive + # this is a collection of test, most things are covered. + path2file(fi, '/tmp/null.html', null, show=show, quiet=quiet) + path2file(fi, '/tmp/null_css.html', null, show=show, + markup='css', quiet=quiet) + path2file(fi, '/tmp/mono.html', mono, show=show, quiet=quiet) + path2file(fi, '/tmp/mono_css.html', mono, show=show, + markup='css', quiet=quiet) + path2file(fi, '/tmp/lite.html', lite, show=show, quiet=quiet) + path2file(fi, '/tmp/lite_css.html', lite, show=show, + markup='css', quiet=quiet, header='', footer='', + linenumbers=1) + path2file(fi, '/tmp/lite_xhtml.html', lite, show=show, + markup='xhtml', quiet=quiet) + path2file(fi, '/tmp/dark.html', dark, show=show, quiet=quiet) + path2file(fi, '/tmp/dark_css.html', dark, show=show, + markup='css', quiet=quiet, linenumbers=1) + path2file(fi, '/tmp/dark2.html', dark2, show=show, quiet=quiet) + path2file(fi, '/tmp/dark2_css.html', dark2, show=show, + markup='css', quiet=quiet) + path2file(fi, '/tmp/dark2_xhtml.html', dark2, show=show, + markup='xhtml', quiet=quiet, header='', footer='', + linenumbers=1, form='external') + path2file(fi, '/tmp/idle.html', idle, show=show, quiet=quiet) + path2file(fi, '/tmp/idle_css.html', idle, show=show, + markup='css', quiet=quiet) + path2file(fi, '/tmp/viewcvs.html', viewcvs, show=show, + quiet=quiet, linenumbers=1) + path2file(fi, '/tmp/viewcvs_css.html', viewcvs, show=show, + markup='css', linenumbers=1, quiet=quiet) + path2file(fi, '/tmp/pythonwin.html', pythonwin, show=show, + quiet=quiet) + path2file(fi, '/tmp/pythonwin_css.html', pythonwin, show=show, + markup='css', quiet=quiet) + teststr=r'''"""This is a test of decorators and other things""" +# This should be line 421... +@whatever(arg,arg2) +@A @B(arghh) @C +def LlamaSaysNi(arg='Ni!',arg2="RALPH"): + """This docstring is deeply disturbed by all the llama references""" + print '%s The Wonder Llama says %s'% (arg2,arg) +# So I was like duh!, and he was like ya know?!, +# and so we were both like huh...wtf!? RTFM!! LOL!!;) +@staticmethod## Double comments are KewL. +def LlamasRLumpy(): + """This docstring is too sexy to be here. + """ + u""" +============================= +A Møøse once bit my sister... +============================= + """ + ## Relax, this won't hurt a bit, just a simple, painless procedure, + ## hold still while I get the anesthetizing hammer. + m = {'three':'1','won':'2','too':'3'} + o = r'fishy\fishy\fishy/fish\oh/where/is\my/little\..' + python = uR""" + No realli! She was Karving her initials øn the møøse with the sharpened end + of an interspace tøøthbrush given her by Svenge - her brother-in-law -an Oslo + dentist and star of many Norwegian møvies: "The Høt Hands of an Oslo + Dentist", "Fillings of Passion", "The Huge Mølars of Horst Nordfink"...""" + RU"""142 MEXICAN WHOOPING LLAMAS"""#<-Can you fit 142 llamas in a red box? + n = u' HERMSGERVØRDENBRØTBØRDA ' + """ YUTTE """ + t = """SAMALLNIATNUOMNAIRODAUCE"""+"DENIARTYLLAICEPS04" + ## We apologise for the fault in the + ## comments. Those responsible have been + ## sacked. + y = '14 NORTH CHILEAN GUANACOS \ +(CLOSELY RELATED TO THE LLAMA)' + rules = [0,1,2,3,4,5] + print y''' + htmlPath = os.path.abspath('/tmp/strtest_lines.html') + str2file(teststr, htmlPath, colors=dark, markup='xhtml', + linenumbers=420, show=show) + _printinfo(" wrote %s" % htmlPath, quiet) + htmlPath = os.path.abspath('/tmp/strtest_nolines.html') + str2file(teststr, htmlPath, colors=dark, markup='xhtml', + show=show) + _printinfo(" wrote %s" % htmlPath, quiet) + else: + Usage() + return +####################################################### User funtctions + +def str2stdout(sourcestring, colors=None, title='', markup='html', + header=None, footer=None, + linenumbers=0, form=None): + """Converts a code(string) to colorized HTML. Writes to stdout. + + form='code',or'snip' (for "
yourcode
" only) + colors=null,mono,lite,dark,dark2,idle,or pythonwin + """ + Parser(sourcestring, colors=colors, title=title, markup=markup, + header=header, footer=footer, + linenumbers=linenumbers).format(form) + +def path2stdout(sourcepath, title='', colors=None, markup='html', + header=None, footer=None, + linenumbers=0, form=None): + """Converts code(file) to colorized HTML. Writes to stdout. + + form='code',or'snip' (for "
yourcode
" only) + colors=null,mono,lite,dark,dark2,idle,or pythonwin + """ + sourcestring = open(sourcepath).read() + Parser(sourcestring, colors=colors, title=sourcepath, + markup=markup, header=header, footer=footer, + linenumbers=linenumbers).format(form) + +def str2html(sourcestring, colors=None, title='', + markup='html', header=None, footer=None, + linenumbers=0, form=None): + """Converts a code(string) to colorized HTML. Returns an HTML string. + + form='code',or'snip' (for "
yourcode
" only) + colors=null,mono,lite,dark,dark2,idle,or pythonwin + """ + stringIO = StringIO.StringIO() + Parser(sourcestring, colors=colors, title=title, out=stringIO, + markup=markup, header=header, footer=footer, + linenumbers=linenumbers).format(form) + stringIO.seek(0) + return stringIO.read() + +def str2css(sourcestring, colors=None, title='', + markup='css', header=None, footer=None, + linenumbers=0, form=None): + """Converts a code string to colorized CSS/HTML. Returns CSS/HTML string + + If form != None then this will return (stylesheet_str, code_str) + colors=null,mono,lite,dark,dark2,idle,or pythonwin + """ + if markup.lower() not in ['css' ,'xhtml']: + markup = 'css' + stringIO = StringIO.StringIO() + parse = Parser(sourcestring, colors=colors, title=title, + out=stringIO, markup=markup, + header=header, footer=footer, + linenumbers=linenumbers) + parse.format(form) + stringIO.seek(0) + if form != None: + return parse._sendCSSStyle(external=1), stringIO.read() + else: + return None, stringIO.read() + +def str2markup(sourcestring, colors=None, title = '', + markup='xhtml', header=None, footer=None, + linenumbers=0, form=None): + """ Convert code strings into ([stylesheet or None], colorized string) """ + if markup.lower() == 'html': + return None, str2html(sourcestring, colors=colors, title=title, + header=header, footer=footer, markup=markup, + linenumbers=linenumbers, form=form) + else: + return str2css(sourcestring, colors=colors, title=title, + header=header, footer=footer, markup=markup, + linenumbers=linenumbers, form=form) + +def str2file(sourcestring, outfile, colors=None, title='', + markup='html', header=None, footer=None, + linenumbers=0, show=0, dosheet=1, form=None): + """Converts a code string to a file. + + makes no attempt at correcting bad pathnames + """ + css , html = str2markup(sourcestring, colors=colors, title='', + markup=markup, header=header, footer=footer, + linenumbers=linenumbers, form=form) + # write html + f = open(outfile,'wt') + f.writelines(html) + f.close() + #write css + if css != None and dosheet: + dir = os.path.dirname(outfile) + outcss = os.path.join(dir,'pystyle.css') + f = open(outcss,'wt') + f.writelines(css) + f.close() + if show: + showpage(outfile) + +def path2html(sourcepath, colors=None, markup='html', + header=None, footer=None, + linenumbers=0, form=None): + """Converts code(file) to colorized HTML. Returns an HTML string. + + form='code',or'snip' (for "
yourcode
" only) + colors=null,mono,lite,dark,dark2,idle,or pythonwin + """ + stringIO = StringIO.StringIO() + sourcestring = open(sourcepath).read() + Parser(sourcestring, colors, title=sourcepath, out=stringIO, + markup=markup, header=header, footer=footer, + linenumbers=linenumbers).format(form) + stringIO.seek(0) + return stringIO.read() + +def convert(source, outdir=None, colors=None, + show=0, markup='html', quiet=0, + header=None, footer=None, linenumbers=0, form=None): + """Takes a file or dir as input and places the html in the outdir. + + If outdir is none it defaults to the input dir + """ + count=0 + # If it is a filename then path2file + if not os.path.isdir(source): + if os.path.isfile(source): + count+=1 + path2file(source, outdir, colors, show, markup, + quiet, form, header, footer, linenumbers, count) + else: + raise PathError, 'File does not exist!' + # If we pass in a dir we need to walkdir for files. + # Then we need to colorize them with path2file + else: + fileList = walkdir(source) + if fileList != None: + # make sure outdir is a dir + if outdir != None: + if os.path.splitext(outdir)[1] != '': + outdir = os.path.split(outdir)[0] + for item in fileList: + count+=1 + path2file(item, outdir, colors, show, markup, + quiet, form, header, footer, linenumbers, count) + _printinfo('Completed colorizing %s files.'%str(count), quiet) + else: + _printinfo("No files to convert in dir.", quiet) + +def path2file(sourcePath, out=None, colors=None, show=0, + markup='html', quiet=0, form=None, + header=None, footer=None, linenumbers=0, count=1): + """ Converts python source to html file""" + # If no outdir is given we use the sourcePath + if out == None:#this is a guess + htmlPath = sourcePath + '.html' + else: + # If we do give an out_dir, and it does + # not exist , it will be created. + if os.path.splitext(out)[1] == '': + if not os.path.isdir(out): + os.makedirs(out) + sourceName = os.path.basename(sourcePath) + htmlPath = os.path.join(out,sourceName)+'.html' + # If we do give an out_name, and its dir does + # not exist , it will be created. + else: + outdir = os.path.split(out)[0] + if not os.path.isdir(outdir): + os.makedirs(outdir) + htmlPath = out + htmlPath = os.path.abspath(htmlPath) + # Open the text and do the parsing. + source = open(sourcePath).read() + parse = Parser(source, colors, sourcePath, open(htmlPath, 'wt'), + markup, header, footer, linenumbers) + parse.format(form) + _printinfo(" wrote %s" % htmlPath, quiet) + # html markup will ignore the external flag, but + # we need to stop the blank file from being written. + if form == 'external' and count == 1 and markup != 'html': + cssSheet = parse._sendCSSStyle(external=1) + cssPath = os.path.join(os.path.dirname(htmlPath),'pystyle.css') + css = open(cssPath, 'wt') + css.write(cssSheet) + css.close() + _printinfo(" wrote %s" % cssPath, quiet) + if show: + # load HTML page into the default web browser. + showpage(htmlPath) + return htmlPath + +def tagreplace(sourcestr, colors=lite, markup='xhtml', + linenumbers=0, dosheet=1, tagstart=''.lower(), + tagend=''.lower(), stylesheet='pystyle.css'): + """This is a helper function for pageconvert. Returns css, page. + """ + if markup.lower() != 'html': + link = '' + css = link%stylesheet + if sourcestr.find(css) == -1: + sourcestr = sourcestr.replace('', css, 1) + starttags = sourcestr.count(tagstart) + endtags = sourcestr.count(tagend) + if starttags: + if starttags == endtags: + for _ in range(starttags): + datastart = sourcestr.find(tagstart) + dataend = sourcestr.find(tagend) + data = sourcestr[datastart+len(tagstart):dataend] + data = unescape(data) + css , data = str2markup(data, colors=colors, + linenumbers=linenumbers, markup=markup, form='embed') + start = sourcestr[:datastart] + end = sourcestr[dataend+len(tagend):] + sourcestr = ''.join([start,data,end]) + else: + raise InputError,'Tag mismatch!\nCheck %s,%s tags'%tagstart,tagend + if not dosheet: + css = None + return css, sourcestr + +def pageconvert(path, out=None, colors=lite, markup='xhtml', linenumbers=0, + dosheet=1, tagstart=''.lower(), tagend=''.lower(), + stylesheet='pystyle', show=1, returnstr=0): + """This function can colorize Python source + + that is written in a webpage enclosed in tags. + """ + if out == None: + out = os.path.dirname(path) + infile = open(path, 'r').read() + css,page = tagreplace(sourcestr=infile,colors=colors, + markup=markup, linenumbers=linenumbers, dosheet=dosheet, + tagstart=tagstart, tagend=tagend, stylesheet=stylesheet) + if not returnstr: + newpath = os.path.abspath(os.path.join( + out,'tmp', os.path.basename(path))) + if not os.path.exists(newpath): + try: + os.makedirs(os.path.dirname(newpath)) + except: + pass#traceback.print_exc() + #Usage() + y = open(newpath, 'w') + y.write(page) + y.close() + if css: + csspath = os.path.abspath(os.path.join( + out,'tmp','%s.css'%stylesheet)) + x = open(csspath,'w') + x.write(css) + x.close() + if show: + try: + os.startfile(newpath) + except: + traceback.print_exc() + return newpath + else: + return css, page + +##################################################################### helpers + +def walkdir(dir): + """Return a list of .py and .pyw files from a given directory. + + This function can be written as a generator Python 2.3, or a genexp + in Python 2.4. But 2.2 and 2.1 would be left out.... + """ + # Get a list of files that match *.py* + GLOB_PATTERN = os.path.join(dir, "*.[p][y]*") + pathlist = glob.glob(GLOB_PATTERN) + # Now filter out all but py and pyw + filterlist = [x for x in pathlist + if x.endswith('.py') + or x.endswith('.pyw')] + if filterlist != []: + # if we have a list send it + return filterlist + else: + return None + +def showpage(path): + """Helper function to open webpages""" + try: + webbrowser.open_new(os.path.abspath(path)) + except: + traceback.print_exc() + +def _printinfo(message, quiet): + """Helper to print messages""" + if not quiet: + print message + +def escape(text): + """escape text for html. similar to cgi.escape""" + text = text.replace("&", "&") + text = text.replace("<", "<") + text = text.replace(">", ">") + return text + +def unescape(text): + """unsecape escaped text""" + text = text.replace(""", '"') + text = text.replace(">", ">") + text = text.replace("<", "<") + text = text.replace("&", "&") + return text + +########################################################### Custom Exceptions + +class PySourceColorError(Exception): + # Base for custom errors + def __init__(self, msg=''): + self._msg = msg + Exception.__init__(self, msg) + def __repr__(self): + return self._msg + __str__ = __repr__ + +class PathError(PySourceColorError): + def __init__(self, msg): + PySourceColorError.__init__(self, + 'Path error! : %s'% msg) + +class InputError(PySourceColorError): + def __init__(self, msg): + PySourceColorError.__init__(self, + 'Input error! : %s'% msg) + +########################################################## Python code parser + +class Parser(object): + + """MoinMoin python parser heavily chopped :)""" + + def __init__(self, raw, colors=None, title='', out=sys.stdout, + markup='html', header=None, footer=None, linenumbers=0): + """Store the source text & set some flags""" + if colors == None: + colors = defaultColors + self.raw = raw.expandtabs().rstrip() + self.title = os.path.basename(title) + self.out = out + self.line = '' + self.lasttext = '' + self.argFlag = 0 + self.classFlag = 0 + self.defFlag = 0 + self.decoratorFlag = 0 + self.external = 0 + self.markup = markup.upper() + self.colors = colors + self.header = header + self.footer = footer + self.doArgs = 1 # overrides the new tokens + self.doNames = 1 # overrides the new tokens + self.doMathOps = 1 # overrides the new tokens + self.doBrackets = 1 # overrides the new tokens + self.doURL = 1 # override url conversion + self.LINENUMHOLDER = "___line___".upper() + self.LINESTART = "___start___".upper() + self.skip = 0 + # add space left side of code for padding.Override in color dict. + self.extraspace = self.colors.get(EXTRASPACE, '') + # Linenumbers less then zero also have numberlinks + self.dolinenums = self.linenum = abs(linenumbers) + if linenumbers < 0: + self.numberlinks = 1 + else: + self.numberlinks = 0 + + def format(self, form=None): + """Parse and send the colorized source""" + if form in ('snip','code'): + self.addEnds = 0 + elif form == 'embed': + self.addEnds = 0 + self.external = 1 + else: + if form == 'external': + self.external = 1 + self.addEnds = 1 + + # Store line offsets in self.lines + self.lines = [0, 0] + pos = 0 + + # Add linenumbers + if self.dolinenums: + start=self.LINENUMHOLDER+' '+self.extraspace + else: + start=''+self.extraspace + newlines = [] + lines = self.raw.splitlines(0) + for l in lines: + # span and div escape for customizing and embedding raw text + if (l.startswith('#$#') + or l.startswith('#%#') + or l.startswith('#@#')): + newlines.append(l) + else: + # kludge for line spans in css,xhtml + if self.markup in ['XHTML','CSS']: + newlines.append(self.LINESTART+' '+start+l) + else: + newlines.append(start+l) + self.raw = "\n".join(newlines)+'\n'# plus an extra newline at the end + + # Gather lines + while 1: + pos = self.raw.find('\n', pos) + 1 + if not pos: break + self.lines.append(pos) + self.lines.append(len(self.raw)) + + # Wrap text in a filelike object + self.pos = 0 + text = StringIO.StringIO(self.raw) + + # Markup start + if self.addEnds: + self._doPageStart() + else: + self._doSnippetStart() + + ## Tokenize calls the __call__ + ## function for each token till done. + # Parse the source and write out the results. + try: + tokenize.tokenize(text.readline, self) + except tokenize.TokenError, ex: + msg = ex[0] + line = ex[1][0] + self.out.write("

ERROR: %s

%s\n"% + (msg, self.raw[self.lines[line]:])) + traceback.print_exc() + + # Markup end + if self.addEnds: + self._doPageEnd() + else: + self._doSnippetEnd() + + def __call__(self, toktype, toktext, (srow,scol), (erow,ecol), line): + """Token handler. Order is important do not rearrange.""" + self.line = line + # Calculate new positions + oldpos = self.pos + newpos = self.lines[srow] + scol + self.pos = newpos + len(toktext) + # Handle newlines + if toktype in (token.NEWLINE, tokenize.NL): + self.decoratorFlag = self.argFlag = 0 + # kludge for line spans in css,xhtml + if self.markup in ['XHTML','CSS']: + self.out.write('') + self.out.write('\n') + return + + # Send the original whitespace, and tokenize backslashes if present. + # Tokenizer.py just sends continued line backslashes with whitespace. + # This is a hack to tokenize continued line slashes as operators. + # Should continued line backslashes be treated as operators + # or some other token? + + if newpos > oldpos: + if self.raw[oldpos:newpos].isspace(): + # consume a single space after linestarts and linenumbers + # had to have them so tokenizer could seperate them. + # multiline strings are handled by do_Text functions + if self.lasttext != self.LINESTART \ + and self.lasttext != self.LINENUMHOLDER: + self.out.write(self.raw[oldpos:newpos]) + else: + self.out.write(self.raw[oldpos+1:newpos]) + else: + slash = self.raw[oldpos:newpos].find('\\')+oldpos + self.out.write(self.raw[oldpos:slash]) + getattr(self, '_send%sText'%(self.markup))(OPERATOR, '\\') + self.linenum+=1 + # kludge for line spans in css,xhtml + if self.markup in ['XHTML','CSS']: + self.out.write('') + self.out.write(self.raw[slash+1:newpos]) + + # Skip indenting tokens + if toktype in (token.INDENT, token.DEDENT): + self.pos = newpos + return + + # Look for operators + if token.LPAR <= toktype and toktype <= token.OP: + # Trap decorators py2.4 > + if toktext == '@': + toktype = DECORATOR + # Set a flag if this was the decorator start so + # the decorator name and arguments can be identified + self.decoratorFlag = self.argFlag = 1 + else: + if self.doArgs: + # Find the start for arguments + if toktext == '(' and self.argFlag: + self.argFlag = 2 + # Find the end for arguments + elif toktext == ':': + self.argFlag = 0 + ## Seperate the diffrent operator types + # Brackets + if self.doBrackets and toktext in ['[',']','(',')','{','}']: + toktype = BRACKETS + # Math operators + elif self.doMathOps and toktext in ['*=','**=','-=','+=','|=', + '%=','>>=','<<=','=','^=', + '/=', '+','-','**','*','/','%']: + toktype = MATH_OPERATOR + # Operator + else: + toktype = OPERATOR + # example how flags should work. + # def fun(arg=argvalue,arg2=argvalue2): + # 0 1 2 A 1 N 2 A 1 N 0 + if toktext == "=" and self.argFlag == 2: + self.argFlag = 1 + elif toktext == "," and self.argFlag == 1: + self.argFlag = 2 + # Look for keywords + elif toktype == NAME and keyword.iskeyword(toktext): + toktype = KEYWORD + # Set a flag if this was the class / def start so + # the class / def name and arguments can be identified + if toktext in ['class', 'def']: + if toktext =='class' and \ + not line[:line.find('class')].endswith('.'): + self.classFlag = self.argFlag = 1 + elif toktext == 'def' and \ + not line[:line.find('def')].endswith('.'): + self.defFlag = self.argFlag = 1 + else: + # must have used a keyword as a name i.e. self.class + toktype = ERRORTOKEN + + # Look for class, def, decorator name + elif (self.classFlag or self.defFlag or self.decoratorFlag) \ + and self.doNames: + if self.classFlag: + self.classFlag = 0 + toktype = CLASS_NAME + elif self.defFlag: + self.defFlag = 0 + toktype = DEF_NAME + elif self.decoratorFlag: + self.decoratorFlag = 0 + toktype = DECORATOR_NAME + + # Look for strings + # Order of evaluation is important do not change. + elif toktype == token.STRING: + text = toktext.lower() + # TRIPLE DOUBLE QUOTE's + if (text[:3] == '"""'): + toktype = TRIPLEDOUBLEQUOTE + elif (text[:4] == 'r"""'): + toktype = TRIPLEDOUBLEQUOTE_R + elif (text[:4] == 'u"""' or + text[:5] == 'ur"""'): + toktype = TRIPLEDOUBLEQUOTE_U + # DOUBLE QUOTE's + elif (text[:1] == '"'): + toktype = DOUBLEQUOTE + elif (text[:2] == 'r"'): + toktype = DOUBLEQUOTE_R + elif (text[:2] == 'u"' or + text[:3] == 'ur"'): + toktype = DOUBLEQUOTE_U + # TRIPLE SINGLE QUOTE's + elif (text[:3] == "'''"): + toktype = TRIPLESINGLEQUOTE + elif (text[:4] == "r'''"): + toktype = TRIPLESINGLEQUOTE_R + elif (text[:4] == "u'''" or + text[:5] == "ur'''"): + toktype = TRIPLESINGLEQUOTE_U + # SINGLE QUOTE's + elif (text[:1] == "'"): + toktype = SINGLEQUOTE + elif (text[:2] == "r'"): + toktype = SINGLEQUOTE_R + elif (text[:2] == "u'" or + text[:3] == "ur'"): + toktype = SINGLEQUOTE_U + + # test for invalid string declaration + if self.lasttext.lower() == 'ru': + toktype = ERRORTOKEN + + # Look for comments + elif toktype == COMMENT: + if toktext[:2] == "##": + toktype = DOUBLECOMMENT + elif toktext[:3] == '#$#': + toktype = TEXT + self.textFlag = 'SPAN' + toktext = toktext[3:] + elif toktext[:3] == '#%#': + toktype = TEXT + self.textFlag = 'DIV' + toktext = toktext[3:] + elif toktext[:3] == '#@#': + toktype = TEXT + self.textFlag = 'RAW' + toktext = toktext[3:] + if self.doURL: + # this is a 'fake helper function' + # url(URI,Alias_name) or url(URI) + url_pos = toktext.find('url(') + if url_pos != -1: + before = toktext[:url_pos] + url = toktext[url_pos+4:] + splitpoint = url.find(',') + endpoint = url.find(')') + after = url[endpoint+1:] + url = url[:endpoint] + if splitpoint != -1: + urlparts = url.split(',',1) + toktext = '%s%s%s'%( + before,urlparts[0],urlparts[1].lstrip(),after) + else: + toktext = '%s%s%s'%(before,url,url,after) + + # Seperate errors from decorators + elif toktype == ERRORTOKEN: + # Bug fix for < py2.4 + # space between decorators + if self.argFlag and toktext.isspace(): + #toktype = NAME + self.out.write(toktext) + return + # Bug fix for py2.2 linenumbers with decorators + elif toktext.isspace(): + # What if we have a decorator after a >>> or ... + #p = line.find('@') + #if p >= 0 and not line[:p].isspace(): + #self.out.write(toktext) + #return + if self.skip: + self.skip=0 + return + else: + self.out.write(toktext) + return + # trap decorators < py2.4 + elif toktext == '@': + toktype = DECORATOR + # Set a flag if this was the decorator start so + # the decorator name and arguments can be identified + self.decoratorFlag = self.argFlag = 1 + + # Seperate args from names + elif (self.argFlag == 2 and + toktype == NAME and + toktext != 'None' and + self.doArgs): + toktype = ARGS + + # Look for line numbers + # The conversion code for them is in the send_text functions. + if toktext in [self.LINENUMHOLDER,self.LINESTART]: + toktype = LINENUMBER + # if we don't have linenumbers set flag + # to skip the trailing space from linestart + if toktext == self.LINESTART and not self.dolinenums \ + or toktext == self.LINENUMHOLDER: + self.skip=1 + + + # Skip blank token that made it thru + ## bugfix for the last empty tag. + if toktext == '': + return + + # Last token text history + self.lasttext = toktext + + # escape all but the urls in the comments + if toktype in (DOUBLECOMMENT, COMMENT): + if toktext.find('\n') + else: + # Start of css/xhtml snippet + self.out.write(self.colors.get(CODESTART,'
\n'))
+
+    def _doSnippetEnd(self):
+        # End of html snippet
+        self.out.write(self.colors.get(CODEEND,'
\n')) + + ######################################################## markup selectors + + def _getFile(self, filepath): + try: + _file = open(filepath,'r') + content = _file.read() + _file.close() + except: + traceback.print_exc() + content = '' + return content + + def _doPageStart(self): + getattr(self, '_do%sStart'%(self.markup))() + + def _doPageHeader(self): + if self.header != None: + if self.header.find('#$#') != -1 or \ + self.header.find('#$#') != -1 or \ + self.header.find('#%#') != -1: + self.out.write(self.header[3:]) + else: + if self.header != '': + self.header = self._getFile(self.header) + getattr(self, '_do%sHeader'%(self.markup))() + + def _doPageFooter(self): + if self.footer != None: + if self.footer.find('#$#') != -1 or \ + self.footer.find('#@#') != -1 or \ + self.footer.find('#%#') != -1: + self.out.write(self.footer[3:]) + else: + if self.footer != '': + self.footer = self._getFile(self.footer) + getattr(self, '_do%sFooter'%(self.markup))() + + def _doPageEnd(self): + getattr(self, '_do%sEnd'%(self.markup))() + + ################################################### color/style retrieval + ## Some of these are not used anymore but are kept for documentation + + def _getLineNumber(self): + num = self.linenum + self.linenum+=1 + return str(num).rjust(5)+" " + + def _getTags(self, key): + # style tags + return self.colors.get(key, self.colors[NAME])[0] + + def _getForeColor(self, key): + # get text foreground color, if not set to black + color = self.colors.get(key, self.colors[NAME])[1] + if color[:1] != '#': + color = '#000000' + return color + + def _getBackColor(self, key): + # get text background color + return self.colors.get(key, self.colors[NAME])[2] + + def _getPageColor(self): + # get page background color + return self.colors.get(PAGEBACKGROUND, '#FFFFFF') + + def _getStyle(self, key): + # get the token style from the color dictionary + return self.colors.get(key, self.colors[NAME]) + + def _getMarkupClass(self, key): + # get the markup class name from the markup dictionary + return MARKUPDICT.get(key, MARKUPDICT[NAME]) + + def _getDocumentCreatedBy(self): + return '\n'%( + __title__,__version__,time.ctime()) + + ################################################### HTML markup functions + + def _doHTMLStart(self): + # Start of html page + self.out.write('\n') + self.out.write('%s\n'%(self.title)) + self.out.write(self._getDocumentCreatedBy()) + self.out.write('\n') + # Get background + self.out.write('\n'%self._getPageColor()) + self._doPageHeader() + self.out.write('
')
+
+    def _getHTMLStyles(self, toktype, toktext):
+        # Get styles
+        tags, color = self.colors.get(toktype, self.colors[NAME])[:2]#
+        tagstart=[]
+        tagend=[]
+        # check for styles and set them if needed.
+        if 'b' in tags:#Bold
+            tagstart.append('')
+            tagend.append('')
+        if 'i' in tags:#Italics
+            tagstart.append('')
+            tagend.append('')
+        if 'u' in tags:#Underline
+            tagstart.append('')
+            tagend.append('')
+        # HTML tags should be paired like so : Doh!
+        tagend.reverse()
+        starttags="".join(tagstart)
+        endtags="".join(tagend)
+        return starttags,endtags,color
+
+    def _sendHTMLText(self, toktype, toktext):
+        numberlinks = self.numberlinks
+        
+        # If it is an error, set a red box around the bad tokens
+        # older browsers should ignore it
+        if toktype == ERRORTOKEN:
+            style = ' style="border: solid 1.5pt #FF0000;"'
+        else:
+            style = ''
+        # Get styles
+        starttag, endtag, color = self._getHTMLStyles(toktype, toktext)
+        # This is a hack to 'fix' multi-line  strings.
+        # Multi-line strings are treated as only one token 
+        # even though they can be several physical lines.
+        # That makes it hard to spot the start of a line,
+        # because at this level all we know about are tokens.
+        
+        if toktext.count(self.LINENUMHOLDER):
+            # rip apart the string and separate it by line.
+            # count lines and change all linenum token to line numbers.
+            # embedded all the new font tags inside the current one.
+            # Do this by ending the tag first then writing our new tags,
+            # then starting another font tag exactly like the first one.
+            if toktype == LINENUMBER:
+                splittext = toktext.split(self.LINENUMHOLDER)
+            else:    
+                splittext = toktext.split(self.LINENUMHOLDER+' ')
+            store = []
+            store.append(splittext.pop(0))
+            lstarttag, lendtag, lcolor = self._getHTMLStyles(LINENUMBER, toktext)
+            count = len(splittext)
+            for item in splittext:
+                num =  self._getLineNumber()
+                if numberlinks:
+                    numstrip = num.strip()
+                    content = '%s' \
+                              %(numstrip,numstrip,num)
+                else:
+                    content = num
+                if count <= 1:
+                    endtag,starttag = '',''
+                linenumber = ''.join([endtag,'',
+                            lstarttag, content, lendtag, '' ,starttag])
+                store.append(linenumber+item)
+            toktext = ''.join(store)
+        # send text
+        ## Output optimization
+        # skip font tag if black text, but styles will still be sent. (b,u,i)
+        if color !='#000000':
+            startfont = ''%(color, style)
+            endfont = ''
+        else:
+            startfont, endfont = ('','')
+        if toktype != LINENUMBER:
+            self.out.write(''.join([startfont,starttag,
+                                     toktext,endtag,endfont]))
+        else:
+            self.out.write(toktext)
+        return
+
+    def _doHTMLHeader(self):
+        # Optional
+        if self.header != '':
+            self.out.write('%s\n'%self.header)
+        else:
+            color = self._getForeColor(NAME)
+            self.out.write('# %s \
+                            
# %s

\n'% + (color, self.title, time.ctime())) + + def _doHTMLFooter(self): + # Optional + if self.footer != '': + self.out.write('%s\n'%self.footer) + else: + color = self._getForeColor(NAME) + self.out.write(' \ +
# %s
# %s
\n'% + (color, self.title, time.ctime())) + + def _doHTMLEnd(self): + # End of html page + self.out.write('
\n') + # Write a little info at the bottom + self._doPageFooter() + self.out.write('\n') + + #################################################### CSS markup functions + + def _getCSSStyle(self, key): + # Get the tags and colors from the dictionary + tags, forecolor, backcolor = self._getStyle(key) + style=[] + border = None + bordercolor = None + tags = tags.lower() + if tags: + # get the border color if specified + # the border color will be appended to + # the list after we define a border + if '#' in tags:# border color + start = tags.find('#') + end = start + 7 + bordercolor = tags[start:end] + tags.replace(bordercolor,'',1) + # text styles + if 'b' in tags:# Bold + style.append('font-weight:bold;') + else: + style.append('font-weight:normal;') + if 'i' in tags:# Italic + style.append('font-style:italic;') + if 'u' in tags:# Underline + style.append('text-decoration:underline;') + # border size + if 'l' in tags:# thick border + size='thick' + elif 'm' in tags:# medium border + size='medium' + elif 't' in tags:# thin border + size='thin' + else:# default + size='medium' + # border styles + if 'n' in tags:# inset border + border='inset' + elif 'o' in tags:# outset border + border='outset' + elif 'r' in tags:# ridge border + border='ridge' + elif 'g' in tags:# groove border + border='groove' + elif '=' in tags:# double border + border='double' + elif '.' in tags:# dotted border + border='dotted' + elif '-' in tags:# dashed border + border='dashed' + elif 's' in tags:# solid border + border='solid' + # border type check + seperate_sides=0 + for side in ['<','>','^','v']: + if side in tags: + seperate_sides+=1 + # border box or seperate sides + if seperate_sides==0 and border: + style.append('border: %s %s;'%(border,size)) + else: + if border == None: + border = 'solid' + if 'v' in tags:# bottom border + style.append('border-bottom:%s %s;'%(border,size)) + if '<' in tags:# left border + style.append('border-left:%s %s;'%(border,size)) + if '>' in tags:# right border + style.append('border-right:%s %s;'%(border,size)) + if '^' in tags:# top border + style.append('border-top:%s %s;'%(border,size)) + else: + style.append('font-weight:normal;')# css inherited style fix + # we have to define our borders before we set colors + if bordercolor: + style.append('border-color:%s;'%bordercolor) + # text forecolor + style.append('color:%s;'% forecolor) + # text backcolor + if backcolor: + style.append('background-color:%s;'%backcolor) + return (self._getMarkupClass(key),' '.join(style)) + + def _sendCSSStyle(self, external=0): + """ create external and internal style sheets""" + styles = [] + external += self.external + if not external: + styles.append('\n') + return ''.join(styles) + + def _doCSSStart(self): + # Start of css/html 4.01 page + self.out.write('\n') + self.out.write('%s\n'%(self.title)) + self.out.write(self._getDocumentCreatedBy()) + self.out.write('\n') + self._doCSSStyleSheet() + self.out.write('\n\n') + # Write a little info at the top. + self._doPageHeader() + self.out.write(self.colors.get(CODESTART,'
\n'))
+        return
+
+    def _doCSSStyleSheet(self):
+        if not self.external:
+            # write an embedded style sheet
+            self.out.write(self._sendCSSStyle())
+        else:
+            # write a link to an external style sheet
+            self.out.write('')
+        return
+
+    def _sendCSSText(self, toktype, toktext):
+        # This is a hack to 'fix' multi-line strings.
+        # Multi-line strings are treated as only one token 
+        # even though they can be several physical lines.
+        # That makes it hard to spot the start of a line,
+        # because at this level all we know about are tokens.
+        markupclass = MARKUPDICT.get(toktype, MARKUPDICT[NAME])
+        # if it is a LINENUMBER type then we can skip the rest
+        if toktext == self.LINESTART and toktype == LINENUMBER:
+            self.out.write('')
+            return
+        if toktext.count(self.LINENUMHOLDER):
+            # rip apart the string and separate it by line
+            # count lines and change all linenum token to line numbers
+            # also convert linestart and lineend tokens
+            #   lnum  text 
+            #################################################
+            newmarkup = MARKUPDICT.get(LINENUMBER, MARKUPDICT[NAME])
+            lstartspan = ''%(newmarkup)
+            if toktype == LINENUMBER:
+                splittext = toktext.split(self.LINENUMHOLDER)
+            else:    
+                splittext = toktext.split(self.LINENUMHOLDER+' ')
+            store = []
+            # we have already seen the first linenumber token
+            # so we can skip the first one
+            store.append(splittext.pop(0))
+            for item in splittext:
+                num = self._getLineNumber()
+                if self.numberlinks:
+                    numstrip = num.strip()
+                    content= '%s' \
+                              %(numstrip,numstrip,num)
+                else:
+                    content = num
+                linenumber= ''.join([lstartspan,content,''])
+                store.append(linenumber+item)
+            toktext = ''.join(store)
+        if toktext.count(self.LINESTART):
+            # wraps the textline in a line span
+            # this adds a lot of kludges, is it really worth it?
+            store = []
+            parts = toktext.split(self.LINESTART+' ')
+            # handle the first part differently
+            # the whole token gets wraqpped in a span later on
+            first = parts.pop(0)
+            # place spans before the newline
+            pos = first.rfind('\n')
+            if pos != -1:
+                first=first[:pos]+''+first[pos:]
+            store.append(first)
+            #process the rest of the string
+            for item in parts:
+                #handle line numbers if present
+                if self.dolinenums:
+                    item = item.replace('',
+                           ''%(markupclass))
+                else:
+                    item = '%s'%(markupclass,item)
+                # add endings for line and string tokens
+                pos = item.rfind('\n')
+                if pos != -1:
+                    item=item[:pos]+'\n'
+                store.append(item)
+            # add start tags for lines
+            toktext = ''.join(store)
+        # Send text
+        if toktype != LINENUMBER:
+            if toktype == TEXT and self.textFlag == 'DIV':
+                startspan = '
'%(markupclass) + endspan = '
' + elif toktype == TEXT and self.textFlag == 'RAW': + startspan,endspan = ('','') + else: + startspan = ''%(markupclass) + endspan = '' + self.out.write(''.join([startspan, toktext, endspan])) + else: + self.out.write(toktext) + return + + def _doCSSHeader(self): + if self.header != '': + self.out.write('%s\n'%self.header) + else: + name = MARKUPDICT.get(NAME) + self.out.write('
# %s
\ +# %s

\n'%(name, self.title, time.ctime())) + + def _doCSSFooter(self): + # Optional + if self.footer != '': + self.out.write('%s\n'%self.footer) + else: + self.out.write('
# %s
\ +# %s
\n'%(MARKUPDICT.get(NAME),self.title, time.ctime())) + + def _doCSSEnd(self): + # End of css/html page + self.out.write(self.colors.get(CODEEND,'
\n')) + # Write a little info at the bottom + self._doPageFooter() + self.out.write('\n') + return + + ################################################## XHTML markup functions + + def _doXHTMLStart(self): + # XHTML is really just XML + HTML 4.01. + # We only need to change the page headers, + # and a few tags to get valid XHTML. + # Start of xhtml page + self.out.write('\n \ +\n \ +\n') + self.out.write('%s\n'%(self.title)) + self.out.write(self._getDocumentCreatedBy()) + self.out.write('\n') + self._doXHTMLStyleSheet() + self.out.write('\n\n') + # Write a little info at the top. + self._doPageHeader() + self.out.write(self.colors.get(CODESTART,'
\n'))
+        return
+
+    def _doXHTMLStyleSheet(self):
+        if not self.external:
+            # write an embedded style sheet
+            self.out.write(self._sendCSSStyle())
+        else:
+            # write a link to an external style sheet
+            self.out.write('\n')
+        return
+
+    def _sendXHTMLText(self, toktype, toktext):
+        self._sendCSSText(toktype, toktext)
+
+    def _doXHTMLHeader(self):
+        # Optional
+        if self.header:
+            self.out.write('%s\n'%self.header)
+        else:
+            name = MARKUPDICT.get(NAME)
+            self.out.write('
# %s
\ +# %s

\n '%( + name, self.title, time.ctime())) + + def _doXHTMLFooter(self): + # Optional + if self.footer: + self.out.write('%s\n'%self.footer) + else: + self.out.write('
# %s
\ +# %s
\n'%(MARKUPDICT.get(NAME), self.title, time.ctime())) + + def _doXHTMLEnd(self): + self._doCSSEnd() + +############################################################################# + +if __name__ == '__main__': + cli() + +############################################################################# +# PySourceColor.py +# 2004, 2005 M.E.Farmer Jr. +# Python license \ No newline at end of file -- cgit v1.2.1