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