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