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