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

Created by PySourceColor.py
copyright M.E.Farmer Jr. 2004