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