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