root/galaxy-central/eggs/Paste-1.6-py2.6.egg/paste/util/PySourceColor.py @ 3

リビジョン 3, 83.8 KB (コミッタ: kohda, 14 年 前)

Install Unix tools  http://hannonlab.cshl.edu/galaxy_unix_tools/galaxy.html

行番号 
1# -*- coding: Latin-1 -*-
2"""
3PySourceColor: color Python source code
4"""
5
6"""
7 PySourceColor.py
8
9----------------------------------------------------------------------------
10
11 A python source to colorized html/css/xhtml converter.
12 Hacked by M.E.Farmer Jr. 2004, 2005
13 Python license
14
15----------------------------------------------------------------------------
16
17 - HTML markup does not create w3c valid html, but it works on every
18   browser i've tried so far.(I.E.,Mozilla/Firefox,Opera,Konqueror,wxHTML).
19 - CSS markup is w3c validated html 4.01 strict,
20   but will not render correctly on all browsers.
21 - XHTML markup is w3c validated xhtml 1.0 strict,
22   like html 4.01, will not render correctly on all browsers.
23
24----------------------------------------------------------------------------
25
26Features:
27
28 -Three types of markup:
29    html (default)
30    css/html 4.01 strict
31    xhtml 1.0 strict
32
33 -Can tokenize and colorize:
34    12 types of strings
35    2 comment types
36    numbers
37    operators
38    brackets
39    math operators
40    class / name
41    def / name
42    decorator / name
43    keywords
44    arguments class/def/decorator
45    linenumbers
46    names
47    text
48
49 -Eight colorschemes built-in:
50    null
51    mono
52    lite (default)
53    dark
54    dark2
55    idle
56    viewcvs
57    pythonwin
58
59 -Header and footer
60    set to '' for builtin header / footer.
61    give path to a file containing the html
62        you want added as header or footer.
63
64 -Arbitrary text and html
65    html markup converts all to raw (TEXT token)
66    #@# for raw -> send raw text.
67    #$# for span -> inline html and text.
68    #%# for div -> block level html and text.
69
70 -Linenumbers
71    Supports all styles. New token is called LINENUMBER.
72    Defaults to NAME if not defined.
73
74 Style options
75 
76 -ALL markups support these text styles:
77         b = bold
78         i = italic
79         u = underline
80 -CSS and XHTML has limited support  for borders:
81     HTML markup functions will ignore these.
82     Optional: Border color in RGB hex
83     Defaults to the text forecolor.
84         #rrggbb = border color
85     Border size:
86         l = thick
87         m = medium
88         t = thin
89     Border type:
90         - = dashed
91         . = dotted
92         s = solid
93         d = double
94         g = groove
95         r = ridge
96         n = inset
97         o = outset
98     You can specify multiple sides,
99     they will all use the same style.
100     Optional: Default is full border.
101         v = bottom
102         < = left
103         > = right
104         ^ = top
105     NOTE: Specify the styles you want.
106           The markups will ignore unsupported styles
107           Also note not all browsers can show these options
108
109 -All tokens default to NAME if not defined
110     so the only absolutely critical ones to define are:
111     NAME, ERRORTOKEN, PAGEBACKGROUND
112
113----------------------------------------------------------------------------
114
115Example usage::
116
117 # import
118 import PySourceColor as psc
119 psc.convert('c:/Python22/PySourceColor.py', colors=psc.idle, show=1)
120
121 # from module import *
122 from PySourceColor import *
123 convert('c:/Python22/Lib', colors=lite, markup="css",
124          header='#$#<b>This is a simpe heading</b><hr/>')
125
126 # How to use a custom colorscheme, and most of the 'features'
127 from PySourceColor import *
128 new = {
129   ERRORTOKEN:             ('bui','#FF8080',''),
130   DECORATOR_NAME:         ('s','#AACBBC',''),
131   DECORATOR:              ('n','#333333',''),
132   NAME:                   ('t.<v','#1133AA','#DDFF22'),
133   NUMBER:                 ('','#236676','#FF5555'),
134   OPERATOR:               ('b','#454567','#BBBB11'),
135   MATH_OPERATOR:          ('','#935623','#423afb'),
136   BRACKETS:               ('b','#ac34bf','#6457a5'),
137   COMMENT:                ('t-#0022FF','#545366','#AABBFF'),
138   DOUBLECOMMENT:          ('<l#553455','#553455','#FF00FF'),
139   CLASS_NAME:             ('m^v-','#000000','#FFFFFF'),
140   DEF_NAME:               ('l=<v','#897845','#000022'),
141   KEYWORD:                ('.b','#345345','#FFFF22'),
142   SINGLEQUOTE:            ('mn','#223344','#AADDCC'),
143   SINGLEQUOTE_R:          ('','#344522',''),
144   SINGLEQUOTE_U:          ('','#234234',''),
145   DOUBLEQUOTE:            ('m#0022FF','#334421',''),
146   DOUBLEQUOTE_R:          ('','#345345',''),
147   DOUBLEQUOTE_U:          ('','#678673',''),
148   TRIPLESINGLEQUOTE:      ('tv','#FFFFFF','#000000'),
149   TRIPLESINGLEQUOTE_R:    ('tbu','#443256','#DDFFDA'),
150   TRIPLESINGLEQUOTE_U:    ('','#423454','#DDFFDA'),
151   TRIPLEDOUBLEQUOTE:      ('li#236fd3b<>','#000000','#FFFFFF'),
152   TRIPLEDOUBLEQUOTE_R:    ('tub','#000000','#FFFFFF'),
153   TRIPLEDOUBLEQUOTE_U:    ('-', '#CCAABB','#FFFAFF'),
154   LINENUMBER:             ('ib-','#ff66aa','#7733FF'),]
155   TEXT:                   ('','#546634',''),
156   PAGEBACKGROUND:         '#FFFAAA',
157     }
158 if __name__ == '__main__':
159     import sys
160     convert(sys.argv[1], './xhtml.html', colors=new, markup='xhtml', show=1,
161             linenumbers=1)
162     convert(sys.argv[1], './html.html', colors=new, markup='html', show=1,
163             linenumbers=1)
164
165"""
166
167__all__ = ['ERRORTOKEN','DECORATOR_NAME', 'DECORATOR', 'ARGS', 'EXTRASPACE',
168       'NAME', 'NUMBER', 'OPERATOR', 'COMMENT', 'MATH_OPERATOR',
169       'DOUBLECOMMENT', 'CLASS_NAME', 'DEF_NAME', 'KEYWORD', 'BRACKETS',
170       'SINGLEQUOTE','SINGLEQUOTE_R','SINGLEQUOTE_U','DOUBLEQUOTE',
171       'DOUBLEQUOTE_R', 'DOUBLEQUOTE_U', 'TRIPLESINGLEQUOTE', 'TEXT',
172       'TRIPLESINGLEQUOTE_R', 'TRIPLESINGLEQUOTE_U', 'TRIPLEDOUBLEQUOTE',
173       'TRIPLEDOUBLEQUOTE_R', 'TRIPLEDOUBLEQUOTE_U', 'PAGEBACKGROUND',
174       'LINENUMBER', 'CODESTART', 'CODEEND', 'PY', 'TOKEN_NAMES', 'CSSHOOK',
175       'null', 'mono', 'lite', 'dark','dark2', 'pythonwin','idle',
176       'viewcvs', 'Usage', 'cli', 'str2stdout', 'path2stdout', 'Parser',
177       'str2file', 'str2html', 'str2css', 'str2markup', 'path2file',
178       'path2html', 'convert', 'walkdir', 'defaultColors', 'showpage',
179       'pageconvert','tagreplace', 'MARKUPDICT']
180__title__ = 'PySourceColor'
181__version__ = "2.1a"
182__date__ = '25 April 2005'
183__author__ = "M.E.Farmer Jr."
184__credits__ = '''This was originally based on a python recipe
185submitted by J�gen Hermann to ASPN. Now based on the voices in my head.
186M.E.Farmer 2004, 2005
187Python license
188'''
189import os
190import sys
191import time
192import glob
193import getopt
194import keyword
195import token
196import tokenize
197import traceback
198import webbrowser
199try :
200    import cStringIO as StringIO
201except:
202    import StringIO
203# Do not edit
204NAME = token.NAME
205NUMBER = token.NUMBER
206COMMENT = tokenize.COMMENT
207OPERATOR = token.OP
208ERRORTOKEN = token.ERRORTOKEN
209ARGS = token.NT_OFFSET + 1
210DOUBLECOMMENT = token.NT_OFFSET + 2
211CLASS_NAME = token.NT_OFFSET + 3
212DEF_NAME = token.NT_OFFSET + 4
213KEYWORD = token.NT_OFFSET + 5
214SINGLEQUOTE = token.NT_OFFSET + 6
215SINGLEQUOTE_R = token.NT_OFFSET + 7
216SINGLEQUOTE_U = token.NT_OFFSET + 8
217DOUBLEQUOTE = token.NT_OFFSET + 9
218DOUBLEQUOTE_R = token.NT_OFFSET + 10
219DOUBLEQUOTE_U = token.NT_OFFSET + 11
220TRIPLESINGLEQUOTE = token.NT_OFFSET + 12
221TRIPLESINGLEQUOTE_R = token.NT_OFFSET + 13
222TRIPLESINGLEQUOTE_U = token.NT_OFFSET + 14
223TRIPLEDOUBLEQUOTE = token.NT_OFFSET + 15
224TRIPLEDOUBLEQUOTE_R = token.NT_OFFSET + 16
225TRIPLEDOUBLEQUOTE_U = token.NT_OFFSET + 17
226PAGEBACKGROUND = token.NT_OFFSET + 18
227DECORATOR = token.NT_OFFSET + 19
228DECORATOR_NAME = token.NT_OFFSET + 20
229BRACKETS = token.NT_OFFSET + 21
230MATH_OPERATOR = token.NT_OFFSET + 22
231LINENUMBER = token.NT_OFFSET + 23
232TEXT = token.NT_OFFSET + 24
233PY = token.NT_OFFSET + 25
234CODESTART = token.NT_OFFSET + 26
235CODEEND = token.NT_OFFSET + 27
236CSSHOOK = token.NT_OFFSET + 28
237EXTRASPACE = token.NT_OFFSET + 29
238
239# markup classname lookup
240MARKUPDICT = {
241        ERRORTOKEN:             'py_err',
242        DECORATOR_NAME:         'py_decn',
243        DECORATOR:              'py_dec',
244        ARGS:                   'py_args',
245        NAME:                   'py_name',
246        NUMBER:                 'py_num',
247        OPERATOR:               'py_op',
248        COMMENT:                'py_com',
249        DOUBLECOMMENT:          'py_dcom',
250        CLASS_NAME:             'py_clsn',
251        DEF_NAME:               'py_defn',
252        KEYWORD:                'py_key',
253        SINGLEQUOTE:            'py_sq',
254        SINGLEQUOTE_R:          'py_sqr',
255        SINGLEQUOTE_U:          'py_squ',
256        DOUBLEQUOTE:            'py_dq',
257        DOUBLEQUOTE_R:          'py_dqr',
258        DOUBLEQUOTE_U:          'py_dqu',
259        TRIPLESINGLEQUOTE:      'py_tsq',
260        TRIPLESINGLEQUOTE_R:    'py_tsqr',
261        TRIPLESINGLEQUOTE_U:    'py_tsqu',
262        TRIPLEDOUBLEQUOTE:      'py_tdq',
263        TRIPLEDOUBLEQUOTE_R:    'py_tdqr',
264        TRIPLEDOUBLEQUOTE_U:    'py_tdqu',
265        BRACKETS:               'py_bra',
266        MATH_OPERATOR:          'py_mop',
267        LINENUMBER:             'py_lnum',
268        TEXT:                   'py_text',
269        }
270# might help users that want to create custom schemes
271TOKEN_NAMES= {
272       ERRORTOKEN:'ERRORTOKEN',
273       DECORATOR_NAME:'DECORATOR_NAME',
274       DECORATOR:'DECORATOR',
275       ARGS:'ARGS',
276       NAME:'NAME',
277       NUMBER:'NUMBER',
278       OPERATOR:'OPERATOR',
279       COMMENT:'COMMENT',
280       DOUBLECOMMENT:'DOUBLECOMMENT',
281       CLASS_NAME:'CLASS_NAME',
282       DEF_NAME:'DEF_NAME',
283       KEYWORD:'KEYWORD',
284       SINGLEQUOTE:'SINGLEQUOTE',
285       SINGLEQUOTE_R:'SINGLEQUOTE_R',
286       SINGLEQUOTE_U:'SINGLEQUOTE_U',
287       DOUBLEQUOTE:'DOUBLEQUOTE',
288       DOUBLEQUOTE_R:'DOUBLEQUOTE_R',
289       DOUBLEQUOTE_U:'DOUBLEQUOTE_U',
290       TRIPLESINGLEQUOTE:'TRIPLESINGLEQUOTE',
291       TRIPLESINGLEQUOTE_R:'TRIPLESINGLEQUOTE_R',
292       TRIPLESINGLEQUOTE_U:'TRIPLESINGLEQUOTE_U',
293       TRIPLEDOUBLEQUOTE:'TRIPLEDOUBLEQUOTE',
294       TRIPLEDOUBLEQUOTE_R:'TRIPLEDOUBLEQUOTE_R',
295       TRIPLEDOUBLEQUOTE_U:'TRIPLEDOUBLEQUOTE_U',
296       BRACKETS:'BRACKETS',
297       MATH_OPERATOR:'MATH_OPERATOR',
298       LINENUMBER:'LINENUMBER',
299       TEXT:'TEXT',
300       PAGEBACKGROUND:'PAGEBACKGROUND',
301       }
302
303######################################################################
304# Edit colors and styles to taste
305# Create your own scheme, just copy one below , rename and edit.
306# Custom styles must at least define NAME, ERRORTOKEN, PAGEBACKGROUND,
307# all missing elements will default to NAME.
308# See module docstring for details on style attributes.
309######################################################################
310# Copy null and use it as a starter colorscheme.
311null = {# tokentype: ('tags border_color', 'textforecolor', 'textbackcolor')
312        ERRORTOKEN:             ('','#000000',''),# Error token
313        DECORATOR_NAME:         ('','#000000',''),# Decorator name
314        DECORATOR:              ('','#000000',''),# @ symbol
315        ARGS:                   ('','#000000',''),# class,def,deco arguments
316        NAME:                   ('','#000000',''),# All other python text
317        NUMBER:                 ('','#000000',''),# 0->10
318        OPERATOR:               ('','#000000',''),# ':','<=',';',',','.','==', etc
319        MATH_OPERATOR:          ('','#000000',''),# '+','-','=','','**',etc
320        BRACKETS:               ('','#000000',''),# '[',']','(',')','{','}'
321        COMMENT:                ('','#000000',''),# Single comment
322        DOUBLECOMMENT:          ('','#000000',''),## Double comment
323        CLASS_NAME:             ('','#000000',''),# Class name
324        DEF_NAME:               ('','#000000',''),# Def name
325        KEYWORD:                ('','#000000',''),# Python keywords
326        SINGLEQUOTE:            ('','#000000',''),# 'SINGLEQUOTE'
327        SINGLEQUOTE_R:          ('','#000000',''),# r'SINGLEQUOTE'
328        SINGLEQUOTE_U:          ('','#000000',''),# u'SINGLEQUOTE'
329        DOUBLEQUOTE:            ('','#000000',''),# "DOUBLEQUOTE"
330        DOUBLEQUOTE_R:          ('','#000000',''),# r"DOUBLEQUOTE"
331        DOUBLEQUOTE_U:          ('','#000000',''),# u"DOUBLEQUOTE"
332        TRIPLESINGLEQUOTE:      ('','#000000',''),# '''TRIPLESINGLEQUOTE'''
333        TRIPLESINGLEQUOTE_R:    ('','#000000',''),# r'''TRIPLESINGLEQUOTE'''
334        TRIPLESINGLEQUOTE_U:    ('','#000000',''),# u'''TRIPLESINGLEQUOTE'''
335        TRIPLEDOUBLEQUOTE:      ('','#000000',''),# """TRIPLEDOUBLEQUOTE"""
336        TRIPLEDOUBLEQUOTE_R:    ('','#000000',''),# r"""TRIPLEDOUBLEQUOTE"""
337        TRIPLEDOUBLEQUOTE_U:    ('','#000000',''),# u"""TRIPLEDOUBLEQUOTE"""
338        TEXT:                   ('','#000000',''),# non python text
339        LINENUMBER:             ('>ti#555555','#000000',''),# Linenumbers
340        PAGEBACKGROUND:         '#FFFFFF'# set the page background
341        }
342
343mono = {
344        ERRORTOKEN:             ('s#FF0000','#FF8080',''),
345        DECORATOR_NAME:         ('bu','#000000',''),
346        DECORATOR:              ('b','#000000',''),
347        ARGS:                   ('b','#555555',''),
348        NAME:                   ('','#000000',''),
349        NUMBER:                 ('b','#000000',''),
350        OPERATOR:               ('b','#000000',''),
351        MATH_OPERATOR:          ('b','#000000',''),
352        BRACKETS:               ('b','#000000',''),
353        COMMENT:                ('i','#999999',''),
354        DOUBLECOMMENT:          ('b','#999999',''),
355        CLASS_NAME:             ('bu','#000000',''),
356        DEF_NAME:               ('b','#000000',''),
357        KEYWORD:                ('b','#000000',''),
358        SINGLEQUOTE:            ('','#000000',''),
359        SINGLEQUOTE_R:          ('','#000000',''),
360        SINGLEQUOTE_U:          ('','#000000',''),
361        DOUBLEQUOTE:            ('','#000000',''),
362        DOUBLEQUOTE_R:          ('','#000000',''),
363        DOUBLEQUOTE_U:          ('','#000000',''),
364        TRIPLESINGLEQUOTE:      ('','#000000',''),
365        TRIPLESINGLEQUOTE_R:    ('','#000000',''),
366        TRIPLESINGLEQUOTE_U:    ('','#000000',''),
367        TRIPLEDOUBLEQUOTE:      ('i','#000000',''),
368        TRIPLEDOUBLEQUOTE_R:    ('i','#000000',''),
369        TRIPLEDOUBLEQUOTE_U:    ('i','#000000',''),
370        TEXT:                   ('','#000000',''),
371        LINENUMBER:             ('>ti#555555','#000000',''),
372        PAGEBACKGROUND:         '#FFFFFF'
373        }
374
375dark = {
376        ERRORTOKEN:             ('s#FF0000','#FF8080',''),
377        DECORATOR_NAME:         ('b','#FFBBAA',''),
378        DECORATOR:              ('b','#CC5511',''),
379        ARGS:                   ('b','#DDDDFF',''),
380        NAME:                   ('','#DDDDDD',''),
381        NUMBER:                 ('','#FF0000',''),
382        OPERATOR:               ('b','#FAF785',''),
383        MATH_OPERATOR:          ('b','#FAF785',''),
384        BRACKETS:               ('b','#FAF785',''),
385        COMMENT:                ('','#45FCA0',''),
386        DOUBLECOMMENT:          ('i','#A7C7A9',''),
387        CLASS_NAME:             ('b','#B666FD',''),
388        DEF_NAME:               ('b','#EBAE5C',''),
389        KEYWORD:                ('b','#8680FF',''),
390        SINGLEQUOTE:            ('','#F8BAFE',''),
391        SINGLEQUOTE_R:          ('','#F8BAFE',''),
392        SINGLEQUOTE_U:          ('','#F8BAFE',''),
393        DOUBLEQUOTE:            ('','#FF80C0',''),
394        DOUBLEQUOTE_R:          ('','#FF80C0',''),
395        DOUBLEQUOTE_U:          ('','#FF80C0',''),
396        TRIPLESINGLEQUOTE:      ('','#FF9595',''),
397        TRIPLESINGLEQUOTE_R:    ('','#FF9595',''),
398        TRIPLESINGLEQUOTE_U:    ('','#FF9595',''),
399        TRIPLEDOUBLEQUOTE:      ('','#B3FFFF',''),
400        TRIPLEDOUBLEQUOTE_R:    ('','#B3FFFF',''),
401        TRIPLEDOUBLEQUOTE_U:    ('','#B3FFFF',''),
402        TEXT:                   ('','#FFFFFF',''),
403        LINENUMBER:             ('>mi#555555','#bbccbb','#333333'),
404        PAGEBACKGROUND:         '#000000'
405        }
406
407dark2 = {
408        ERRORTOKEN:             ('','#FF0000',''),
409        DECORATOR_NAME:         ('b','#FFBBAA',''),
410        DECORATOR:              ('b','#CC5511',''),
411        ARGS:                   ('b','#DDDDDD',''),
412        NAME:                   ('','#C0C0C0',''),
413        NUMBER:                 ('b','#00FF00',''),
414        OPERATOR:               ('b','#FF090F',''),
415        MATH_OPERATOR:          ('b','#EE7020',''),
416        BRACKETS:               ('b','#FFB90F',''),
417        COMMENT:                ('i','#D0D000','#522000'),#'#88AA88','#11111F'),
418        DOUBLECOMMENT:          ('i','#D0D000','#522000'),#'#77BB77','#11111F'),
419        CLASS_NAME:             ('b','#DD4080',''),
420        DEF_NAME:               ('b','#FF8040',''),
421        KEYWORD:                ('b','#4726d1',''),
422        SINGLEQUOTE:            ('','#8080C0',''),
423        SINGLEQUOTE_R:          ('','#8080C0',''),
424        SINGLEQUOTE_U:          ('','#8080C0',''),
425        DOUBLEQUOTE:            ('','#ADB9F1',''),
426        DOUBLEQUOTE_R:          ('','#ADB9F1',''),
427        DOUBLEQUOTE_U:          ('','#ADB9F1',''),
428        TRIPLESINGLEQUOTE:      ('','#00C1C1',''),#A050C0
429        TRIPLESINGLEQUOTE_R:    ('','#00C1C1',''),#A050C0
430        TRIPLESINGLEQUOTE_U:    ('','#00C1C1',''),#A050C0
431        TRIPLEDOUBLEQUOTE:      ('','#33E3E3',''),#B090E0
432        TRIPLEDOUBLEQUOTE_R:    ('','#33E3E3',''),#B090E0
433        TRIPLEDOUBLEQUOTE_U:    ('','#33E3E3',''),#B090E0
434        TEXT:                   ('','#C0C0C0',''),
435        LINENUMBER:             ('>mi#555555','#bbccbb','#333333'),
436        PAGEBACKGROUND:         '#000000'
437        }
438
439lite = {
440        ERRORTOKEN:             ('s#FF0000','#FF8080',''),
441        DECORATOR_NAME:         ('b','#BB4422',''),
442        DECORATOR:              ('b','#3333AF',''),
443        ARGS:                   ('b','#000000',''),
444        NAME:                   ('','#333333',''),
445        NUMBER:                 ('b','#DD2200',''),
446        OPERATOR:               ('b','#000000',''),
447        MATH_OPERATOR:          ('b','#000000',''),
448        BRACKETS:               ('b','#000000',''),
449        COMMENT:                ('','#007F00',''),
450        DOUBLECOMMENT:          ('','#608060',''),
451        CLASS_NAME:             ('b','#0000DF',''),
452        DEF_NAME:               ('b','#9C7A00',''),#f09030
453        KEYWORD:                ('b','#0000AF',''),
454        SINGLEQUOTE:            ('','#600080',''),
455        SINGLEQUOTE_R:          ('','#600080',''),
456        SINGLEQUOTE_U:          ('','#600080',''),
457        DOUBLEQUOTE:            ('','#A0008A',''),
458        DOUBLEQUOTE_R:          ('','#A0008A',''),
459        DOUBLEQUOTE_U:          ('','#A0008A',''),
460        TRIPLESINGLEQUOTE:      ('','#337799',''),
461        TRIPLESINGLEQUOTE_R:    ('','#337799',''),
462        TRIPLESINGLEQUOTE_U:    ('','#337799',''),
463        TRIPLEDOUBLEQUOTE:      ('','#1166AA',''),
464        TRIPLEDOUBLEQUOTE_R:    ('','#1166AA',''),
465        TRIPLEDOUBLEQUOTE_U:    ('','#1166AA',''),
466        TEXT:                   ('','#000000',''),
467        LINENUMBER:             ('>ti#555555','#000000',''),
468        PAGEBACKGROUND:         '#FFFFFF'
469        }
470
471idle = {
472        ERRORTOKEN:             ('s#FF0000','#FF8080',''),
473        DECORATOR_NAME:         ('','#900090',''),
474        DECORATOR:              ('','#FF7700',''),
475        NAME:                   ('','#000000',''),
476        NUMBER:                 ('','#000000',''),
477        OPERATOR:               ('','#000000',''),
478        MATH_OPERATOR:          ('','#000000',''),
479        BRACKETS:               ('','#000000',''),
480        COMMENT:                ('','#DD0000',''),
481        DOUBLECOMMENT:          ('','#DD0000',''),
482        CLASS_NAME:             ('','#0000FF',''),
483        DEF_NAME:               ('','#0000FF',''),
484        KEYWORD:                ('','#FF7700',''),
485        SINGLEQUOTE:            ('','#00AA00',''),
486        SINGLEQUOTE_R:          ('','#00AA00',''),
487        SINGLEQUOTE_U:          ('','#00AA00',''),
488        DOUBLEQUOTE:            ('','#00AA00',''),
489        DOUBLEQUOTE_R:          ('','#00AA00',''),
490        DOUBLEQUOTE_U:          ('','#00AA00',''),
491        TRIPLESINGLEQUOTE:      ('','#00AA00',''),
492        TRIPLESINGLEQUOTE_R:    ('','#00AA00',''),
493        TRIPLESINGLEQUOTE_U:    ('','#00AA00',''),
494        TRIPLEDOUBLEQUOTE:      ('','#00AA00',''),
495        TRIPLEDOUBLEQUOTE_R:    ('','#00AA00',''),
496        TRIPLEDOUBLEQUOTE_U:    ('','#00AA00',''),
497        TEXT:                   ('','#000000',''),
498        LINENUMBER:             ('>ti#555555','#000000',''),
499        PAGEBACKGROUND:         '#FFFFFF'
500        }
501
502pythonwin = {
503        ERRORTOKEN:             ('s#FF0000','#FF8080',''),
504        DECORATOR_NAME:         ('b','#DD0080',''),
505        DECORATOR:              ('b','#000080',''),
506        ARGS:                   ('','#000000',''),
507        NAME:                   ('','#303030',''),
508        NUMBER:                 ('','#008080',''),
509        OPERATOR:               ('','#000000',''),
510        MATH_OPERATOR:          ('','#000000',''),
511        BRACKETS:               ('','#000000',''),
512        COMMENT:                ('','#007F00',''),
513        DOUBLECOMMENT:          ('','#7F7F7F',''),
514        CLASS_NAME:             ('b','#0000FF',''),
515        DEF_NAME:               ('b','#007F7F',''),
516        KEYWORD:                ('b','#000080',''),
517        SINGLEQUOTE:            ('','#808000',''),
518        SINGLEQUOTE_R:          ('','#808000',''),
519        SINGLEQUOTE_U:          ('','#808000',''),
520        DOUBLEQUOTE:            ('','#808000',''),
521        DOUBLEQUOTE_R:          ('','#808000',''),
522        DOUBLEQUOTE_U:          ('','#808000',''),
523        TRIPLESINGLEQUOTE:      ('','#808000',''),
524        TRIPLESINGLEQUOTE_R:    ('','#808000',''),
525        TRIPLESINGLEQUOTE_U:    ('','#808000',''),
526        TRIPLEDOUBLEQUOTE:      ('','#808000',''),
527        TRIPLEDOUBLEQUOTE_R:    ('','#808000',''),
528        TRIPLEDOUBLEQUOTE_U:    ('','#808000',''),
529        TEXT:                   ('','#303030',''),
530        LINENUMBER:             ('>ti#555555','#000000',''),
531        PAGEBACKGROUND:         '#FFFFFF'
532        }
533
534viewcvs = {
535        ERRORTOKEN:             ('s#FF0000','#FF8080',''),
536        DECORATOR_NAME:         ('','#000000',''),
537        DECORATOR:              ('','#000000',''),
538        ARGS:                   ('','#000000',''),
539        NAME:                   ('','#000000',''),
540        NUMBER:                 ('','#000000',''),
541        OPERATOR:               ('','#000000',''),
542        MATH_OPERATOR:          ('','#000000',''),
543        BRACKETS:               ('','#000000',''),
544        COMMENT:                ('i','#b22222',''),
545        DOUBLECOMMENT:          ('i','#b22222',''),
546        CLASS_NAME:             ('','#000000',''),
547        DEF_NAME:               ('b','#0000ff',''),
548        KEYWORD:                ('b','#a020f0',''),
549        SINGLEQUOTE:            ('b','#bc8f8f',''),
550        SINGLEQUOTE_R:          ('b','#bc8f8f',''),
551        SINGLEQUOTE_U:          ('b','#bc8f8f',''),
552        DOUBLEQUOTE:            ('b','#bc8f8f',''),
553        DOUBLEQUOTE_R:          ('b','#bc8f8f',''),
554        DOUBLEQUOTE_U:          ('b','#bc8f8f',''),
555        TRIPLESINGLEQUOTE:      ('b','#bc8f8f',''),
556        TRIPLESINGLEQUOTE_R:    ('b','#bc8f8f',''),
557        TRIPLESINGLEQUOTE_U:    ('b','#bc8f8f',''),
558        TRIPLEDOUBLEQUOTE:      ('b','#bc8f8f',''),
559        TRIPLEDOUBLEQUOTE_R:    ('b','#bc8f8f',''),
560        TRIPLEDOUBLEQUOTE_U:    ('b','#bc8f8f',''),
561        TEXT:                   ('','#000000',''),
562        LINENUMBER:             ('>ti#555555','#000000',''),
563        PAGEBACKGROUND:         '#FFFFFF'
564        }
565
566defaultColors = lite
567
568def Usage():
569    doc = """
570 -----------------------------------------------------------------------------
571  PySourceColor.py ver: %s
572 -----------------------------------------------------------------------------
573  Module summary:
574     This module is designed to colorize python source code.
575         Input--->python source
576         Output-->colorized (html, html4.01/css, xhtml1.0)
577     Standalone:
578         This module will work from the command line with options.
579         This module will work with redirected stdio.
580     Imported:
581         This module can be imported and used directly in your code.
582 -----------------------------------------------------------------------------
583  Command line options:
584     -h, --help
585         Optional-> Display this help message.
586     -t, --test
587         Optional-> Will ignore all others flags but  --profile
588             test all schemes and markup combinations
589     -p, --profile
590         Optional-> Works only with --test or -t
591             runs profile.py and makes the test work in quiet mode.
592     -i, --in, --input
593         Optional-> If you give input on stdin.
594         Use any of these for the current dir (.,cwd)
595         Input can be file or dir.
596         Input from stdin use one of the following (-,stdin)
597         If stdin is used as input stdout is output unless specified.
598     -o, --out, --output
599         Optional-> output dir for the colorized source.
600             default: output dir is the input dir.
601         To output html to stdout use one of the following (-,stdout)
602         Stdout can be used without stdin if you give a file as input.
603     -c, --color
604         Optional-> null, mono, dark, dark2, lite, idle, pythonwin, viewcvs
605             default: dark
606     -s, --show
607         Optional-> Show page after creation.
608             default: no show
609     -m, --markup
610         Optional-> html, css, xhtml
611             css, xhtml also support external stylesheets (-e,--external)
612             default: HTML
613     -e, --external
614         Optional-> use with css, xhtml
615             Writes an style sheet instead of embedding it in the page
616             saves it as pystyle.css in the same directory.
617             html markup will silently ignore this flag.
618     -H, --header
619         Opional-> add a page header to the top of the output
620         -H
621             Builtin header (name,date,hrule)
622         --header
623             You must specify a filename.
624             The header file must be valid html
625             and must handle its own font colors.
626             ex. --header c:/tmp/header.txt
627     -F, --footer
628         Opional-> add a page footer to the bottom of the output
629         -F
630             Builtin footer (hrule,name,date)
631         --footer
632             You must specify a filename.
633             The footer file must be valid html
634             and must handle its own font colors.
635             ex. --footer c:/tmp/footer.txt 
636     -l, --linenumbers
637         Optional-> default is no linenumbers
638             Adds line numbers to the start of each line in the code.
639    --convertpage
640         Given a webpage that has code embedded in tags it will
641             convert embedded code to colorized html.
642             (see pageconvert for details)
643 -----------------------------------------------------------------------------
644  Option usage:
645   # Test and show pages
646      python PySourceColor.py -t -s
647   # Test and only show profile results
648      python PySourceColor.py -t -p
649   # Colorize all .py,.pyw files in cwdir you can also use: (.,cwd)
650      python PySourceColor.py -i .
651   # Using long options w/ =
652      python PySourceColor.py --in=c:/myDir/my.py --color=lite --show
653   # Using short options w/out =
654      python PySourceColor.py -i c:/myDir/  -c idle -m css -e
655   # Using any mix
656      python PySourceColor.py --in . -o=c:/myDir --show
657   # Place a custom header on your files
658      python PySourceColor.py -i . -o c:/tmp -m xhtml --header c:/header.txt
659 -----------------------------------------------------------------------------
660  Stdio usage:
661   # Stdio using no options
662      python PySourceColor.py < c:/MyFile.py > c:/tmp/MyFile.html
663   # Using stdin alone automatically uses stdout for output: (stdin,-)
664      python PySourceColor.py -i- < c:/MyFile.py > c:/tmp/myfile.html
665   # Stdout can also be written to directly from a file instead of stdin
666      python PySourceColor.py -i c:/MyFile.py -m css -o- > c:/tmp/myfile.html
667   # Stdin can be used as input , but output can still be specified
668      python PySourceColor.py -i- -o c:/pydoc.py.html -s < c:/Python22/my.py
669 _____________________________________________________________________________
670 """
671    print doc % (__version__)
672    sys.exit(1)
673
674###################################################### Command line interface
675
676def cli():
677    """Handle command line args and redirections"""
678    try:
679        # try to get command line args
680        opts, args = getopt.getopt(sys.argv[1:],
681              "hseqtplHFi:o:c:m:h:f:",["help", "show", "quiet",
682              "test", "external", "linenumbers", "convertpage", "profile",
683              "input=", "output=", "color=", "markup=","header=", "footer="])
684    except getopt.GetoptError:
685        # on error print help information and exit:
686        Usage()
687    # init some names
688    input = None
689    output = None
690    colorscheme = None
691    markup = 'html'
692    header = None
693    footer = None
694    linenumbers = 0
695    show = 0
696    quiet = 0
697    test = 0
698    profile = 0
699    convertpage = 0
700    form = None
701    # if we have args then process them
702    for o, a in opts:
703        if o in ["-h", "--help"]:
704            Usage()
705            sys.exit()
706        if o in ["-o", "--output", "--out"]:
707            output = a
708        if o in ["-i", "--input", "--in"]:
709            input = a
710            if input in [".", "cwd"]:
711                input = os.getcwd()
712        if o in ["-s", "--show"]:
713            show = 1
714        if o in ["-q", "--quiet"]:
715            quiet = 1
716        if o in ["-t", "--test"]:
717            test = 1
718        if o in ["--convertpage"]:
719            convertpage = 1
720        if o in ["-p", "--profile"]:
721            profile = 1
722        if o in ["-e", "--external"]:
723            form = 'external'
724        if o in ["-m", "--markup"]:
725            markup = str(a)
726        if o in ["-l", "--linenumbers"]:
727            linenumbers = 1
728        if o in ["--header"]:
729            header = str(a)
730        elif o == "-H":
731            header = ''
732        if o in ["--footer"]:
733            footer = str(a)
734        elif o == "-F":
735            footer = ''
736        if o in ["-c", "--color"]:
737            try:
738                colorscheme = globals().get(a.lower())
739            except:
740                traceback.print_exc()
741                Usage()
742    if test:
743        if profile:
744            import profile
745            profile.run('_test(show=%s, quiet=%s)'%(show,quiet))
746        else:
747            # Parse this script in every possible colorscheme and markup
748            _test(show,quiet)
749    elif input in [None, "-", "stdin"] or output in ["-", "stdout"]:
750        # determine if we are going to use stdio
751        if input not in [None, "-", "stdin"]:
752            if os.path.isfile(input) :
753                path2stdout(input, colors=colorscheme, markup=markup,
754                            linenumbers=linenumbers, header=header,
755                            footer=footer, form=form)
756            else:
757                raise PathError, 'File does not exists!'
758        else:
759            try:
760                if sys.stdin.isatty():
761                    raise InputError, 'Please check input!'
762                else:
763                    if output in [None,"-","stdout"]:
764                        str2stdout(sys.stdin.read(), colors=colorscheme,
765                                   markup=markup, header=header,
766                                   footer=footer, linenumbers=linenumbers,
767                                   form=form)
768                    else:
769                        str2file(sys.stdin.read(), outfile=output, show=show,
770                                markup=markup, header=header, footer=footer,
771                                linenumbers=linenumbers, form=form)
772            except:
773                traceback.print_exc()
774                Usage()
775    else:
776        if os.path.exists(input):
777            if convertpage:
778                # if there was at least an input given we can proceed
779                pageconvert(input, out=output, colors=colorscheme,
780                            show=show, markup=markup,linenumbers=linenumbers)
781            else:
782                # if there was at least an input given we can proceed
783                convert(source=input, outdir=output, colors=colorscheme,
784                        show=show, markup=markup, quiet=quiet, header=header,
785                        footer=footer, linenumbers=linenumbers, form=form)
786        else:
787            raise PathError, 'File does not exists!'
788            Usage()
789
790######################################################### Simple markup tests
791
792def _test(show=0, quiet=0):
793    """Test the parser and most of the functions.
794
795       There are 19 test total(eight colorschemes in three diffrent markups,
796       and a str2file test. Most functions are tested by this.
797    """
798    fi = sys.argv[0]
799    if not fi.endswith('.exe'):# Do not test if frozen as an archive
800        # this is a collection of test, most things are covered.
801        path2file(fi, '/tmp/null.html', null, show=show, quiet=quiet)
802        path2file(fi, '/tmp/null_css.html', null, show=show,
803                  markup='css', quiet=quiet)
804        path2file(fi, '/tmp/mono.html', mono, show=show, quiet=quiet)
805        path2file(fi, '/tmp/mono_css.html', mono, show=show,
806                  markup='css', quiet=quiet)
807        path2file(fi, '/tmp/lite.html', lite, show=show, quiet=quiet)
808        path2file(fi, '/tmp/lite_css.html', lite, show=show,
809                  markup='css', quiet=quiet, header='', footer='',
810                  linenumbers=1)
811        path2file(fi, '/tmp/lite_xhtml.html', lite, show=show,
812                  markup='xhtml', quiet=quiet)
813        path2file(fi, '/tmp/dark.html', dark, show=show, quiet=quiet)
814        path2file(fi, '/tmp/dark_css.html', dark, show=show,
815                  markup='css', quiet=quiet, linenumbers=1)
816        path2file(fi, '/tmp/dark2.html', dark2, show=show, quiet=quiet)
817        path2file(fi, '/tmp/dark2_css.html', dark2, show=show,
818                  markup='css', quiet=quiet)
819        path2file(fi, '/tmp/dark2_xhtml.html', dark2, show=show,
820                  markup='xhtml', quiet=quiet, header='', footer='',
821                  linenumbers=1, form='external')
822        path2file(fi, '/tmp/idle.html', idle, show=show, quiet=quiet)
823        path2file(fi, '/tmp/idle_css.html', idle, show=show,
824                  markup='css', quiet=quiet)
825        path2file(fi, '/tmp/viewcvs.html', viewcvs, show=show,
826                  quiet=quiet, linenumbers=1)
827        path2file(fi, '/tmp/viewcvs_css.html', viewcvs, show=show,
828                  markup='css', linenumbers=1, quiet=quiet)
829        path2file(fi, '/tmp/pythonwin.html', pythonwin, show=show,
830                  quiet=quiet)
831        path2file(fi, '/tmp/pythonwin_css.html', pythonwin, show=show,
832                  markup='css', quiet=quiet)
833        teststr=r'''"""This is a test of decorators and other things"""
834# This should be line 421...
835@whatever(arg,arg2)
836@A @B(arghh) @C
837def LlamaSaysNi(arg='Ni!',arg2="RALPH"):
838   """This docstring is deeply disturbed by all the llama references"""
839   print '%s The Wonder Llama says %s'% (arg2,arg)
840# So I was like duh!, and he was like ya know?!,
841# and so we were both like huh...wtf!? RTFM!! LOL!!;)
842@staticmethod## Double comments are KewL.
843def LlamasRLumpy():
844   """This docstring is too sexy to be here.
845   """
846   u"""
847=============================
848A Mse once bit my sister...
849=============================
850   """
851   ## Relax, this won't hurt a bit, just a simple, painless procedure,
852   ## hold still while I get the anesthetizing hammer.
853   m = {'three':'1','won':'2','too':'3'}
854   o = r'fishy\fishy\fishy/fish\oh/where/is\my/little\..'
855   python = uR"""
856 No realli! She was Karving her initials  the mse with the sharpened end 
857 of an interspace tthbrush given her by Svenge - her brother-in-law -an Oslo
858 dentist and star of many Norwegian mies: "The H Hands of an Oslo         
859 Dentist", "Fillings of Passion", "The Huge Mars of Horst Nordfink"..."""
860   RU"""142 MEXICAN WHOOPING LLAMAS"""#<-Can you fit 142 llamas in a red box?
861   n = u' HERMSGERVリRDENBRリTBリRDA ' + """ YUTTE """
862   t = """SAMALLNIATNUOMNAIRODAUCE"""+"DENIARTYLLAICEPS04"
863   ## We apologise for the fault in the
864   ## comments. Those responsible have been
865   ## sacked.
866   y = '14 NORTH CHILEAN GUANACOS \
867(CLOSELY RELATED TO THE LLAMA)'
868   rules = [0,1,2,3,4,5]
869   print y'''
870        htmlPath = os.path.abspath('/tmp/strtest_lines.html')
871        str2file(teststr, htmlPath, colors=dark, markup='xhtml',
872                 linenumbers=420, show=show)
873        _printinfo("  wrote %s" % htmlPath, quiet)
874        htmlPath = os.path.abspath('/tmp/strtest_nolines.html')
875        str2file(teststr, htmlPath, colors=dark, markup='xhtml',
876                 show=show)
877        _printinfo("  wrote %s" % htmlPath, quiet)
878    else:
879        Usage()
880    return
881
882# emacs wants this: '
883
884####################################################### User funtctions
885
886def str2stdout(sourcestring, colors=None, title='', markup='html',
887                 header=None, footer=None,
888                 linenumbers=0, form=None):
889    """Converts a code(string) to colorized HTML. Writes to stdout.
890
891       form='code',or'snip' (for "<pre>yourcode</pre>" only)
892       colors=null,mono,lite,dark,dark2,idle,or pythonwin
893    """
894    Parser(sourcestring, colors=colors, title=title, markup=markup,
895           header=header, footer=footer,
896           linenumbers=linenumbers).format(form)
897
898def path2stdout(sourcepath, title='', colors=None, markup='html',
899                   header=None, footer=None,
900                   linenumbers=0, form=None):
901    """Converts code(file) to colorized HTML. Writes to stdout.
902
903       form='code',or'snip' (for "<pre>yourcode</pre>" only)
904       colors=null,mono,lite,dark,dark2,idle,or pythonwin
905    """
906    sourcestring = open(sourcepath).read()
907    Parser(sourcestring, colors=colors, title=sourcepath,
908           markup=markup, header=header, footer=footer,
909           linenumbers=linenumbers).format(form)
910
911def str2html(sourcestring, colors=None, title='',
912               markup='html', header=None, footer=None,
913               linenumbers=0, form=None):
914    """Converts a code(string) to colorized HTML. Returns an HTML string.
915
916       form='code',or'snip' (for "<pre>yourcode</pre>" only)
917       colors=null,mono,lite,dark,dark2,idle,or pythonwin
918    """
919    stringIO = StringIO.StringIO()
920    Parser(sourcestring, colors=colors, title=title, out=stringIO,
921           markup=markup, header=header, footer=footer,
922           linenumbers=linenumbers).format(form)
923    stringIO.seek(0)
924    return stringIO.read()
925 
926def str2css(sourcestring, colors=None, title='',
927              markup='css', header=None, footer=None, 
928              linenumbers=0, form=None):
929    """Converts a code string to colorized CSS/HTML. Returns CSS/HTML string
930       
931       If form != None then this will return (stylesheet_str, code_str)
932       colors=null,mono,lite,dark,dark2,idle,or pythonwin
933    """
934    if markup.lower() not in ['css' ,'xhtml']:
935        markup = 'css'
936    stringIO = StringIO.StringIO()
937    parse = Parser(sourcestring, colors=colors, title=title,
938                   out=stringIO, markup=markup,
939                   header=header, footer=footer,
940                   linenumbers=linenumbers)
941    parse.format(form)
942    stringIO.seek(0)
943    if form != None:
944        return parse._sendCSSStyle(external=1), stringIO.read()
945    else:
946        return None, stringIO.read()
947
948def str2markup(sourcestring, colors=None, title = '',
949               markup='xhtml', header=None, footer=None,
950              linenumbers=0, form=None):
951    """ Convert code strings into ([stylesheet or None], colorized string) """
952    if markup.lower() == 'html':
953        return None, str2html(sourcestring, colors=colors, title=title,
954                   header=header, footer=footer, markup=markup,
955                   linenumbers=linenumbers, form=form)
956    else:
957        return str2css(sourcestring, colors=colors, title=title,
958                   header=header, footer=footer, markup=markup,
959                   linenumbers=linenumbers, form=form)
960
961def str2file(sourcestring, outfile, colors=None, title='',
962               markup='html', header=None, footer=None,
963               linenumbers=0, show=0, dosheet=1, form=None):
964    """Converts a code string to a file.
965
966       makes no attempt at correcting bad pathnames
967    """
968    css , html = str2markup(sourcestring, colors=colors, title='',
969                    markup=markup, header=header, footer=footer,
970                    linenumbers=linenumbers, form=form)
971    # write html
972    f = open(outfile,'wt')
973    f.writelines(html)
974    f.close()
975    #write css
976    if css != None and dosheet:
977        dir = os.path.dirname(outfile)
978        outcss = os.path.join(dir,'pystyle.css')
979        f = open(outcss,'wt')
980        f.writelines(css)
981        f.close()
982    if show:
983        showpage(outfile)
984
985def path2html(sourcepath, colors=None, markup='html',
986                header=None, footer=None,
987                linenumbers=0, form=None):
988    """Converts code(file) to colorized HTML. Returns an HTML string.
989
990       form='code',or'snip' (for "<pre>yourcode</pre>" only)
991       colors=null,mono,lite,dark,dark2,idle,or pythonwin
992    """
993    stringIO = StringIO.StringIO()
994    sourcestring = open(sourcepath).read()
995    Parser(sourcestring, colors, title=sourcepath, out=stringIO,
996           markup=markup, header=header, footer=footer,
997           linenumbers=linenumbers).format(form)
998    stringIO.seek(0)
999    return stringIO.read()
1000
1001def convert(source, outdir=None, colors=None,
1002              show=0, markup='html', quiet=0,
1003              header=None, footer=None, linenumbers=0, form=None):
1004    """Takes a file or dir as input and places the html in the outdir.
1005
1006       If outdir is none it defaults to the input dir
1007    """
1008    count=0
1009    # If it is a filename then path2file
1010    if not os.path.isdir(source):
1011        if os.path.isfile(source):
1012            count+=1
1013            path2file(source, outdir, colors, show, markup,
1014                     quiet, form, header, footer, linenumbers, count)
1015        else:
1016            raise PathError, 'File does not exist!'
1017    # If we pass in a dir we need to walkdir for files.
1018    # Then we need to colorize them with path2file
1019    else:
1020        fileList = walkdir(source)
1021        if fileList != None:
1022            # make sure outdir is a dir
1023            if outdir != None:
1024                if os.path.splitext(outdir)[1] != '':
1025                    outdir = os.path.split(outdir)[0]
1026            for item in fileList:
1027                count+=1
1028                path2file(item, outdir, colors, show, markup,
1029                          quiet, form, header, footer, linenumbers, count)
1030            _printinfo('Completed colorizing %s files.'%str(count), quiet)
1031        else:
1032            _printinfo("No files to convert in dir.", quiet)
1033
1034def path2file(sourcePath, out=None, colors=None, show=0,
1035                markup='html', quiet=0, form=None,
1036                header=None, footer=None, linenumbers=0, count=1):
1037    """ Converts python source to html file"""
1038    # If no outdir is given we use the sourcePath
1039    if out == None:#this is a guess
1040        htmlPath = sourcePath + '.html'
1041    else:
1042        # If we do give an out_dir, and it does
1043        # not exist , it will be created.
1044        if os.path.splitext(out)[1] == '':
1045            if not os.path.isdir(out):
1046                os.makedirs(out)
1047            sourceName = os.path.basename(sourcePath)
1048            htmlPath = os.path.join(out,sourceName)+'.html'
1049        # If we do give an out_name, and its dir does
1050        # not exist , it will be created.
1051        else:
1052            outdir = os.path.split(out)[0]
1053            if not os.path.isdir(outdir):
1054                os.makedirs(outdir)
1055            htmlPath = out
1056    htmlPath = os.path.abspath(htmlPath)
1057    # Open the text and do the parsing.
1058    source = open(sourcePath).read()
1059    parse = Parser(source, colors, sourcePath, open(htmlPath, 'wt'),
1060                   markup, header, footer, linenumbers)
1061    parse.format(form)
1062    _printinfo("  wrote %s" % htmlPath, quiet)
1063    # html markup will ignore the external flag, but
1064    # we need to stop the blank file from being written.
1065    if form == 'external' and count == 1 and markup != 'html':
1066        cssSheet = parse._sendCSSStyle(external=1)
1067        cssPath = os.path.join(os.path.dirname(htmlPath),'pystyle.css')
1068        css = open(cssPath, 'wt')
1069        css.write(cssSheet)
1070        css.close()
1071        _printinfo("    wrote %s" % cssPath, quiet)
1072    if show:
1073        # load HTML page into the default web browser.
1074        showpage(htmlPath)
1075    return htmlPath
1076
1077def tagreplace(sourcestr, colors=lite, markup='xhtml',
1078               linenumbers=0, dosheet=1, tagstart='<PY>'.lower(),
1079               tagend='</PY>'.lower(), stylesheet='pystyle.css'):
1080    """This is a helper function for pageconvert. Returns css, page.
1081    """
1082    if markup.lower() != 'html':
1083        link  = '<link rel="stylesheet" href="%s" type="text/css"/></head>'
1084        css = link%stylesheet
1085        if sourcestr.find(css) == -1:
1086            sourcestr = sourcestr.replace('</head>', css, 1)
1087    starttags = sourcestr.count(tagstart)
1088    endtags = sourcestr.count(tagend)
1089    if starttags:
1090        if starttags == endtags:
1091            for _ in range(starttags):
1092               datastart = sourcestr.find(tagstart)
1093               dataend = sourcestr.find(tagend)
1094               data = sourcestr[datastart+len(tagstart):dataend]
1095               data = unescape(data)
1096               css , data = str2markup(data, colors=colors,
1097                         linenumbers=linenumbers, markup=markup, form='embed')
1098               start = sourcestr[:datastart]
1099               end = sourcestr[dataend+len(tagend):]
1100               sourcestr =  ''.join([start,data,end])
1101        else:
1102            raise InputError,'Tag mismatch!\nCheck %s,%s tags'%tagstart,tagend
1103    if not dosheet:
1104        css = None
1105    return css, sourcestr
1106   
1107def pageconvert(path, out=None, colors=lite, markup='xhtml', linenumbers=0,
1108                  dosheet=1, tagstart='<PY>'.lower(), tagend='</PY>'.lower(),
1109                  stylesheet='pystyle', show=1, returnstr=0):
1110    """This function can colorize Python source
1111
1112       that is written in a webpage enclosed in tags.
1113    """
1114    if out == None:
1115        out = os.path.dirname(path)
1116    infile = open(path, 'r').read()
1117    css,page  = tagreplace(sourcestr=infile,colors=colors,
1118                   markup=markup, linenumbers=linenumbers, dosheet=dosheet,
1119                   tagstart=tagstart, tagend=tagend, stylesheet=stylesheet)
1120    if not returnstr:
1121        newpath = os.path.abspath(os.path.join(
1122                  out,'tmp', os.path.basename(path)))
1123        if not os.path.exists(newpath):
1124            try:
1125                os.makedirs(os.path.dirname(newpath))
1126            except:
1127                pass#traceback.print_exc()
1128                #Usage()
1129        y = open(newpath, 'w')
1130        y.write(page)
1131        y.close()
1132        if css:
1133            csspath = os.path.abspath(os.path.join(
1134                      out,'tmp','%s.css'%stylesheet))
1135            x = open(csspath,'w')
1136            x.write(css)
1137            x.close()
1138        if show:
1139            try:
1140                os.startfile(newpath)
1141            except:
1142                traceback.print_exc()
1143        return newpath
1144    else:
1145        return css, page
1146
1147##################################################################### helpers
1148
1149def walkdir(dir):
1150    """Return a list of .py and .pyw files from a given directory.
1151
1152       This function can be written as a generator Python 2.3, or a genexp
1153       in Python 2.4. But 2.2 and 2.1 would be left out....
1154    """
1155    # Get a list of files that match *.py*
1156    GLOB_PATTERN = os.path.join(dir, "*.[p][y]*")
1157    pathlist = glob.glob(GLOB_PATTERN)
1158    # Now filter out all but py and pyw
1159    filterlist = [x for x in pathlist
1160                        if x.endswith('.py')
1161                        or x.endswith('.pyw')]
1162    if filterlist != []:
1163        # if we have a list send it
1164        return filterlist
1165    else:
1166        return None
1167
1168def showpage(path):
1169    """Helper function to open webpages"""
1170    try:
1171        webbrowser.open_new(os.path.abspath(path))
1172    except:
1173        traceback.print_exc()
1174
1175def _printinfo(message, quiet):
1176    """Helper to print messages"""
1177    if not quiet:
1178        print message
1179
1180def escape(text):
1181     """escape text for html. similar to cgi.escape"""
1182     text = text.replace("&", "&amp;")
1183     text = text.replace("<", "&lt;")
1184     text = text.replace(">", "&gt;")
1185     return text
1186
1187def unescape(text):
1188     """unsecape escaped text"""
1189     text = text.replace("&quot;", '"')
1190     text = text.replace("&gt;", ">")
1191     text = text.replace("&lt;", "<")
1192     text = text.replace("&amp;", "&")
1193     return text
1194
1195########################################################### Custom Exceptions
1196
1197class PySourceColorError(Exception):
1198    # Base for custom errors
1199    def __init__(self, msg=''):
1200        self._msg = msg
1201        Exception.__init__(self, msg)
1202    def __repr__(self):
1203        return self._msg
1204    __str__ = __repr__
1205
1206class PathError(PySourceColorError):
1207    def __init__(self, msg):
1208       PySourceColorError.__init__(self,
1209         'Path error! : %s'% msg)
1210
1211class InputError(PySourceColorError):
1212   def __init__(self, msg):
1213       PySourceColorError.__init__(self,
1214         'Input error! : %s'% msg)
1215
1216########################################################## Python code parser
1217
1218class Parser(object):
1219
1220    """MoinMoin python parser heavily chopped :)"""
1221
1222    def __init__(self, raw, colors=None, title='', out=sys.stdout,
1223                   markup='html', header=None, footer=None, linenumbers=0):
1224        """Store the source text & set some flags"""
1225        if colors == None:
1226            colors = defaultColors
1227        self.raw = raw.expandtabs().rstrip()
1228        self.title = os.path.basename(title)
1229        self.out = out
1230        self.line = ''
1231        self.lasttext = ''
1232        self.argFlag = 0
1233        self.classFlag = 0
1234        self.defFlag = 0
1235        self.decoratorFlag = 0
1236        self.external = 0
1237        self.markup = markup.upper()
1238        self.colors = colors
1239        self.header = header
1240        self.footer = footer
1241        self.doArgs = 1 #  overrides the new tokens
1242        self.doNames = 1 #  overrides the new tokens
1243        self.doMathOps = 1 #  overrides the new tokens
1244        self.doBrackets = 1 #  overrides the new tokens
1245        self.doURL = 1 # override url conversion
1246        self.LINENUMHOLDER = "___line___".upper()
1247        self.LINESTART = "___start___".upper()
1248        self.skip = 0
1249        # add space left side of code for padding.Override in color dict.
1250        self.extraspace = self.colors.get(EXTRASPACE, '')
1251        # Linenumbers less then zero also have numberlinks
1252        self.dolinenums = self.linenum = abs(linenumbers)
1253        if linenumbers < 0:
1254            self.numberlinks = 1
1255        else:
1256            self.numberlinks = 0
1257
1258    def format(self, form=None):
1259        """Parse and send the colorized source"""
1260        if form in ('snip','code'):
1261            self.addEnds = 0
1262        elif form == 'embed':
1263            self.addEnds = 0
1264            self.external = 1
1265        else:
1266            if form == 'external':
1267                self.external = 1
1268            self.addEnds = 1
1269
1270        # Store line offsets in self.lines
1271        self.lines = [0, 0]
1272        pos = 0
1273
1274        # Add linenumbers
1275        if self.dolinenums:
1276            start=self.LINENUMHOLDER+' '+self.extraspace
1277        else:
1278            start=''+self.extraspace
1279        newlines = []
1280        lines = self.raw.splitlines(0)
1281        for l in lines:
1282             # span and div escape for customizing and embedding raw text
1283             if (l.startswith('#$#')
1284                  or l.startswith('#%#')
1285                  or l.startswith('#@#')):   
1286                newlines.append(l)
1287             else:
1288                # kludge for line spans in css,xhtml
1289                if self.markup in ['XHTML','CSS']:
1290                    newlines.append(self.LINESTART+' '+start+l)
1291                else:
1292                    newlines.append(start+l)
1293        self.raw = "\n".join(newlines)+'\n'# plus an extra newline at the end
1294
1295        # Gather lines
1296        while 1:
1297            pos = self.raw.find('\n', pos) + 1
1298            if not pos: break
1299            self.lines.append(pos)
1300        self.lines.append(len(self.raw))
1301
1302        # Wrap text in a filelike object
1303        self.pos = 0
1304        text = StringIO.StringIO(self.raw)
1305       
1306        # Markup start
1307        if self.addEnds:
1308            self._doPageStart()
1309        else:
1310            self._doSnippetStart()
1311
1312        ## Tokenize calls the __call__
1313        ## function for each token till done.
1314        # Parse the source and write out the results.
1315        try:
1316            tokenize.tokenize(text.readline, self)
1317        except tokenize.TokenError, ex:
1318            msg = ex[0]
1319            line = ex[1][0]
1320            self.out.write("<h3>ERROR: %s</h3>%s\n"%
1321                            (msg, self.raw[self.lines[line]:]))
1322            #traceback.print_exc()
1323
1324        # Markup end
1325        if self.addEnds:
1326            self._doPageEnd()
1327        else:
1328            self._doSnippetEnd()
1329
1330    def __call__(self, toktype, toktext, (srow,scol), (erow,ecol), line):
1331        """Token handler. Order is important do not rearrange."""
1332        self.line = line
1333        # Calculate new positions
1334        oldpos = self.pos
1335        newpos = self.lines[srow] + scol
1336        self.pos = newpos + len(toktext)
1337        # Handle newlines
1338        if toktype in (token.NEWLINE, tokenize.NL):
1339            self.decoratorFlag = self.argFlag = 0
1340            # kludge for line spans in css,xhtml
1341            if self.markup in ['XHTML','CSS']:
1342                self.out.write('</span>')
1343            self.out.write('\n')
1344            return
1345
1346        # Send the original whitespace, and tokenize backslashes if present.
1347        # Tokenizer.py just sends continued line backslashes with whitespace.
1348        # This is a hack to tokenize continued line slashes as operators.
1349        # Should continued line backslashes be treated as operators
1350        # or some other token?
1351
1352        if newpos > oldpos:
1353            if self.raw[oldpos:newpos].isspace():
1354                # consume a single space after linestarts and linenumbers
1355                # had to have them so tokenizer could seperate them.
1356                # multiline strings are handled by do_Text functions
1357                if self.lasttext != self.LINESTART \
1358                        and self.lasttext != self.LINENUMHOLDER:
1359                    self.out.write(self.raw[oldpos:newpos])
1360                else:
1361                    self.out.write(self.raw[oldpos+1:newpos])
1362            else:
1363                slash = self.raw[oldpos:newpos].find('\\')+oldpos
1364                self.out.write(self.raw[oldpos:slash])
1365                getattr(self, '_send%sText'%(self.markup))(OPERATOR, '\\')
1366                self.linenum+=1
1367                # kludge for line spans in css,xhtml
1368                if self.markup in ['XHTML','CSS']:
1369                    self.out.write('</span>')
1370                self.out.write(self.raw[slash+1:newpos])
1371
1372        # Skip indenting tokens
1373        if toktype in (token.INDENT, token.DEDENT):
1374            self.pos = newpos
1375            return
1376
1377        # Look for operators
1378        if token.LPAR <= toktype and toktype <= token.OP:
1379            # Trap decorators py2.4 >
1380            if toktext == '@':
1381                toktype = DECORATOR
1382                # Set a flag if this was the decorator start so
1383                # the decorator name and arguments can be identified
1384                self.decoratorFlag = self.argFlag = 1
1385            else:
1386                if self.doArgs:
1387                    # Find the start for arguments
1388                    if toktext == '(' and self.argFlag:
1389                        self.argFlag = 2
1390                    # Find the end for arguments
1391                    elif toktext == ':':
1392                        self.argFlag = 0
1393                ## Seperate the diffrent operator types
1394                # Brackets
1395                if self.doBrackets and toktext in ['[',']','(',')','{','}']:
1396                    toktype = BRACKETS
1397                # Math operators
1398                elif self.doMathOps and toktext in ['*=','**=','-=','+=','|=',
1399                                                      '%=','>>=','<<=','=','^=',
1400                                                      '/=', '+','-','**','*','/','%']:
1401                    toktype = MATH_OPERATOR
1402                # Operator
1403                else:
1404                    toktype = OPERATOR
1405                    # example how flags should work.
1406                    # def fun(arg=argvalue,arg2=argvalue2):
1407                    # 0   1  2 A 1   N    2 A  1    N     0
1408                    if toktext == "=" and self.argFlag == 2:
1409                         self.argFlag = 1
1410                    elif toktext == "," and self.argFlag == 1:
1411                        self.argFlag = 2
1412        # Look for keywords
1413        elif toktype == NAME and keyword.iskeyword(toktext):
1414            toktype = KEYWORD
1415            # Set a flag if this was the class / def start so
1416            # the class / def name and arguments can be identified
1417            if toktext in ['class', 'def']:
1418                if toktext =='class' and \
1419                         not line[:line.find('class')].endswith('.'):
1420                    self.classFlag = self.argFlag = 1
1421                elif toktext == 'def' and \
1422                         not line[:line.find('def')].endswith('.'):
1423                    self.defFlag = self.argFlag = 1
1424                else:
1425                    # must have used a keyword as a name i.e. self.class
1426                    toktype = ERRORTOKEN
1427
1428        # Look for class, def, decorator name
1429        elif (self.classFlag or self.defFlag or self.decoratorFlag) \
1430                and self.doNames:
1431            if self.classFlag:
1432                self.classFlag = 0
1433                toktype = CLASS_NAME
1434            elif self.defFlag:
1435                self.defFlag = 0
1436                toktype = DEF_NAME
1437            elif self.decoratorFlag:
1438                self.decoratorFlag = 0
1439                toktype = DECORATOR_NAME
1440
1441        # Look for strings
1442        # Order of evaluation is important do not change.
1443        elif toktype == token.STRING:
1444            text = toktext.lower()
1445            # TRIPLE DOUBLE QUOTE's
1446            if (text[:3] == '"""'):
1447                toktype = TRIPLEDOUBLEQUOTE
1448            elif (text[:4] == 'r"""'):
1449                toktype = TRIPLEDOUBLEQUOTE_R
1450            elif (text[:4] == 'u"""' or
1451                   text[:5] == 'ur"""'):
1452                toktype = TRIPLEDOUBLEQUOTE_U
1453            # DOUBLE QUOTE's
1454            elif (text[:1] == '"'):
1455                toktype = DOUBLEQUOTE
1456            elif (text[:2] == 'r"'):
1457                toktype = DOUBLEQUOTE_R
1458            elif (text[:2] == 'u"' or
1459                   text[:3] == 'ur"'):
1460                toktype = DOUBLEQUOTE_U
1461            # TRIPLE SINGLE QUOTE's
1462            elif (text[:3] == "'''"):
1463                 toktype = TRIPLESINGLEQUOTE
1464            elif (text[:4] == "r'''"):
1465                toktype = TRIPLESINGLEQUOTE_R
1466            elif (text[:4] == "u'''" or
1467                   text[:5] == "ur'''"):
1468                toktype = TRIPLESINGLEQUOTE_U
1469            # SINGLE QUOTE's
1470            elif (text[:1] == "'"):
1471                toktype = SINGLEQUOTE
1472            elif (text[:2] == "r'"):
1473                toktype = SINGLEQUOTE_R
1474            elif (text[:2] == "u'" or
1475                   text[:3] == "ur'"):
1476                toktype = SINGLEQUOTE_U
1477
1478            # test for invalid string declaration
1479            if self.lasttext.lower() == 'ru':
1480                toktype = ERRORTOKEN
1481           
1482        # Look for comments
1483        elif toktype == COMMENT:
1484            if toktext[:2] == "##":
1485                toktype = DOUBLECOMMENT
1486            elif toktext[:3] == '#$#':
1487                toktype = TEXT
1488                self.textFlag = 'SPAN'
1489                toktext = toktext[3:]
1490            elif toktext[:3] == '#%#':
1491                toktype = TEXT
1492                self.textFlag = 'DIV'
1493                toktext = toktext[3:]
1494            elif toktext[:3] == '#@#':
1495                toktype = TEXT
1496                self.textFlag = 'RAW'
1497                toktext = toktext[3:]
1498            if self.doURL:
1499                # this is a 'fake helper function'
1500                # url(URI,Alias_name) or url(URI)
1501                url_pos = toktext.find('url(')
1502                if url_pos != -1:
1503                    before = toktext[:url_pos]
1504                    url = toktext[url_pos+4:]
1505                    splitpoint = url.find(',')
1506                    endpoint = url.find(')')
1507                    after = url[endpoint+1:]
1508                    url = url[:endpoint]
1509                    if splitpoint != -1:
1510                        urlparts = url.split(',',1)
1511                        toktext = '%s<a href="%s">%s</a>%s'%(
1512                                   before,urlparts[0],urlparts[1].lstrip(),after)
1513                    else:
1514                        toktext = '%s<a href="%s">%s</a>%s'%(before,url,url,after)
1515                       
1516        # Seperate errors from decorators
1517        elif toktype == ERRORTOKEN:
1518            # Bug fix for < py2.4
1519            # space between decorators
1520            if self.argFlag and toktext.isspace():
1521                #toktype = NAME
1522                self.out.write(toktext)
1523                return
1524            # Bug fix for py2.2 linenumbers with decorators
1525            elif toktext.isspace():
1526                # What if we have a decorator after a >>> or ...
1527                #p = line.find('@')
1528                #if p >= 0 and not line[:p].isspace():
1529                    #self.out.write(toktext)
1530                    #return
1531                if self.skip:
1532                    self.skip=0
1533                    return
1534                else:           
1535                    self.out.write(toktext)
1536                    return
1537            # trap decorators < py2.4
1538            elif toktext == '@':
1539                toktype = DECORATOR
1540                # Set a flag if this was the decorator start so
1541                # the decorator name and arguments can be identified
1542                self.decoratorFlag = self.argFlag = 1
1543
1544        # Seperate args from names
1545        elif (self.argFlag == 2 and
1546              toktype == NAME and
1547              toktext != 'None' and
1548              self.doArgs):
1549            toktype = ARGS
1550
1551        # Look for line numbers
1552        # The conversion code for them is in the send_text functions.
1553        if toktext in [self.LINENUMHOLDER,self.LINESTART]:
1554            toktype = LINENUMBER
1555            # if we don't have linenumbers set flag
1556            # to skip the trailing space from linestart
1557            if toktext == self.LINESTART and not self.dolinenums \
1558                                or toktext == self.LINENUMHOLDER:
1559                self.skip=1
1560
1561
1562        # Skip blank token that made it thru
1563        ## bugfix for the last empty tag.
1564        if toktext == '':
1565            return
1566
1567        # Last token text history
1568        self.lasttext = toktext
1569       
1570        # escape all but the urls in the comments
1571        if toktype in (DOUBLECOMMENT, COMMENT):
1572            if toktext.find('<a href=') == -1:
1573                toktext = escape(toktext)
1574            else:
1575                pass
1576        elif toktype == TEXT:
1577            pass
1578        else:
1579            toktext = escape(toktext)
1580
1581        # Send text for any markup
1582        getattr(self, '_send%sText'%(self.markup))(toktype, toktext)
1583        return
1584
1585    ################################################################# Helpers
1586
1587    def _doSnippetStart(self):
1588        if self.markup == 'HTML':
1589            # Start of html snippet
1590            self.out.write('<pre>\n')
1591        else:
1592            # Start of css/xhtml snippet
1593            self.out.write(self.colors.get(CODESTART,'<pre class="py">\n'))
1594
1595    def _doSnippetEnd(self):
1596        # End of html snippet
1597        self.out.write(self.colors.get(CODEEND,'</pre>\n'))
1598
1599    ######################################################## markup selectors
1600
1601    def _getFile(self, filepath):
1602        try:
1603            _file = open(filepath,'r')
1604            content = _file.read()
1605            _file.close()
1606        except:
1607            traceback.print_exc()
1608            content = ''
1609        return content
1610
1611    def _doPageStart(self):
1612        getattr(self, '_do%sStart'%(self.markup))()
1613
1614    def _doPageHeader(self):
1615        if self.header != None:
1616            if self.header.find('#$#') != -1 or \
1617                self.header.find('#$#') != -1 or \
1618                self.header.find('#%#') != -1:
1619                self.out.write(self.header[3:])
1620            else:
1621                if self.header != '':
1622                    self.header = self._getFile(self.header)
1623                getattr(self, '_do%sHeader'%(self.markup))()
1624
1625    def _doPageFooter(self):
1626        if self.footer != None:
1627            if self.footer.find('#$#') != -1 or \
1628                self.footer.find('#@#') != -1 or \
1629                self.footer.find('#%#') != -1:
1630                self.out.write(self.footer[3:])
1631            else:
1632                if self.footer != '':
1633                    self.footer = self._getFile(self.footer)
1634                getattr(self, '_do%sFooter'%(self.markup))()
1635
1636    def _doPageEnd(self):
1637        getattr(self, '_do%sEnd'%(self.markup))()
1638
1639    ################################################### color/style retrieval
1640    ## Some of these are not used anymore but are kept for documentation
1641
1642    def _getLineNumber(self):
1643        num = self.linenum
1644        self.linenum+=1
1645        return  str(num).rjust(5)+" "
1646
1647    def _getTags(self, key):
1648        # style tags
1649        return self.colors.get(key, self.colors[NAME])[0]
1650
1651    def _getForeColor(self, key):
1652        # get text foreground color, if not set to black
1653        color = self.colors.get(key, self.colors[NAME])[1]
1654        if color[:1] != '#':
1655            color = '#000000'
1656        return color
1657
1658    def _getBackColor(self, key):
1659        # get text background color
1660        return self.colors.get(key, self.colors[NAME])[2]
1661
1662    def _getPageColor(self):
1663        # get page background color
1664        return self.colors.get(PAGEBACKGROUND, '#FFFFFF')
1665
1666    def _getStyle(self, key):
1667        # get the token style from the color dictionary
1668        return self.colors.get(key, self.colors[NAME])
1669
1670    def _getMarkupClass(self, key):
1671        # get the markup class name from the markup dictionary
1672        return MARKUPDICT.get(key, MARKUPDICT[NAME])
1673
1674    def _getDocumentCreatedBy(self):
1675        return '<!--This document created by %s ver.%s on: %s-->\n'%(
1676                  __title__,__version__,time.ctime())
1677
1678    ################################################### HTML markup functions
1679
1680    def _doHTMLStart(self):
1681        # Start of html page
1682        self.out.write('<!DOCTYPE html PUBLIC \
1683"-//W3C//DTD HTML 4.01//EN">\n')
1684        self.out.write('<html><head><title>%s</title>\n'%(self.title))
1685        self.out.write(self._getDocumentCreatedBy())
1686        self.out.write('<meta http-equiv="Content-Type" \
1687content="text/html;charset=iso-8859-1">\n')
1688        # Get background
1689        self.out.write('</head><body bgcolor="%s">\n'%self._getPageColor())
1690        self._doPageHeader()
1691        self.out.write('<pre>')
1692
1693    def _getHTMLStyles(self, toktype, toktext):
1694        # Get styles
1695        tags, color = self.colors.get(toktype, self.colors[NAME])[:2]#
1696        tagstart=[]
1697        tagend=[]
1698        # check for styles and set them if needed.
1699        if 'b' in tags:#Bold
1700            tagstart.append('<b>')
1701            tagend.append('</b>')
1702        if 'i' in tags:#Italics
1703            tagstart.append('<i>')
1704            tagend.append('</i>')
1705        if 'u' in tags:#Underline
1706            tagstart.append('<u>')
1707            tagend.append('</u>')
1708        # HTML tags should be paired like so : <b><i><u>Doh!</u></i></b>
1709        tagend.reverse()
1710        starttags="".join(tagstart)
1711        endtags="".join(tagend)
1712        return starttags,endtags,color
1713
1714    def _sendHTMLText(self, toktype, toktext):
1715        numberlinks = self.numberlinks
1716       
1717        # If it is an error, set a red box around the bad tokens
1718        # older browsers should ignore it
1719        if toktype == ERRORTOKEN:
1720            style = ' style="border: solid 1.5pt #FF0000;"'
1721        else:
1722            style = ''
1723        # Get styles
1724        starttag, endtag, color = self._getHTMLStyles(toktype, toktext)
1725        # This is a hack to 'fix' multi-line  strings.
1726        # Multi-line strings are treated as only one token
1727        # even though they can be several physical lines.
1728        # That makes it hard to spot the start of a line,
1729        # because at this level all we know about are tokens.
1730       
1731        if toktext.count(self.LINENUMHOLDER):
1732            # rip apart the string and separate it by line.
1733            # count lines and change all linenum token to line numbers.
1734            # embedded all the new font tags inside the current one.
1735            # Do this by ending the tag first then writing our new tags,
1736            # then starting another font tag exactly like the first one.
1737            if toktype == LINENUMBER:
1738                splittext = toktext.split(self.LINENUMHOLDER)
1739            else:   
1740                splittext = toktext.split(self.LINENUMHOLDER+' ')
1741            store = []
1742            store.append(splittext.pop(0))
1743            lstarttag, lendtag, lcolor = self._getHTMLStyles(LINENUMBER, toktext)
1744            count = len(splittext)
1745            for item in splittext:
1746                num =  self._getLineNumber()
1747                if numberlinks:
1748                    numstrip = num.strip()
1749                    content = '<a name="%s" href="#%s">%s</a>' \
1750                              %(numstrip,numstrip,num)
1751                else:
1752                    content = num
1753                if count <= 1:
1754                    endtag,starttag = '',''
1755                linenumber = ''.join([endtag,'<font color=', lcolor, '>',
1756                            lstarttag, content, lendtag, '</font>' ,starttag])
1757                store.append(linenumber+item)
1758            toktext = ''.join(store)
1759        # send text
1760        ## Output optimization
1761        # skip font tag if black text, but styles will still be sent. (b,u,i)
1762        if color !='#000000':
1763            startfont = '<font color="%s"%s>'%(color, style)
1764            endfont = '</font>'
1765        else:
1766            startfont, endfont = ('','')
1767        if toktype != LINENUMBER:
1768            self.out.write(''.join([startfont,starttag,
1769                                     toktext,endtag,endfont]))
1770        else:
1771            self.out.write(toktext)
1772        return
1773
1774    def _doHTMLHeader(self):
1775        # Optional
1776        if self.header != '':
1777            self.out.write('%s\n'%self.header)
1778        else:
1779            color = self._getForeColor(NAME)
1780            self.out.write('<b><font color="%s"># %s \
1781                            <br># %s</font></b><hr>\n'%
1782                           (color, self.title, time.ctime()))
1783
1784    def _doHTMLFooter(self):
1785        # Optional
1786        if self.footer != '':
1787            self.out.write('%s\n'%self.footer)
1788        else:
1789            color = self._getForeColor(NAME)
1790            self.out.write('<b><font color="%s"> \
1791                            <hr># %s<br># %s</font></b>\n'%
1792                           (color, self.title, time.ctime()))
1793
1794    def _doHTMLEnd(self):
1795        # End of html page
1796        self.out.write('</pre>\n')
1797        # Write a little info at the bottom
1798        self._doPageFooter()
1799        self.out.write('</body></html>\n')
1800
1801    #################################################### CSS markup functions
1802
1803    def _getCSSStyle(self, key):
1804        # Get the tags and colors from the dictionary
1805        tags, forecolor, backcolor = self._getStyle(key)
1806        style=[]
1807        border = None
1808        bordercolor = None
1809        tags = tags.lower()
1810        if tags:
1811            # get the border color if specified
1812            # the border color will be appended to
1813            # the list after we define a border
1814            if '#' in tags:# border color
1815                start = tags.find('#')
1816                end = start + 7
1817                bordercolor = tags[start:end]
1818                tags.replace(bordercolor,'',1)
1819            # text styles
1820            if 'b' in tags:# Bold
1821                style.append('font-weight:bold;')
1822            else:
1823                style.append('font-weight:normal;')   
1824            if 'i' in tags:# Italic
1825                style.append('font-style:italic;')
1826            if 'u' in tags:# Underline
1827                style.append('text-decoration:underline;')
1828            # border size
1829            if 'l' in tags:# thick border
1830                size='thick'
1831            elif 'm' in tags:# medium border
1832                size='medium'
1833            elif 't' in tags:# thin border
1834                size='thin'
1835            else:# default
1836                size='medium'
1837            # border styles
1838            if 'n' in tags:# inset border
1839                border='inset'
1840            elif 'o' in tags:# outset border
1841                border='outset'
1842            elif 'r' in tags:# ridge border
1843                border='ridge'
1844            elif 'g' in tags:# groove border
1845                border='groove'
1846            elif '=' in tags:# double border
1847                border='double'
1848            elif '.' in tags:# dotted border
1849                border='dotted'
1850            elif '-' in tags:# dashed border
1851                border='dashed'
1852            elif 's' in tags:# solid border
1853                border='solid'
1854            # border type check
1855            seperate_sides=0
1856            for side in ['<','>','^','v']:
1857                if side in tags:
1858                    seperate_sides+=1
1859            # border box or seperate sides
1860            if seperate_sides==0 and border:
1861                    style.append('border: %s %s;'%(border,size))
1862            else:
1863                if border == None:
1864                   border = 'solid'
1865                if 'v' in tags:# bottom border
1866                    style.append('border-bottom:%s %s;'%(border,size))
1867                if '<' in tags:# left border
1868                    style.append('border-left:%s %s;'%(border,size))
1869                if '>' in tags:# right border
1870                    style.append('border-right:%s %s;'%(border,size))
1871                if '^' in tags:# top border
1872                    style.append('border-top:%s %s;'%(border,size))
1873        else:
1874            style.append('font-weight:normal;')# css inherited style fix   
1875        # we have to define our borders before we set colors
1876        if bordercolor:
1877            style.append('border-color:%s;'%bordercolor)
1878        # text forecolor 
1879        style.append('color:%s;'% forecolor)
1880        # text backcolor
1881        if backcolor:
1882            style.append('background-color:%s;'%backcolor)
1883        return (self._getMarkupClass(key),' '.join(style))
1884
1885    def _sendCSSStyle(self, external=0):
1886        """ create external and internal style sheets"""
1887        styles = []
1888        external += self.external
1889        if not external:
1890            styles.append('<style type="text/css">\n<!--\n')
1891        # Get page background color and write styles ignore any we don't know
1892        styles.append('body { background:%s; }\n'%self._getPageColor())
1893        # write out the various css styles
1894        for key in MARKUPDICT:
1895            styles.append('.%s { %s }\n'%self._getCSSStyle(key))
1896        # If you want to style the pre tag you must modify the color dict.
1897        #  Example:
1898        #  lite[PY] = .py {border: solid thin #000000;background:#555555}\n'''
1899        styles.append(self.colors.get(PY, '.py { }\n'))
1900        # Extra css can be added here
1901        # add CSSHOOK to the color dict if you need it.
1902        # Example:
1903        #lite[CSSHOOK] = """.mytag { border: solid thin #000000; } \n
1904        #                   .myothertag { font-weight:bold; )\n"""
1905        styles.append(self.colors.get(CSSHOOK,''))
1906        if not self.external:
1907             styles.append('--></style>\n')
1908        return ''.join(styles)
1909
1910    def _doCSSStart(self):
1911        # Start of css/html 4.01 page
1912        self.out.write('<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">\n')
1913        self.out.write('<html><head><title>%s</title>\n'%(self.title))
1914        self.out.write(self._getDocumentCreatedBy())
1915        self.out.write('<meta http-equiv="Content-Type" \
1916content="text/html;charset=iso-8859-1">\n')
1917        self._doCSSStyleSheet()
1918        self.out.write('</head>\n<body>\n')
1919        # Write a little info at the top.
1920        self._doPageHeader()
1921        self.out.write(self.colors.get(CODESTART,'<pre class="py">\n'))
1922        return
1923
1924    def _doCSSStyleSheet(self):
1925        if not self.external:
1926            # write an embedded style sheet
1927            self.out.write(self._sendCSSStyle())
1928        else:
1929            # write a link to an external style sheet
1930            self.out.write('<link rel="stylesheet" \
1931href="pystyle.css" type="text/css">')
1932        return
1933
1934    def _sendCSSText(self, toktype, toktext):
1935        # This is a hack to 'fix' multi-line strings.
1936        # Multi-line strings are treated as only one token
1937        # even though they can be several physical lines.
1938        # That makes it hard to spot the start of a line,
1939        # because at this level all we know about are tokens.
1940        markupclass = MARKUPDICT.get(toktype, MARKUPDICT[NAME])
1941        # if it is a LINENUMBER type then we can skip the rest
1942        if toktext == self.LINESTART and toktype == LINENUMBER:
1943            self.out.write('<span class="py_line">')
1944            return
1945        if toktext.count(self.LINENUMHOLDER):
1946            # rip apart the string and separate it by line
1947            # count lines and change all linenum token to line numbers
1948            # also convert linestart and lineend tokens
1949            # <linestart> <lnumstart> lnum <lnumend> text <lineend>
1950            #################################################
1951            newmarkup = MARKUPDICT.get(LINENUMBER, MARKUPDICT[NAME])
1952            lstartspan = '<span class="%s">'%(newmarkup)
1953            if toktype == LINENUMBER:
1954                splittext = toktext.split(self.LINENUMHOLDER)
1955            else:   
1956                splittext = toktext.split(self.LINENUMHOLDER+' ')
1957            store = []
1958            # we have already seen the first linenumber token
1959            # so we can skip the first one
1960            store.append(splittext.pop(0))
1961            for item in splittext:
1962                num = self._getLineNumber()
1963                if self.numberlinks:
1964                    numstrip = num.strip()
1965                    content= '<a name="%s" href="#%s">%s</a>' \
1966                              %(numstrip,numstrip,num)
1967                else:
1968                    content = num
1969                linenumber= ''.join([lstartspan,content,'</span>'])
1970                store.append(linenumber+item)
1971            toktext = ''.join(store)
1972        if toktext.count(self.LINESTART):
1973            # wraps the textline in a line span
1974            # this adds a lot of kludges, is it really worth it?
1975            store = []
1976            parts = toktext.split(self.LINESTART+' ')
1977            # handle the first part differently
1978            # the whole token gets wraqpped in a span later on
1979            first = parts.pop(0)
1980            # place spans before the newline
1981            pos = first.rfind('\n')
1982            if pos != -1:
1983                first=first[:pos]+'</span></span>'+first[pos:]
1984            store.append(first)
1985            #process the rest of the string
1986            for item in parts:
1987                #handle line numbers if present
1988                if self.dolinenums:
1989                    item = item.replace('</span>',
1990                           '</span><span class="%s">'%(markupclass))
1991                else:
1992                    item = '<span class="%s">%s'%(markupclass,item)
1993                # add endings for line and string tokens
1994                pos = item.rfind('\n')
1995                if pos != -1:
1996                    item=item[:pos]+'</span></span>\n'
1997                store.append(item)
1998            # add start tags for lines
1999            toktext = '<span class="py_line">'.join(store)
2000        # Send text
2001        if toktype != LINENUMBER:
2002            if toktype == TEXT and self.textFlag == 'DIV':
2003                startspan = '<div class="%s">'%(markupclass)
2004                endspan = '</div>'
2005            elif toktype == TEXT and self.textFlag == 'RAW':
2006                startspan,endspan = ('','')
2007            else:
2008                startspan = '<span class="%s">'%(markupclass)
2009                endspan = '</span>'
2010            self.out.write(''.join([startspan, toktext, endspan]))
2011        else:
2012            self.out.write(toktext)
2013        return
2014
2015    def _doCSSHeader(self):
2016        if self.header != '':
2017            self.out.write('%s\n'%self.header)
2018        else:
2019            name = MARKUPDICT.get(NAME)
2020            self.out.write('<div class="%s"># %s <br> \
2021# %s</div><hr>\n'%(name, self.title, time.ctime()))
2022
2023    def _doCSSFooter(self):
2024        # Optional
2025        if self.footer != '':
2026            self.out.write('%s\n'%self.footer)
2027        else:
2028            self.out.write('<hr><div class="%s"># %s <br> \
2029# %s</div>\n'%(MARKUPDICT.get(NAME),self.title, time.ctime()))
2030
2031    def _doCSSEnd(self):
2032        # End of css/html page
2033        self.out.write(self.colors.get(CODEEND,'</pre>\n'))
2034        # Write a little info at the bottom
2035        self._doPageFooter()
2036        self.out.write('</body></html>\n')
2037        return
2038
2039    ################################################## XHTML markup functions
2040
2041    def _doXHTMLStart(self):
2042        # XHTML is really just XML + HTML 4.01.
2043        # We only need to change the page headers,
2044        # and a few tags to get valid XHTML.
2045        # Start of xhtml page
2046        self.out.write('<?xml version="1.0"?>\n \
2047<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"\n \
2048    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n \
2049<html xmlns="http://www.w3.org/1999/xhtml">\n')
2050        self.out.write('<head><title>%s</title>\n'%(self.title))
2051        self.out.write(self._getDocumentCreatedBy())
2052        self.out.write('<meta http-equiv="Content-Type" \
2053content="text/html;charset=iso-8859-1"/>\n')
2054        self._doXHTMLStyleSheet()
2055        self.out.write('</head>\n<body>\n')
2056        # Write a little info at the top.
2057        self._doPageHeader()
2058        self.out.write(self.colors.get(CODESTART,'<pre class="py">\n'))
2059        return
2060
2061    def _doXHTMLStyleSheet(self):
2062        if not self.external:
2063            # write an embedded style sheet
2064            self.out.write(self._sendCSSStyle())
2065        else:
2066            # write a link to an external style sheet
2067            self.out.write('<link rel="stylesheet" \
2068href="pystyle.css" type="text/css"/>\n')
2069        return
2070
2071    def _sendXHTMLText(self, toktype, toktext):
2072        self._sendCSSText(toktype, toktext)
2073
2074    def _doXHTMLHeader(self):
2075        # Optional
2076        if self.header:
2077            self.out.write('%s\n'%self.header)
2078        else:
2079            name = MARKUPDICT.get(NAME)
2080            self.out.write('<div class="%s"># %s <br/> \
2081# %s</div><hr/>\n '%(
2082            name, self.title, time.ctime()))
2083
2084    def _doXHTMLFooter(self):
2085        # Optional
2086        if self.footer:
2087            self.out.write('%s\n'%self.footer)
2088        else:
2089            self.out.write('<hr/><div class="%s"># %s <br/> \
2090# %s</div>\n'%(MARKUPDICT.get(NAME), self.title, time.ctime()))
2091
2092    def _doXHTMLEnd(self):
2093        self._doCSSEnd()
2094
2095#############################################################################
2096
2097if __name__ == '__main__':
2098    cli()
2099
2100#############################################################################
2101# PySourceColor.py
2102# 2004, 2005 M.E.Farmer Jr.
2103# Python license
Note: リポジトリブラウザについてのヘルプは TracBrowser を参照してください。