summaryrefslogtreecommitdiff
path: root/paste/util
diff options
context:
space:
mode:
authorVictor Stinner <victor.stinner@gmail.com>2015-04-21 15:47:06 +0200
committerVictor Stinner <victor.stinner@gmail.com>2015-04-21 15:47:06 +0200
commit87f53c983e7b94ed1d8389f97c605c6afe5b1c57 (patch)
treec8daef3d8a9adadecc3825ed228b1f1dbba4f596 /paste/util
parent04ecd5542849191457ccfc3e42a39452db47825a (diff)
downloadpaste-87f53c983e7b94ed1d8389f97c605c6afe5b1c57.tar.gz
Convert files from file format DOS to UNIX
Diffstat (limited to 'paste/util')
-rw-r--r--paste/util/PySourceColor.py4204
-rw-r--r--paste/util/datetimeutil.py722
2 files changed, 2463 insertions, 2463 deletions
diff --git a/paste/util/PySourceColor.py b/paste/util/PySourceColor.py
index 1c11041..c576ead 100644
--- a/paste/util/PySourceColor.py
+++ b/paste/util/PySourceColor.py
@@ -1,2102 +1,2102 @@
-# -*- coding: Latin-1 -*-
-"""
-PySourceColor: color Python source code
-"""
-
-"""
- PySourceColor.py
-
-----------------------------------------------------------------------------
-
- A python source to colorized html/css/xhtml converter.
- Hacked by M.E.Farmer Jr. 2004, 2005
- Python license
-
-----------------------------------------------------------------------------
-
- - HTML markup does not create w3c valid html, but it works on every
- browser i've tried so far.(I.E.,Mozilla/Firefox,Opera,Konqueror,wxHTML).
- - CSS markup is w3c validated html 4.01 strict,
- but will not render correctly on all browsers.
- - XHTML markup is w3c validated xhtml 1.0 strict,
- like html 4.01, will not render correctly on all browsers.
-
-----------------------------------------------------------------------------
-
-Features:
-
- -Three types of markup:
- html (default)
- css/html 4.01 strict
- xhtml 1.0 strict
-
- -Can tokenize and colorize:
- 12 types of strings
- 2 comment types
- numbers
- operators
- brackets
- math operators
- class / name
- def / name
- decorator / name
- keywords
- arguments class/def/decorator
- linenumbers
- names
- text
-
- -Eight colorschemes built-in:
- null
- mono
- lite (default)
- dark
- dark2
- idle
- viewcvs
- pythonwin
-
- -Header and footer
- set to '' for builtin header / footer.
- give path to a file containing the html
- you want added as header or footer.
-
- -Arbitrary text and html
- html markup converts all to raw (TEXT token)
- #@# for raw -> send raw text.
- #$# for span -> inline html and text.
- #%# for div -> block level html and text.
-
- -Linenumbers
- Supports all styles. New token is called LINENUMBER.
- Defaults to NAME if not defined.
-
- Style options
-
- -ALL markups support these text styles:
- b = bold
- i = italic
- u = underline
- -CSS and XHTML has limited support for borders:
- HTML markup functions will ignore these.
- Optional: Border color in RGB hex
- Defaults to the text forecolor.
- #rrggbb = border color
- Border size:
- l = thick
- m = medium
- t = thin
- Border type:
- - = dashed
- . = dotted
- s = solid
- d = double
- g = groove
- r = ridge
- n = inset
- o = outset
- You can specify multiple sides,
- they will all use the same style.
- Optional: Default is full border.
- v = bottom
- < = left
- > = right
- ^ = top
- NOTE: Specify the styles you want.
- The markups will ignore unsupported styles
- Also note not all browsers can show these options
-
- -All tokens default to NAME if not defined
- so the only absolutely critical ones to define are:
- NAME, ERRORTOKEN, PAGEBACKGROUND
-
-----------------------------------------------------------------------------
-
-Example usage::
-
- # import
- import PySourceColor as psc
- psc.convert('c:/Python22/PySourceColor.py', colors=psc.idle, show=1)
-
- # from module import *
- from PySourceColor import *
- convert('c:/Python22/Lib', colors=lite, markup="css",
- header='#$#<b>This is a simpe heading</b><hr/>')
-
- # How to use a custom colorscheme, and most of the 'features'
- from PySourceColor import *
- new = {
- ERRORTOKEN: ('bui','#FF8080',''),
- DECORATOR_NAME: ('s','#AACBBC',''),
- DECORATOR: ('n','#333333',''),
- NAME: ('t.<v','#1133AA','#DDFF22'),
- NUMBER: ('','#236676','#FF5555'),
- OPERATOR: ('b','#454567','#BBBB11'),
- MATH_OPERATOR: ('','#935623','#423afb'),
- BRACKETS: ('b','#ac34bf','#6457a5'),
- COMMENT: ('t-#0022FF','#545366','#AABBFF'),
- DOUBLECOMMENT: ('<l#553455','#553455','#FF00FF'),
- CLASS_NAME: ('m^v-','#000000','#FFFFFF'),
- DEF_NAME: ('l=<v','#897845','#000022'),
- KEYWORD: ('.b','#345345','#FFFF22'),
- SINGLEQUOTE: ('mn','#223344','#AADDCC'),
- SINGLEQUOTE_R: ('','#344522',''),
- SINGLEQUOTE_U: ('','#234234',''),
- DOUBLEQUOTE: ('m#0022FF','#334421',''),
- DOUBLEQUOTE_R: ('','#345345',''),
- DOUBLEQUOTE_U: ('','#678673',''),
- TRIPLESINGLEQUOTE: ('tv','#FFFFFF','#000000'),
- TRIPLESINGLEQUOTE_R: ('tbu','#443256','#DDFFDA'),
- TRIPLESINGLEQUOTE_U: ('','#423454','#DDFFDA'),
- TRIPLEDOUBLEQUOTE: ('li#236fd3b<>','#000000','#FFFFFF'),
- TRIPLEDOUBLEQUOTE_R: ('tub','#000000','#FFFFFF'),
- TRIPLEDOUBLEQUOTE_U: ('-', '#CCAABB','#FFFAFF'),
- LINENUMBER: ('ib-','#ff66aa','#7733FF'),]
- TEXT: ('','#546634',''),
- PAGEBACKGROUND: '#FFFAAA',
- }
- if __name__ == '__main__':
- import sys
- convert(sys.argv[1], './xhtml.html', colors=new, markup='xhtml', show=1,
- linenumbers=1)
- convert(sys.argv[1], './html.html', colors=new, markup='html', show=1,
- linenumbers=1)
-
-"""
-
-__all__ = ['ERRORTOKEN','DECORATOR_NAME', 'DECORATOR', 'ARGS', 'EXTRASPACE',
- 'NAME', 'NUMBER', 'OPERATOR', 'COMMENT', 'MATH_OPERATOR',
- 'DOUBLECOMMENT', 'CLASS_NAME', 'DEF_NAME', 'KEYWORD', 'BRACKETS',
- 'SINGLEQUOTE','SINGLEQUOTE_R','SINGLEQUOTE_U','DOUBLEQUOTE',
- 'DOUBLEQUOTE_R', 'DOUBLEQUOTE_U', 'TRIPLESINGLEQUOTE', 'TEXT',
- 'TRIPLESINGLEQUOTE_R', 'TRIPLESINGLEQUOTE_U', 'TRIPLEDOUBLEQUOTE',
- 'TRIPLEDOUBLEQUOTE_R', 'TRIPLEDOUBLEQUOTE_U', 'PAGEBACKGROUND',
- 'LINENUMBER', 'CODESTART', 'CODEEND', 'PY', 'TOKEN_NAMES', 'CSSHOOK',
- 'null', 'mono', 'lite', 'dark','dark2', 'pythonwin','idle',
- 'viewcvs', 'Usage', 'cli', 'str2stdout', 'path2stdout', 'Parser',
- 'str2file', 'str2html', 'str2css', 'str2markup', 'path2file',
- 'path2html', 'convert', 'walkdir', 'defaultColors', 'showpage',
- 'pageconvert','tagreplace', 'MARKUPDICT']
-__title__ = 'PySourceColor'
-__version__ = "2.1a"
-__date__ = '25 April 2005'
-__author__ = "M.E.Farmer Jr."
-__credits__ = '''This was originally based on a python recipe
-submitted by Jürgen Hermann to ASPN. Now based on the voices in my head.
-M.E.Farmer 2004, 2005
-Python license
-'''
-import os
-import sys
-import time
-import glob
-import getopt
-import keyword
-import token
-import tokenize
-import traceback
-from six.moves import cStringIO as StringIO
-# Do not edit
-NAME = token.NAME
-NUMBER = token.NUMBER
-COMMENT = tokenize.COMMENT
-OPERATOR = token.OP
-ERRORTOKEN = token.ERRORTOKEN
-ARGS = token.NT_OFFSET + 1
-DOUBLECOMMENT = token.NT_OFFSET + 2
-CLASS_NAME = token.NT_OFFSET + 3
-DEF_NAME = token.NT_OFFSET + 4
-KEYWORD = token.NT_OFFSET + 5
-SINGLEQUOTE = token.NT_OFFSET + 6
-SINGLEQUOTE_R = token.NT_OFFSET + 7
-SINGLEQUOTE_U = token.NT_OFFSET + 8
-DOUBLEQUOTE = token.NT_OFFSET + 9
-DOUBLEQUOTE_R = token.NT_OFFSET + 10
-DOUBLEQUOTE_U = token.NT_OFFSET + 11
-TRIPLESINGLEQUOTE = token.NT_OFFSET + 12
-TRIPLESINGLEQUOTE_R = token.NT_OFFSET + 13
-TRIPLESINGLEQUOTE_U = token.NT_OFFSET + 14
-TRIPLEDOUBLEQUOTE = token.NT_OFFSET + 15
-TRIPLEDOUBLEQUOTE_R = token.NT_OFFSET + 16
-TRIPLEDOUBLEQUOTE_U = token.NT_OFFSET + 17
-PAGEBACKGROUND = token.NT_OFFSET + 18
-DECORATOR = token.NT_OFFSET + 19
-DECORATOR_NAME = token.NT_OFFSET + 20
-BRACKETS = token.NT_OFFSET + 21
-MATH_OPERATOR = token.NT_OFFSET + 22
-LINENUMBER = token.NT_OFFSET + 23
-TEXT = token.NT_OFFSET + 24
-PY = token.NT_OFFSET + 25
-CODESTART = token.NT_OFFSET + 26
-CODEEND = token.NT_OFFSET + 27
-CSSHOOK = token.NT_OFFSET + 28
-EXTRASPACE = token.NT_OFFSET + 29
-
-# markup classname lookup
-MARKUPDICT = {
- ERRORTOKEN: 'py_err',
- DECORATOR_NAME: 'py_decn',
- DECORATOR: 'py_dec',
- ARGS: 'py_args',
- NAME: 'py_name',
- NUMBER: 'py_num',
- OPERATOR: 'py_op',
- COMMENT: 'py_com',
- DOUBLECOMMENT: 'py_dcom',
- CLASS_NAME: 'py_clsn',
- DEF_NAME: 'py_defn',
- KEYWORD: 'py_key',
- SINGLEQUOTE: 'py_sq',
- SINGLEQUOTE_R: 'py_sqr',
- SINGLEQUOTE_U: 'py_squ',
- DOUBLEQUOTE: 'py_dq',
- DOUBLEQUOTE_R: 'py_dqr',
- DOUBLEQUOTE_U: 'py_dqu',
- TRIPLESINGLEQUOTE: 'py_tsq',
- TRIPLESINGLEQUOTE_R: 'py_tsqr',
- TRIPLESINGLEQUOTE_U: 'py_tsqu',
- TRIPLEDOUBLEQUOTE: 'py_tdq',
- TRIPLEDOUBLEQUOTE_R: 'py_tdqr',
- TRIPLEDOUBLEQUOTE_U: 'py_tdqu',
- BRACKETS: 'py_bra',
- MATH_OPERATOR: 'py_mop',
- LINENUMBER: 'py_lnum',
- TEXT: 'py_text',
- }
-# might help users that want to create custom schemes
-TOKEN_NAMES= {
- ERRORTOKEN:'ERRORTOKEN',
- DECORATOR_NAME:'DECORATOR_NAME',
- DECORATOR:'DECORATOR',
- ARGS:'ARGS',
- NAME:'NAME',
- NUMBER:'NUMBER',
- OPERATOR:'OPERATOR',
- COMMENT:'COMMENT',
- DOUBLECOMMENT:'DOUBLECOMMENT',
- CLASS_NAME:'CLASS_NAME',
- DEF_NAME:'DEF_NAME',
- KEYWORD:'KEYWORD',
- SINGLEQUOTE:'SINGLEQUOTE',
- SINGLEQUOTE_R:'SINGLEQUOTE_R',
- SINGLEQUOTE_U:'SINGLEQUOTE_U',
- DOUBLEQUOTE:'DOUBLEQUOTE',
- DOUBLEQUOTE_R:'DOUBLEQUOTE_R',
- DOUBLEQUOTE_U:'DOUBLEQUOTE_U',
- TRIPLESINGLEQUOTE:'TRIPLESINGLEQUOTE',
- TRIPLESINGLEQUOTE_R:'TRIPLESINGLEQUOTE_R',
- TRIPLESINGLEQUOTE_U:'TRIPLESINGLEQUOTE_U',
- TRIPLEDOUBLEQUOTE:'TRIPLEDOUBLEQUOTE',
- TRIPLEDOUBLEQUOTE_R:'TRIPLEDOUBLEQUOTE_R',
- TRIPLEDOUBLEQUOTE_U:'TRIPLEDOUBLEQUOTE_U',
- BRACKETS:'BRACKETS',
- MATH_OPERATOR:'MATH_OPERATOR',
- LINENUMBER:'LINENUMBER',
- TEXT:'TEXT',
- PAGEBACKGROUND:'PAGEBACKGROUND',
- }
-
-######################################################################
-# Edit colors and styles to taste
-# Create your own scheme, just copy one below , rename and edit.
-# Custom styles must at least define NAME, ERRORTOKEN, PAGEBACKGROUND,
-# all missing elements will default to NAME.
-# See module docstring for details on style attributes.
-######################################################################
-# Copy null and use it as a starter colorscheme.
-null = {# tokentype: ('tags border_color', 'textforecolor', 'textbackcolor')
- ERRORTOKEN: ('','#000000',''),# Error token
- DECORATOR_NAME: ('','#000000',''),# Decorator name
- DECORATOR: ('','#000000',''),# @ symbol
- ARGS: ('','#000000',''),# class,def,deco arguments
- NAME: ('','#000000',''),# All other python text
- NUMBER: ('','#000000',''),# 0->10
- OPERATOR: ('','#000000',''),# ':','<=',';',',','.','==', etc
- MATH_OPERATOR: ('','#000000',''),# '+','-','=','','**',etc
- BRACKETS: ('','#000000',''),# '[',']','(',')','{','}'
- COMMENT: ('','#000000',''),# Single comment
- DOUBLECOMMENT: ('','#000000',''),## Double comment
- CLASS_NAME: ('','#000000',''),# Class name
- DEF_NAME: ('','#000000',''),# Def name
- KEYWORD: ('','#000000',''),# Python keywords
- SINGLEQUOTE: ('','#000000',''),# 'SINGLEQUOTE'
- SINGLEQUOTE_R: ('','#000000',''),# r'SINGLEQUOTE'
- SINGLEQUOTE_U: ('','#000000',''),# u'SINGLEQUOTE'
- DOUBLEQUOTE: ('','#000000',''),# "DOUBLEQUOTE"
- DOUBLEQUOTE_R: ('','#000000',''),# r"DOUBLEQUOTE"
- DOUBLEQUOTE_U: ('','#000000',''),# u"DOUBLEQUOTE"
- TRIPLESINGLEQUOTE: ('','#000000',''),# '''TRIPLESINGLEQUOTE'''
- TRIPLESINGLEQUOTE_R: ('','#000000',''),# r'''TRIPLESINGLEQUOTE'''
- TRIPLESINGLEQUOTE_U: ('','#000000',''),# u'''TRIPLESINGLEQUOTE'''
- TRIPLEDOUBLEQUOTE: ('','#000000',''),# """TRIPLEDOUBLEQUOTE"""
- TRIPLEDOUBLEQUOTE_R: ('','#000000',''),# r"""TRIPLEDOUBLEQUOTE"""
- TRIPLEDOUBLEQUOTE_U: ('','#000000',''),# u"""TRIPLEDOUBLEQUOTE"""
- TEXT: ('','#000000',''),# non python text
- LINENUMBER: ('>ti#555555','#000000',''),# Linenumbers
- PAGEBACKGROUND: '#FFFFFF'# set the page background
- }
-
-mono = {
- ERRORTOKEN: ('s#FF0000','#FF8080',''),
- DECORATOR_NAME: ('bu','#000000',''),
- DECORATOR: ('b','#000000',''),
- ARGS: ('b','#555555',''),
- NAME: ('','#000000',''),
- NUMBER: ('b','#000000',''),
- OPERATOR: ('b','#000000',''),
- MATH_OPERATOR: ('b','#000000',''),
- BRACKETS: ('b','#000000',''),
- COMMENT: ('i','#999999',''),
- DOUBLECOMMENT: ('b','#999999',''),
- CLASS_NAME: ('bu','#000000',''),
- DEF_NAME: ('b','#000000',''),
- KEYWORD: ('b','#000000',''),
- SINGLEQUOTE: ('','#000000',''),
- SINGLEQUOTE_R: ('','#000000',''),
- SINGLEQUOTE_U: ('','#000000',''),
- DOUBLEQUOTE: ('','#000000',''),
- DOUBLEQUOTE_R: ('','#000000',''),
- DOUBLEQUOTE_U: ('','#000000',''),
- TRIPLESINGLEQUOTE: ('','#000000',''),
- TRIPLESINGLEQUOTE_R: ('','#000000',''),
- TRIPLESINGLEQUOTE_U: ('','#000000',''),
- TRIPLEDOUBLEQUOTE: ('i','#000000',''),
- TRIPLEDOUBLEQUOTE_R: ('i','#000000',''),
- TRIPLEDOUBLEQUOTE_U: ('i','#000000',''),
- TEXT: ('','#000000',''),
- LINENUMBER: ('>ti#555555','#000000',''),
- PAGEBACKGROUND: '#FFFFFF'
- }
-
-dark = {
- ERRORTOKEN: ('s#FF0000','#FF8080',''),
- DECORATOR_NAME: ('b','#FFBBAA',''),
- DECORATOR: ('b','#CC5511',''),
- ARGS: ('b','#DDDDFF',''),
- NAME: ('','#DDDDDD',''),
- NUMBER: ('','#FF0000',''),
- OPERATOR: ('b','#FAF785',''),
- MATH_OPERATOR: ('b','#FAF785',''),
- BRACKETS: ('b','#FAF785',''),
- COMMENT: ('','#45FCA0',''),
- DOUBLECOMMENT: ('i','#A7C7A9',''),
- CLASS_NAME: ('b','#B666FD',''),
- DEF_NAME: ('b','#EBAE5C',''),
- KEYWORD: ('b','#8680FF',''),
- SINGLEQUOTE: ('','#F8BAFE',''),
- SINGLEQUOTE_R: ('','#F8BAFE',''),
- SINGLEQUOTE_U: ('','#F8BAFE',''),
- DOUBLEQUOTE: ('','#FF80C0',''),
- DOUBLEQUOTE_R: ('','#FF80C0',''),
- DOUBLEQUOTE_U: ('','#FF80C0',''),
- TRIPLESINGLEQUOTE: ('','#FF9595',''),
- TRIPLESINGLEQUOTE_R: ('','#FF9595',''),
- TRIPLESINGLEQUOTE_U: ('','#FF9595',''),
- TRIPLEDOUBLEQUOTE: ('','#B3FFFF',''),
- TRIPLEDOUBLEQUOTE_R: ('','#B3FFFF',''),
- TRIPLEDOUBLEQUOTE_U: ('','#B3FFFF',''),
- TEXT: ('','#FFFFFF',''),
- LINENUMBER: ('>mi#555555','#bbccbb','#333333'),
- PAGEBACKGROUND: '#000000'
- }
-
-dark2 = {
- ERRORTOKEN: ('','#FF0000',''),
- DECORATOR_NAME: ('b','#FFBBAA',''),
- DECORATOR: ('b','#CC5511',''),
- ARGS: ('b','#DDDDDD',''),
- NAME: ('','#C0C0C0',''),
- NUMBER: ('b','#00FF00',''),
- OPERATOR: ('b','#FF090F',''),
- MATH_OPERATOR: ('b','#EE7020',''),
- BRACKETS: ('b','#FFB90F',''),
- COMMENT: ('i','#D0D000','#522000'),#'#88AA88','#11111F'),
- DOUBLECOMMENT: ('i','#D0D000','#522000'),#'#77BB77','#11111F'),
- CLASS_NAME: ('b','#DD4080',''),
- DEF_NAME: ('b','#FF8040',''),
- KEYWORD: ('b','#4726d1',''),
- SINGLEQUOTE: ('','#8080C0',''),
- SINGLEQUOTE_R: ('','#8080C0',''),
- SINGLEQUOTE_U: ('','#8080C0',''),
- DOUBLEQUOTE: ('','#ADB9F1',''),
- DOUBLEQUOTE_R: ('','#ADB9F1',''),
- DOUBLEQUOTE_U: ('','#ADB9F1',''),
- TRIPLESINGLEQUOTE: ('','#00C1C1',''),#A050C0
- TRIPLESINGLEQUOTE_R: ('','#00C1C1',''),#A050C0
- TRIPLESINGLEQUOTE_U: ('','#00C1C1',''),#A050C0
- TRIPLEDOUBLEQUOTE: ('','#33E3E3',''),#B090E0
- TRIPLEDOUBLEQUOTE_R: ('','#33E3E3',''),#B090E0
- TRIPLEDOUBLEQUOTE_U: ('','#33E3E3',''),#B090E0
- TEXT: ('','#C0C0C0',''),
- LINENUMBER: ('>mi#555555','#bbccbb','#333333'),
- PAGEBACKGROUND: '#000000'
- }
-
-lite = {
- ERRORTOKEN: ('s#FF0000','#FF8080',''),
- DECORATOR_NAME: ('b','#BB4422',''),
- DECORATOR: ('b','#3333AF',''),
- ARGS: ('b','#000000',''),
- NAME: ('','#333333',''),
- NUMBER: ('b','#DD2200',''),
- OPERATOR: ('b','#000000',''),
- MATH_OPERATOR: ('b','#000000',''),
- BRACKETS: ('b','#000000',''),
- COMMENT: ('','#007F00',''),
- DOUBLECOMMENT: ('','#608060',''),
- CLASS_NAME: ('b','#0000DF',''),
- DEF_NAME: ('b','#9C7A00',''),#f09030
- KEYWORD: ('b','#0000AF',''),
- SINGLEQUOTE: ('','#600080',''),
- SINGLEQUOTE_R: ('','#600080',''),
- SINGLEQUOTE_U: ('','#600080',''),
- DOUBLEQUOTE: ('','#A0008A',''),
- DOUBLEQUOTE_R: ('','#A0008A',''),
- DOUBLEQUOTE_U: ('','#A0008A',''),
- TRIPLESINGLEQUOTE: ('','#337799',''),
- TRIPLESINGLEQUOTE_R: ('','#337799',''),
- TRIPLESINGLEQUOTE_U: ('','#337799',''),
- TRIPLEDOUBLEQUOTE: ('','#1166AA',''),
- TRIPLEDOUBLEQUOTE_R: ('','#1166AA',''),
- TRIPLEDOUBLEQUOTE_U: ('','#1166AA',''),
- TEXT: ('','#000000',''),
- LINENUMBER: ('>ti#555555','#000000',''),
- PAGEBACKGROUND: '#FFFFFF'
- }
-
-idle = {
- ERRORTOKEN: ('s#FF0000','#FF8080',''),
- DECORATOR_NAME: ('','#900090',''),
- DECORATOR: ('','#FF7700',''),
- NAME: ('','#000000',''),
- NUMBER: ('','#000000',''),
- OPERATOR: ('','#000000',''),
- MATH_OPERATOR: ('','#000000',''),
- BRACKETS: ('','#000000',''),
- COMMENT: ('','#DD0000',''),
- DOUBLECOMMENT: ('','#DD0000',''),
- CLASS_NAME: ('','#0000FF',''),
- DEF_NAME: ('','#0000FF',''),
- KEYWORD: ('','#FF7700',''),
- SINGLEQUOTE: ('','#00AA00',''),
- SINGLEQUOTE_R: ('','#00AA00',''),
- SINGLEQUOTE_U: ('','#00AA00',''),
- DOUBLEQUOTE: ('','#00AA00',''),
- DOUBLEQUOTE_R: ('','#00AA00',''),
- DOUBLEQUOTE_U: ('','#00AA00',''),
- TRIPLESINGLEQUOTE: ('','#00AA00',''),
- TRIPLESINGLEQUOTE_R: ('','#00AA00',''),
- TRIPLESINGLEQUOTE_U: ('','#00AA00',''),
- TRIPLEDOUBLEQUOTE: ('','#00AA00',''),
- TRIPLEDOUBLEQUOTE_R: ('','#00AA00',''),
- TRIPLEDOUBLEQUOTE_U: ('','#00AA00',''),
- TEXT: ('','#000000',''),
- LINENUMBER: ('>ti#555555','#000000',''),
- PAGEBACKGROUND: '#FFFFFF'
- }
-
-pythonwin = {
- ERRORTOKEN: ('s#FF0000','#FF8080',''),
- DECORATOR_NAME: ('b','#DD0080',''),
- DECORATOR: ('b','#000080',''),
- ARGS: ('','#000000',''),
- NAME: ('','#303030',''),
- NUMBER: ('','#008080',''),
- OPERATOR: ('','#000000',''),
- MATH_OPERATOR: ('','#000000',''),
- BRACKETS: ('','#000000',''),
- COMMENT: ('','#007F00',''),
- DOUBLECOMMENT: ('','#7F7F7F',''),
- CLASS_NAME: ('b','#0000FF',''),
- DEF_NAME: ('b','#007F7F',''),
- KEYWORD: ('b','#000080',''),
- SINGLEQUOTE: ('','#808000',''),
- SINGLEQUOTE_R: ('','#808000',''),
- SINGLEQUOTE_U: ('','#808000',''),
- DOUBLEQUOTE: ('','#808000',''),
- DOUBLEQUOTE_R: ('','#808000',''),
- DOUBLEQUOTE_U: ('','#808000',''),
- TRIPLESINGLEQUOTE: ('','#808000',''),
- TRIPLESINGLEQUOTE_R: ('','#808000',''),
- TRIPLESINGLEQUOTE_U: ('','#808000',''),
- TRIPLEDOUBLEQUOTE: ('','#808000',''),
- TRIPLEDOUBLEQUOTE_R: ('','#808000',''),
- TRIPLEDOUBLEQUOTE_U: ('','#808000',''),
- TEXT: ('','#303030',''),
- LINENUMBER: ('>ti#555555','#000000',''),
- PAGEBACKGROUND: '#FFFFFF'
- }
-
-viewcvs = {
- ERRORTOKEN: ('s#FF0000','#FF8080',''),
- DECORATOR_NAME: ('','#000000',''),
- DECORATOR: ('','#000000',''),
- ARGS: ('','#000000',''),
- NAME: ('','#000000',''),
- NUMBER: ('','#000000',''),
- OPERATOR: ('','#000000',''),
- MATH_OPERATOR: ('','#000000',''),
- BRACKETS: ('','#000000',''),
- COMMENT: ('i','#b22222',''),
- DOUBLECOMMENT: ('i','#b22222',''),
- CLASS_NAME: ('','#000000',''),
- DEF_NAME: ('b','#0000ff',''),
- KEYWORD: ('b','#a020f0',''),
- SINGLEQUOTE: ('b','#bc8f8f',''),
- SINGLEQUOTE_R: ('b','#bc8f8f',''),
- SINGLEQUOTE_U: ('b','#bc8f8f',''),
- DOUBLEQUOTE: ('b','#bc8f8f',''),
- DOUBLEQUOTE_R: ('b','#bc8f8f',''),
- DOUBLEQUOTE_U: ('b','#bc8f8f',''),
- TRIPLESINGLEQUOTE: ('b','#bc8f8f',''),
- TRIPLESINGLEQUOTE_R: ('b','#bc8f8f',''),
- TRIPLESINGLEQUOTE_U: ('b','#bc8f8f',''),
- TRIPLEDOUBLEQUOTE: ('b','#bc8f8f',''),
- TRIPLEDOUBLEQUOTE_R: ('b','#bc8f8f',''),
- TRIPLEDOUBLEQUOTE_U: ('b','#bc8f8f',''),
- TEXT: ('','#000000',''),
- LINENUMBER: ('>ti#555555','#000000',''),
- PAGEBACKGROUND: '#FFFFFF'
- }
-
-defaultColors = lite
-
-def Usage():
- doc = """
- -----------------------------------------------------------------------------
- PySourceColor.py ver: %s
- -----------------------------------------------------------------------------
- Module summary:
- This module is designed to colorize python source code.
- Input--->python source
- Output-->colorized (html, html4.01/css, xhtml1.0)
- Standalone:
- This module will work from the command line with options.
- This module will work with redirected stdio.
- Imported:
- This module can be imported and used directly in your code.
- -----------------------------------------------------------------------------
- Command line options:
- -h, --help
- Optional-> Display this help message.
- -t, --test
- Optional-> Will ignore all others flags but --profile
- test all schemes and markup combinations
- -p, --profile
- Optional-> Works only with --test or -t
- runs profile.py and makes the test work in quiet mode.
- -i, --in, --input
- Optional-> If you give input on stdin.
- Use any of these for the current dir (.,cwd)
- Input can be file or dir.
- Input from stdin use one of the following (-,stdin)
- If stdin is used as input stdout is output unless specified.
- -o, --out, --output
- Optional-> output dir for the colorized source.
- default: output dir is the input dir.
- To output html to stdout use one of the following (-,stdout)
- Stdout can be used without stdin if you give a file as input.
- -c, --color
- Optional-> null, mono, dark, dark2, lite, idle, pythonwin, viewcvs
- default: dark
- -s, --show
- Optional-> Show page after creation.
- default: no show
- -m, --markup
- Optional-> html, css, xhtml
- css, xhtml also support external stylesheets (-e,--external)
- default: HTML
- -e, --external
- Optional-> use with css, xhtml
- Writes an style sheet instead of embedding it in the page
- saves it as pystyle.css in the same directory.
- html markup will silently ignore this flag.
- -H, --header
- Opional-> add a page header to the top of the output
- -H
- Builtin header (name,date,hrule)
- --header
- You must specify a filename.
- The header file must be valid html
- and must handle its own font colors.
- ex. --header c:/tmp/header.txt
- -F, --footer
- Opional-> add a page footer to the bottom of the output
- -F
- Builtin footer (hrule,name,date)
- --footer
- You must specify a filename.
- The footer file must be valid html
- and must handle its own font colors.
- ex. --footer c:/tmp/footer.txt
- -l, --linenumbers
- Optional-> default is no linenumbers
- Adds line numbers to the start of each line in the code.
- --convertpage
- Given a webpage that has code embedded in tags it will
- convert embedded code to colorized html.
- (see pageconvert for details)
- -----------------------------------------------------------------------------
- Option usage:
- # Test and show pages
- python PySourceColor.py -t -s
- # Test and only show profile results
- python PySourceColor.py -t -p
- # Colorize all .py,.pyw files in cwdir you can also use: (.,cwd)
- python PySourceColor.py -i .
- # Using long options w/ =
- python PySourceColor.py --in=c:/myDir/my.py --color=lite --show
- # Using short options w/out =
- python PySourceColor.py -i c:/myDir/ -c idle -m css -e
- # Using any mix
- python PySourceColor.py --in . -o=c:/myDir --show
- # Place a custom header on your files
- python PySourceColor.py -i . -o c:/tmp -m xhtml --header c:/header.txt
- -----------------------------------------------------------------------------
- Stdio usage:
- # Stdio using no options
- python PySourceColor.py < c:/MyFile.py > c:/tmp/MyFile.html
- # Using stdin alone automatically uses stdout for output: (stdin,-)
- python PySourceColor.py -i- < c:/MyFile.py > c:/tmp/myfile.html
- # Stdout can also be written to directly from a file instead of stdin
- python PySourceColor.py -i c:/MyFile.py -m css -o- > c:/tmp/myfile.html
- # Stdin can be used as input , but output can still be specified
- python PySourceColor.py -i- -o c:/pydoc.py.html -s < c:/Python22/my.py
- _____________________________________________________________________________
- """
- print(doc % (__version__))
- sys.exit(1)
-
-###################################################### Command line interface
-
-def cli():
- """Handle command line args and redirections"""
- try:
- # try to get command line args
- opts, args = getopt.getopt(sys.argv[1:],
- "hseqtplHFi:o:c:m:h:f:",["help", "show", "quiet",
- "test", "external", "linenumbers", "convertpage", "profile",
- "input=", "output=", "color=", "markup=","header=", "footer="])
- except getopt.GetoptError:
- # on error print help information and exit:
- Usage()
- # init some names
- input = None
- output = None
- colorscheme = None
- markup = 'html'
- header = None
- footer = None
- linenumbers = 0
- show = 0
- quiet = 0
- test = 0
- profile = 0
- convertpage = 0
- form = None
- # if we have args then process them
- for o, a in opts:
- if o in ["-h", "--help"]:
- Usage()
- sys.exit()
- if o in ["-o", "--output", "--out"]:
- output = a
- if o in ["-i", "--input", "--in"]:
- input = a
- if input in [".", "cwd"]:
- input = os.getcwd()
- if o in ["-s", "--show"]:
- show = 1
- if o in ["-q", "--quiet"]:
- quiet = 1
- if o in ["-t", "--test"]:
- test = 1
- if o in ["--convertpage"]:
- convertpage = 1
- if o in ["-p", "--profile"]:
- profile = 1
- if o in ["-e", "--external"]:
- form = 'external'
- if o in ["-m", "--markup"]:
- markup = str(a)
- if o in ["-l", "--linenumbers"]:
- linenumbers = 1
- if o in ["--header"]:
- header = str(a)
- elif o == "-H":
- header = ''
- if o in ["--footer"]:
- footer = str(a)
- elif o == "-F":
- footer = ''
- if o in ["-c", "--color"]:
- try:
- colorscheme = globals().get(a.lower())
- except:
- traceback.print_exc()
- Usage()
- if test:
- if profile:
- import profile
- profile.run('_test(show=%s, quiet=%s)'%(show,quiet))
- else:
- # Parse this script in every possible colorscheme and markup
- _test(show,quiet)
- elif input in [None, "-", "stdin"] or output in ["-", "stdout"]:
- # determine if we are going to use stdio
- if input not in [None, "-", "stdin"]:
- if os.path.isfile(input) :
- path2stdout(input, colors=colorscheme, markup=markup,
- linenumbers=linenumbers, header=header,
- footer=footer, form=form)
- else:
- raise PathError('File does not exists!')
- else:
- try:
- if sys.stdin.isatty():
- raise InputError('Please check input!')
- else:
- if output in [None,"-","stdout"]:
- str2stdout(sys.stdin.read(), colors=colorscheme,
- markup=markup, header=header,
- footer=footer, linenumbers=linenumbers,
- form=form)
- else:
- str2file(sys.stdin.read(), outfile=output, show=show,
- markup=markup, header=header, footer=footer,
- linenumbers=linenumbers, form=form)
- except:
- traceback.print_exc()
- Usage()
- else:
- if os.path.exists(input):
- if convertpage:
- # if there was at least an input given we can proceed
- pageconvert(input, out=output, colors=colorscheme,
- show=show, markup=markup,linenumbers=linenumbers)
- else:
- # if there was at least an input given we can proceed
- convert(source=input, outdir=output, colors=colorscheme,
- show=show, markup=markup, quiet=quiet, header=header,
- footer=footer, linenumbers=linenumbers, form=form)
- else:
- raise PathError('File does not exists!')
- Usage()
-
-######################################################### Simple markup tests
-
-def _test(show=0, quiet=0):
- """Test the parser and most of the functions.
-
- There are 19 test total(eight colorschemes in three diffrent markups,
- and a str2file test. Most functions are tested by this.
- """
- fi = sys.argv[0]
- if not fi.endswith('.exe'):# Do not test if frozen as an archive
- # this is a collection of test, most things are covered.
- path2file(fi, '/tmp/null.html', null, show=show, quiet=quiet)
- path2file(fi, '/tmp/null_css.html', null, show=show,
- markup='css', quiet=quiet)
- path2file(fi, '/tmp/mono.html', mono, show=show, quiet=quiet)
- path2file(fi, '/tmp/mono_css.html', mono, show=show,
- markup='css', quiet=quiet)
- path2file(fi, '/tmp/lite.html', lite, show=show, quiet=quiet)
- path2file(fi, '/tmp/lite_css.html', lite, show=show,
- markup='css', quiet=quiet, header='', footer='',
- linenumbers=1)
- path2file(fi, '/tmp/lite_xhtml.html', lite, show=show,
- markup='xhtml', quiet=quiet)
- path2file(fi, '/tmp/dark.html', dark, show=show, quiet=quiet)
- path2file(fi, '/tmp/dark_css.html', dark, show=show,
- markup='css', quiet=quiet, linenumbers=1)
- path2file(fi, '/tmp/dark2.html', dark2, show=show, quiet=quiet)
- path2file(fi, '/tmp/dark2_css.html', dark2, show=show,
- markup='css', quiet=quiet)
- path2file(fi, '/tmp/dark2_xhtml.html', dark2, show=show,
- markup='xhtml', quiet=quiet, header='', footer='',
- linenumbers=1, form='external')
- path2file(fi, '/tmp/idle.html', idle, show=show, quiet=quiet)
- path2file(fi, '/tmp/idle_css.html', idle, show=show,
- markup='css', quiet=quiet)
- path2file(fi, '/tmp/viewcvs.html', viewcvs, show=show,
- quiet=quiet, linenumbers=1)
- path2file(fi, '/tmp/viewcvs_css.html', viewcvs, show=show,
- markup='css', linenumbers=1, quiet=quiet)
- path2file(fi, '/tmp/pythonwin.html', pythonwin, show=show,
- quiet=quiet)
- path2file(fi, '/tmp/pythonwin_css.html', pythonwin, show=show,
- markup='css', quiet=quiet)
- teststr=r'''"""This is a test of decorators and other things"""
-# This should be line 421...
-@whatever(arg,arg2)
-@A @B(arghh) @C
-def LlamaSaysNi(arg='Ni!',arg2="RALPH"):
- """This docstring is deeply disturbed by all the llama references"""
- print('%s The Wonder Llama says %s'% (arg2,arg))
-# So I was like duh!, and he was like ya know?!,
-# and so we were both like huh...wtf!? RTFM!! LOL!!;)
-@staticmethod## Double comments are KewL.
-def LlamasRLumpy():
- """This docstring is too sexy to be here.
- """
- u"""
-=============================
-A Møøse once bit my sister...
-=============================
- """
- ## Relax, this won't hurt a bit, just a simple, painless procedure,
- ## hold still while I get the anesthetizing hammer.
- m = {'three':'1','won':'2','too':'3'}
- o = r'fishy\fishy\fishy/fish\oh/where/is\my/little\..'
- python = uR"""
- No realli! She was Karving her initials øn the møøse with the sharpened end
- of an interspace tøøthbrush given her by Svenge - her brother-in-law -an Oslo
- dentist and star of many Norwegian møvies: "The Høt Hands of an Oslo
- Dentist", "Fillings of Passion", "The Huge Mølars of Horst Nordfink"..."""
- RU"""142 MEXICAN WHOOPING LLAMAS"""#<-Can you fit 142 llamas in a red box?
- n = u' HERMSGERVØRDENBRØTBØRDA ' + """ YUTTE """
- t = """SAMALLNIATNUOMNAIRODAUCE"""+"DENIARTYLLAICEPS04"
- ## We apologise for the fault in the
- ## comments. Those responsible have been
- ## sacked.
- y = '14 NORTH CHILEAN GUANACOS \
-(CLOSELY RELATED TO THE LLAMA)'
- rules = [0,1,2,3,4,5]
- print y'''
- htmlPath = os.path.abspath('/tmp/strtest_lines.html')
- str2file(teststr, htmlPath, colors=dark, markup='xhtml',
- linenumbers=420, show=show)
- _printinfo(" wrote %s" % htmlPath, quiet)
- htmlPath = os.path.abspath('/tmp/strtest_nolines.html')
- str2file(teststr, htmlPath, colors=dark, markup='xhtml',
- show=show)
- _printinfo(" wrote %s" % htmlPath, quiet)
- else:
- Usage()
- return
-
-# emacs wants this: '
-
-####################################################### User funtctions
-
-def str2stdout(sourcestring, colors=None, title='', markup='html',
- header=None, footer=None,
- linenumbers=0, form=None):
- """Converts a code(string) to colorized HTML. Writes to stdout.
-
- form='code',or'snip' (for "<pre>yourcode</pre>" only)
- colors=null,mono,lite,dark,dark2,idle,or pythonwin
- """
- Parser(sourcestring, colors=colors, title=title, markup=markup,
- header=header, footer=footer,
- linenumbers=linenumbers).format(form)
-
-def path2stdout(sourcepath, title='', colors=None, markup='html',
- header=None, footer=None,
- linenumbers=0, form=None):
- """Converts code(file) to colorized HTML. Writes to stdout.
-
- form='code',or'snip' (for "<pre>yourcode</pre>" only)
- colors=null,mono,lite,dark,dark2,idle,or pythonwin
- """
- sourcestring = open(sourcepath).read()
- Parser(sourcestring, colors=colors, title=sourcepath,
- markup=markup, header=header, footer=footer,
- linenumbers=linenumbers).format(form)
-
-def str2html(sourcestring, colors=None, title='',
- markup='html', header=None, footer=None,
- linenumbers=0, form=None):
- """Converts a code(string) to colorized HTML. Returns an HTML string.
-
- form='code',or'snip' (for "<pre>yourcode</pre>" only)
- colors=null,mono,lite,dark,dark2,idle,or pythonwin
- """
- stringIO = StringIO.StringIO()
- Parser(sourcestring, colors=colors, title=title, out=stringIO,
- markup=markup, header=header, footer=footer,
- linenumbers=linenumbers).format(form)
- stringIO.seek(0)
- return stringIO.read()
-
-def str2css(sourcestring, colors=None, title='',
- markup='css', header=None, footer=None,
- linenumbers=0, form=None):
- """Converts a code string to colorized CSS/HTML. Returns CSS/HTML string
-
- If form != None then this will return (stylesheet_str, code_str)
- colors=null,mono,lite,dark,dark2,idle,or pythonwin
- """
- if markup.lower() not in ['css' ,'xhtml']:
- markup = 'css'
- stringIO = StringIO.StringIO()
- parse = Parser(sourcestring, colors=colors, title=title,
- out=stringIO, markup=markup,
- header=header, footer=footer,
- linenumbers=linenumbers)
- parse.format(form)
- stringIO.seek(0)
- if form != None:
- return parse._sendCSSStyle(external=1), stringIO.read()
- else:
- return None, stringIO.read()
-
-def str2markup(sourcestring, colors=None, title = '',
- markup='xhtml', header=None, footer=None,
- linenumbers=0, form=None):
- """ Convert code strings into ([stylesheet or None], colorized string) """
- if markup.lower() == 'html':
- return None, str2html(sourcestring, colors=colors, title=title,
- header=header, footer=footer, markup=markup,
- linenumbers=linenumbers, form=form)
- else:
- return str2css(sourcestring, colors=colors, title=title,
- header=header, footer=footer, markup=markup,
- linenumbers=linenumbers, form=form)
-
-def str2file(sourcestring, outfile, colors=None, title='',
- markup='html', header=None, footer=None,
- linenumbers=0, show=0, dosheet=1, form=None):
- """Converts a code string to a file.
-
- makes no attempt at correcting bad pathnames
- """
- css , html = str2markup(sourcestring, colors=colors, title='',
- markup=markup, header=header, footer=footer,
- linenumbers=linenumbers, form=form)
- # write html
- f = open(outfile,'wt')
- f.writelines(html)
- f.close()
- #write css
- if css != None and dosheet:
- dir = os.path.dirname(outfile)
- outcss = os.path.join(dir,'pystyle.css')
- f = open(outcss,'wt')
- f.writelines(css)
- f.close()
- if show:
- showpage(outfile)
-
-def path2html(sourcepath, colors=None, markup='html',
- header=None, footer=None,
- linenumbers=0, form=None):
- """Converts code(file) to colorized HTML. Returns an HTML string.
-
- form='code',or'snip' (for "<pre>yourcode</pre>" only)
- colors=null,mono,lite,dark,dark2,idle,or pythonwin
- """
- stringIO = StringIO.StringIO()
- sourcestring = open(sourcepath).read()
- Parser(sourcestring, colors, title=sourcepath, out=stringIO,
- markup=markup, header=header, footer=footer,
- linenumbers=linenumbers).format(form)
- stringIO.seek(0)
- return stringIO.read()
-
-def convert(source, outdir=None, colors=None,
- show=0, markup='html', quiet=0,
- header=None, footer=None, linenumbers=0, form=None):
- """Takes a file or dir as input and places the html in the outdir.
-
- If outdir is none it defaults to the input dir
- """
- count=0
- # If it is a filename then path2file
- if not os.path.isdir(source):
- if os.path.isfile(source):
- count+=1
- path2file(source, outdir, colors, show, markup,
- quiet, form, header, footer, linenumbers, count)
- else:
- raise PathError('File does not exist!')
- # If we pass in a dir we need to walkdir for files.
- # Then we need to colorize them with path2file
- else:
- fileList = walkdir(source)
- if fileList != None:
- # make sure outdir is a dir
- if outdir != None:
- if os.path.splitext(outdir)[1] != '':
- outdir = os.path.split(outdir)[0]
- for item in fileList:
- count+=1
- path2file(item, outdir, colors, show, markup,
- quiet, form, header, footer, linenumbers, count)
- _printinfo('Completed colorizing %s files.'%str(count), quiet)
- else:
- _printinfo("No files to convert in dir.", quiet)
-
-def path2file(sourcePath, out=None, colors=None, show=0,
- markup='html', quiet=0, form=None,
- header=None, footer=None, linenumbers=0, count=1):
- """ Converts python source to html file"""
- # If no outdir is given we use the sourcePath
- if out == None:#this is a guess
- htmlPath = sourcePath + '.html'
- else:
- # If we do give an out_dir, and it does
- # not exist , it will be created.
- if os.path.splitext(out)[1] == '':
- if not os.path.isdir(out):
- os.makedirs(out)
- sourceName = os.path.basename(sourcePath)
- htmlPath = os.path.join(out,sourceName)+'.html'
- # If we do give an out_name, and its dir does
- # not exist , it will be created.
- else:
- outdir = os.path.split(out)[0]
- if not os.path.isdir(outdir):
- os.makedirs(outdir)
- htmlPath = out
- htmlPath = os.path.abspath(htmlPath)
- # Open the text and do the parsing.
- source = open(sourcePath).read()
- parse = Parser(source, colors, sourcePath, open(htmlPath, 'wt'),
- markup, header, footer, linenumbers)
- parse.format(form)
- _printinfo(" wrote %s" % htmlPath, quiet)
- # html markup will ignore the external flag, but
- # we need to stop the blank file from being written.
- if form == 'external' and count == 1 and markup != 'html':
- cssSheet = parse._sendCSSStyle(external=1)
- cssPath = os.path.join(os.path.dirname(htmlPath),'pystyle.css')
- css = open(cssPath, 'wt')
- css.write(cssSheet)
- css.close()
- _printinfo(" wrote %s" % cssPath, quiet)
- if show:
- # load HTML page into the default web browser.
- showpage(htmlPath)
- return htmlPath
-
-def tagreplace(sourcestr, colors=lite, markup='xhtml',
- linenumbers=0, dosheet=1, tagstart='<PY>'.lower(),
- tagend='</PY>'.lower(), stylesheet='pystyle.css'):
- """This is a helper function for pageconvert. Returns css, page.
- """
- if markup.lower() != 'html':
- link = '<link rel="stylesheet" href="%s" type="text/css"/></head>'
- css = link%stylesheet
- if sourcestr.find(css) == -1:
- sourcestr = sourcestr.replace('</head>', css, 1)
- starttags = sourcestr.count(tagstart)
- endtags = sourcestr.count(tagend)
- if starttags:
- if starttags == endtags:
- for _ in range(starttags):
- datastart = sourcestr.find(tagstart)
- dataend = sourcestr.find(tagend)
- data = sourcestr[datastart+len(tagstart):dataend]
- data = unescape(data)
- css , data = str2markup(data, colors=colors,
- linenumbers=linenumbers, markup=markup, form='embed')
- start = sourcestr[:datastart]
- end = sourcestr[dataend+len(tagend):]
- sourcestr = ''.join([start,data,end])
- else:
- raise InputError('Tag mismatch!\nCheck %s,%s tags'%tagstart,tagend)
- if not dosheet:
- css = None
- return css, sourcestr
-
-def pageconvert(path, out=None, colors=lite, markup='xhtml', linenumbers=0,
- dosheet=1, tagstart='<PY>'.lower(), tagend='</PY>'.lower(),
- stylesheet='pystyle', show=1, returnstr=0):
- """This function can colorize Python source
-
- that is written in a webpage enclosed in tags.
- """
- if out == None:
- out = os.path.dirname(path)
- infile = open(path, 'r').read()
- css,page = tagreplace(sourcestr=infile,colors=colors,
- markup=markup, linenumbers=linenumbers, dosheet=dosheet,
- tagstart=tagstart, tagend=tagend, stylesheet=stylesheet)
- if not returnstr:
- newpath = os.path.abspath(os.path.join(
- out,'tmp', os.path.basename(path)))
- if not os.path.exists(newpath):
- try:
- os.makedirs(os.path.dirname(newpath))
- except:
- pass#traceback.print_exc()
- #Usage()
- y = open(newpath, 'w')
- y.write(page)
- y.close()
- if css:
- csspath = os.path.abspath(os.path.join(
- out,'tmp','%s.css'%stylesheet))
- x = open(csspath,'w')
- x.write(css)
- x.close()
- if show:
- try:
- os.startfile(newpath)
- except:
- traceback.print_exc()
- return newpath
- else:
- return css, page
-
-##################################################################### helpers
-
-def walkdir(dir):
- """Return a list of .py and .pyw files from a given directory.
-
- This function can be written as a generator Python 2.3, or a genexp
- in Python 2.4. But 2.2 and 2.1 would be left out....
- """
- # Get a list of files that match *.py*
- GLOB_PATTERN = os.path.join(dir, "*.[p][y]*")
- pathlist = glob.glob(GLOB_PATTERN)
- # Now filter out all but py and pyw
- filterlist = [x for x in pathlist
- if x.endswith('.py')
- or x.endswith('.pyw')]
- if filterlist != []:
- # if we have a list send it
- return filterlist
- else:
- return None
-
-def showpage(path):
- """Helper function to open webpages"""
- try:
- import webbrowser
- webbrowser.open_new(os.path.abspath(path))
- except:
- traceback.print_exc()
-
-def _printinfo(message, quiet):
- """Helper to print messages"""
- if not quiet:
- print(message)
-
-def escape(text):
- """escape text for html. similar to cgi.escape"""
- text = text.replace("&", "&amp;")
- text = text.replace("<", "&lt;")
- text = text.replace(">", "&gt;")
- return text
-
-def unescape(text):
- """unsecape escaped text"""
- text = text.replace("&quot;", '"')
- text = text.replace("&gt;", ">")
- text = text.replace("&lt;", "<")
- text = text.replace("&amp;", "&")
- return text
-
-########################################################### Custom Exceptions
-
-class PySourceColorError(Exception):
- # Base for custom errors
- def __init__(self, msg=''):
- self._msg = msg
- Exception.__init__(self, msg)
- def __repr__(self):
- return self._msg
- __str__ = __repr__
-
-class PathError(PySourceColorError):
- def __init__(self, msg):
- PySourceColorError.__init__(self,
- 'Path error! : %s'% msg)
-
-class InputError(PySourceColorError):
- def __init__(self, msg):
- PySourceColorError.__init__(self,
- 'Input error! : %s'% msg)
-
-########################################################## Python code parser
-
-class Parser(object):
-
- """MoinMoin python parser heavily chopped :)"""
-
- def __init__(self, raw, colors=None, title='', out=sys.stdout,
- markup='html', header=None, footer=None, linenumbers=0):
- """Store the source text & set some flags"""
- if colors == None:
- colors = defaultColors
- self.raw = raw.expandtabs().rstrip()
- self.title = os.path.basename(title)
- self.out = out
- self.line = ''
- self.lasttext = ''
- self.argFlag = 0
- self.classFlag = 0
- self.defFlag = 0
- self.decoratorFlag = 0
- self.external = 0
- self.markup = markup.upper()
- self.colors = colors
- self.header = header
- self.footer = footer
- self.doArgs = 1 # overrides the new tokens
- self.doNames = 1 # overrides the new tokens
- self.doMathOps = 1 # overrides the new tokens
- self.doBrackets = 1 # overrides the new tokens
- self.doURL = 1 # override url conversion
- self.LINENUMHOLDER = "___line___".upper()
- self.LINESTART = "___start___".upper()
- self.skip = 0
- # add space left side of code for padding.Override in color dict.
- self.extraspace = self.colors.get(EXTRASPACE, '')
- # Linenumbers less then zero also have numberlinks
- self.dolinenums = self.linenum = abs(linenumbers)
- if linenumbers < 0:
- self.numberlinks = 1
- else:
- self.numberlinks = 0
-
- def format(self, form=None):
- """Parse and send the colorized source"""
- if form in ('snip','code'):
- self.addEnds = 0
- elif form == 'embed':
- self.addEnds = 0
- self.external = 1
- else:
- if form == 'external':
- self.external = 1
- self.addEnds = 1
-
- # Store line offsets in self.lines
- self.lines = [0, 0]
- pos = 0
-
- # Add linenumbers
- if self.dolinenums:
- start=self.LINENUMHOLDER+' '+self.extraspace
- else:
- start=''+self.extraspace
- newlines = []
- lines = self.raw.splitlines(0)
- for l in lines:
- # span and div escape for customizing and embedding raw text
- if (l.startswith('#$#')
- or l.startswith('#%#')
- or l.startswith('#@#')):
- newlines.append(l)
- else:
- # kludge for line spans in css,xhtml
- if self.markup in ['XHTML','CSS']:
- newlines.append(self.LINESTART+' '+start+l)
- else:
- newlines.append(start+l)
- self.raw = "\n".join(newlines)+'\n'# plus an extra newline at the end
-
- # Gather lines
- while 1:
- pos = self.raw.find('\n', pos) + 1
- if not pos: break
- self.lines.append(pos)
- self.lines.append(len(self.raw))
-
- # Wrap text in a filelike object
- self.pos = 0
- text = StringIO.StringIO(self.raw)
-
- # Markup start
- if self.addEnds:
- self._doPageStart()
- else:
- self._doSnippetStart()
-
- ## Tokenize calls the __call__
- ## function for each token till done.
- # Parse the source and write out the results.
- try:
- tokenize.tokenize(text.readline, self)
- except tokenize.TokenError as ex:
- msg = ex[0]
- line = ex[1][0]
- self.out.write("<h3>ERROR: %s</h3>%s\n"%
- (msg, self.raw[self.lines[line]:]))
- #traceback.print_exc()
-
- # Markup end
- if self.addEnds:
- self._doPageEnd()
- else:
- self._doSnippetEnd()
-
- def __call__(self, toktype, toktext, srow_col, erow_col, line):
- """Token handler. Order is important do not rearrange."""
- self.line = line
- srow, scol = srow_col
- erow, ecol = erow_col
- # Calculate new positions
- oldpos = self.pos
- newpos = self.lines[srow] + scol
- self.pos = newpos + len(toktext)
- # Handle newlines
- if toktype in (token.NEWLINE, tokenize.NL):
- self.decoratorFlag = self.argFlag = 0
- # kludge for line spans in css,xhtml
- if self.markup in ['XHTML','CSS']:
- self.out.write('</span>')
- self.out.write('\n')
- return
-
- # Send the original whitespace, and tokenize backslashes if present.
- # Tokenizer.py just sends continued line backslashes with whitespace.
- # This is a hack to tokenize continued line slashes as operators.
- # Should continued line backslashes be treated as operators
- # or some other token?
-
- if newpos > oldpos:
- if self.raw[oldpos:newpos].isspace():
- # consume a single space after linestarts and linenumbers
- # had to have them so tokenizer could seperate them.
- # multiline strings are handled by do_Text functions
- if self.lasttext != self.LINESTART \
- and self.lasttext != self.LINENUMHOLDER:
- self.out.write(self.raw[oldpos:newpos])
- else:
- self.out.write(self.raw[oldpos+1:newpos])
- else:
- slash = self.raw[oldpos:newpos].find('\\')+oldpos
- self.out.write(self.raw[oldpos:slash])
- getattr(self, '_send%sText'%(self.markup))(OPERATOR, '\\')
- self.linenum+=1
- # kludge for line spans in css,xhtml
- if self.markup in ['XHTML','CSS']:
- self.out.write('</span>')
- self.out.write(self.raw[slash+1:newpos])
-
- # Skip indenting tokens
- if toktype in (token.INDENT, token.DEDENT):
- self.pos = newpos
- return
-
- # Look for operators
- if token.LPAR <= toktype and toktype <= token.OP:
- # Trap decorators py2.4 >
- if toktext == '@':
- toktype = DECORATOR
- # Set a flag if this was the decorator start so
- # the decorator name and arguments can be identified
- self.decoratorFlag = self.argFlag = 1
- else:
- if self.doArgs:
- # Find the start for arguments
- if toktext == '(' and self.argFlag:
- self.argFlag = 2
- # Find the end for arguments
- elif toktext == ':':
- self.argFlag = 0
- ## Seperate the diffrent operator types
- # Brackets
- if self.doBrackets and toktext in ['[',']','(',')','{','}']:
- toktype = BRACKETS
- # Math operators
- elif self.doMathOps and toktext in ['*=','**=','-=','+=','|=',
- '%=','>>=','<<=','=','^=',
- '/=', '+','-','**','*','/','%']:
- toktype = MATH_OPERATOR
- # Operator
- else:
- toktype = OPERATOR
- # example how flags should work.
- # def fun(arg=argvalue,arg2=argvalue2):
- # 0 1 2 A 1 N 2 A 1 N 0
- if toktext == "=" and self.argFlag == 2:
- self.argFlag = 1
- elif toktext == "," and self.argFlag == 1:
- self.argFlag = 2
- # Look for keywords
- elif toktype == NAME and keyword.iskeyword(toktext):
- toktype = KEYWORD
- # Set a flag if this was the class / def start so
- # the class / def name and arguments can be identified
- if toktext in ['class', 'def']:
- if toktext =='class' and \
- not line[:line.find('class')].endswith('.'):
- self.classFlag = self.argFlag = 1
- elif toktext == 'def' and \
- not line[:line.find('def')].endswith('.'):
- self.defFlag = self.argFlag = 1
- else:
- # must have used a keyword as a name i.e. self.class
- toktype = ERRORTOKEN
-
- # Look for class, def, decorator name
- elif (self.classFlag or self.defFlag or self.decoratorFlag) \
- and self.doNames:
- if self.classFlag:
- self.classFlag = 0
- toktype = CLASS_NAME
- elif self.defFlag:
- self.defFlag = 0
- toktype = DEF_NAME
- elif self.decoratorFlag:
- self.decoratorFlag = 0
- toktype = DECORATOR_NAME
-
- # Look for strings
- # Order of evaluation is important do not change.
- elif toktype == token.STRING:
- text = toktext.lower()
- # TRIPLE DOUBLE QUOTE's
- if (text[:3] == '"""'):
- toktype = TRIPLEDOUBLEQUOTE
- elif (text[:4] == 'r"""'):
- toktype = TRIPLEDOUBLEQUOTE_R
- elif (text[:4] == 'u"""' or
- text[:5] == 'ur"""'):
- toktype = TRIPLEDOUBLEQUOTE_U
- # DOUBLE QUOTE's
- elif (text[:1] == '"'):
- toktype = DOUBLEQUOTE
- elif (text[:2] == 'r"'):
- toktype = DOUBLEQUOTE_R
- elif (text[:2] == 'u"' or
- text[:3] == 'ur"'):
- toktype = DOUBLEQUOTE_U
- # TRIPLE SINGLE QUOTE's
- elif (text[:3] == "'''"):
- toktype = TRIPLESINGLEQUOTE
- elif (text[:4] == "r'''"):
- toktype = TRIPLESINGLEQUOTE_R
- elif (text[:4] == "u'''" or
- text[:5] == "ur'''"):
- toktype = TRIPLESINGLEQUOTE_U
- # SINGLE QUOTE's
- elif (text[:1] == "'"):
- toktype = SINGLEQUOTE
- elif (text[:2] == "r'"):
- toktype = SINGLEQUOTE_R
- elif (text[:2] == "u'" or
- text[:3] == "ur'"):
- toktype = SINGLEQUOTE_U
-
- # test for invalid string declaration
- if self.lasttext.lower() == 'ru':
- toktype = ERRORTOKEN
-
- # Look for comments
- elif toktype == COMMENT:
- if toktext[:2] == "##":
- toktype = DOUBLECOMMENT
- elif toktext[:3] == '#$#':
- toktype = TEXT
- self.textFlag = 'SPAN'
- toktext = toktext[3:]
- elif toktext[:3] == '#%#':
- toktype = TEXT
- self.textFlag = 'DIV'
- toktext = toktext[3:]
- elif toktext[:3] == '#@#':
- toktype = TEXT
- self.textFlag = 'RAW'
- toktext = toktext[3:]
- if self.doURL:
- # this is a 'fake helper function'
- # url(URI,Alias_name) or url(URI)
- url_pos = toktext.find('url(')
- if url_pos != -1:
- before = toktext[:url_pos]
- url = toktext[url_pos+4:]
- splitpoint = url.find(',')
- endpoint = url.find(')')
- after = url[endpoint+1:]
- url = url[:endpoint]
- if splitpoint != -1:
- urlparts = url.split(',',1)
- toktext = '%s<a href="%s">%s</a>%s'%(
- before,urlparts[0],urlparts[1].lstrip(),after)
- else:
- toktext = '%s<a href="%s">%s</a>%s'%(before,url,url,after)
-
- # Seperate errors from decorators
- elif toktype == ERRORTOKEN:
- # Bug fix for < py2.4
- # space between decorators
- if self.argFlag and toktext.isspace():
- #toktype = NAME
- self.out.write(toktext)
- return
- # Bug fix for py2.2 linenumbers with decorators
- elif toktext.isspace():
- # What if we have a decorator after a >>> or ...
- #p = line.find('@')
- #if p >= 0 and not line[:p].isspace():
- #self.out.write(toktext)
- #return
- if self.skip:
- self.skip=0
- return
- else:
- self.out.write(toktext)
- return
- # trap decorators < py2.4
- elif toktext == '@':
- toktype = DECORATOR
- # Set a flag if this was the decorator start so
- # the decorator name and arguments can be identified
- self.decoratorFlag = self.argFlag = 1
-
- # Seperate args from names
- elif (self.argFlag == 2 and
- toktype == NAME and
- toktext != 'None' and
- self.doArgs):
- toktype = ARGS
-
- # Look for line numbers
- # The conversion code for them is in the send_text functions.
- if toktext in [self.LINENUMHOLDER,self.LINESTART]:
- toktype = LINENUMBER
- # if we don't have linenumbers set flag
- # to skip the trailing space from linestart
- if toktext == self.LINESTART and not self.dolinenums \
- or toktext == self.LINENUMHOLDER:
- self.skip=1
-
-
- # Skip blank token that made it thru
- ## bugfix for the last empty tag.
- if toktext == '':
- return
-
- # Last token text history
- self.lasttext = toktext
-
- # escape all but the urls in the comments
- if toktype in (DOUBLECOMMENT, COMMENT):
- if toktext.find('<a href=') == -1:
- toktext = escape(toktext)
- else:
- pass
- elif toktype == TEXT:
- pass
- else:
- toktext = escape(toktext)
-
- # Send text for any markup
- getattr(self, '_send%sText'%(self.markup))(toktype, toktext)
- return
-
- ################################################################# Helpers
-
- def _doSnippetStart(self):
- if self.markup == 'HTML':
- # Start of html snippet
- self.out.write('<pre>\n')
- else:
- # Start of css/xhtml snippet
- self.out.write(self.colors.get(CODESTART,'<pre class="py">\n'))
-
- def _doSnippetEnd(self):
- # End of html snippet
- self.out.write(self.colors.get(CODEEND,'</pre>\n'))
-
- ######################################################## markup selectors
-
- def _getFile(self, filepath):
- try:
- _file = open(filepath,'r')
- content = _file.read()
- _file.close()
- except:
- traceback.print_exc()
- content = ''
- return content
-
- def _doPageStart(self):
- getattr(self, '_do%sStart'%(self.markup))()
-
- def _doPageHeader(self):
- if self.header != None:
- if self.header.find('#$#') != -1 or \
- self.header.find('#$#') != -1 or \
- self.header.find('#%#') != -1:
- self.out.write(self.header[3:])
- else:
- if self.header != '':
- self.header = self._getFile(self.header)
- getattr(self, '_do%sHeader'%(self.markup))()
-
- def _doPageFooter(self):
- if self.footer != None:
- if self.footer.find('#$#') != -1 or \
- self.footer.find('#@#') != -1 or \
- self.footer.find('#%#') != -1:
- self.out.write(self.footer[3:])
- else:
- if self.footer != '':
- self.footer = self._getFile(self.footer)
- getattr(self, '_do%sFooter'%(self.markup))()
-
- def _doPageEnd(self):
- getattr(self, '_do%sEnd'%(self.markup))()
-
- ################################################### color/style retrieval
- ## Some of these are not used anymore but are kept for documentation
-
- def _getLineNumber(self):
- num = self.linenum
- self.linenum+=1
- return str(num).rjust(5)+" "
-
- def _getTags(self, key):
- # style tags
- return self.colors.get(key, self.colors[NAME])[0]
-
- def _getForeColor(self, key):
- # get text foreground color, if not set to black
- color = self.colors.get(key, self.colors[NAME])[1]
- if color[:1] != '#':
- color = '#000000'
- return color
-
- def _getBackColor(self, key):
- # get text background color
- return self.colors.get(key, self.colors[NAME])[2]
-
- def _getPageColor(self):
- # get page background color
- return self.colors.get(PAGEBACKGROUND, '#FFFFFF')
-
- def _getStyle(self, key):
- # get the token style from the color dictionary
- return self.colors.get(key, self.colors[NAME])
-
- def _getMarkupClass(self, key):
- # get the markup class name from the markup dictionary
- return MARKUPDICT.get(key, MARKUPDICT[NAME])
-
- def _getDocumentCreatedBy(self):
- return '<!--This document created by %s ver.%s on: %s-->\n'%(
- __title__,__version__,time.ctime())
-
- ################################################### HTML markup functions
-
- def _doHTMLStart(self):
- # Start of html page
- self.out.write('<!DOCTYPE html PUBLIC \
-"-//W3C//DTD HTML 4.01//EN">\n')
- self.out.write('<html><head><title>%s</title>\n'%(self.title))
- self.out.write(self._getDocumentCreatedBy())
- self.out.write('<meta http-equiv="Content-Type" \
-content="text/html;charset=iso-8859-1">\n')
- # Get background
- self.out.write('</head><body bgcolor="%s">\n'%self._getPageColor())
- self._doPageHeader()
- self.out.write('<pre>')
-
- def _getHTMLStyles(self, toktype, toktext):
- # Get styles
- tags, color = self.colors.get(toktype, self.colors[NAME])[:2]#
- tagstart=[]
- tagend=[]
- # check for styles and set them if needed.
- if 'b' in tags:#Bold
- tagstart.append('<b>')
- tagend.append('</b>')
- if 'i' in tags:#Italics
- tagstart.append('<i>')
- tagend.append('</i>')
- if 'u' in tags:#Underline
- tagstart.append('<u>')
- tagend.append('</u>')
- # HTML tags should be paired like so : <b><i><u>Doh!</u></i></b>
- tagend.reverse()
- starttags="".join(tagstart)
- endtags="".join(tagend)
- return starttags,endtags,color
-
- def _sendHTMLText(self, toktype, toktext):
- numberlinks = self.numberlinks
-
- # If it is an error, set a red box around the bad tokens
- # older browsers should ignore it
- if toktype == ERRORTOKEN:
- style = ' style="border: solid 1.5pt #FF0000;"'
- else:
- style = ''
- # Get styles
- starttag, endtag, color = self._getHTMLStyles(toktype, toktext)
- # This is a hack to 'fix' multi-line strings.
- # Multi-line strings are treated as only one token
- # even though they can be several physical lines.
- # That makes it hard to spot the start of a line,
- # because at this level all we know about are tokens.
-
- if toktext.count(self.LINENUMHOLDER):
- # rip apart the string and separate it by line.
- # count lines and change all linenum token to line numbers.
- # embedded all the new font tags inside the current one.
- # Do this by ending the tag first then writing our new tags,
- # then starting another font tag exactly like the first one.
- if toktype == LINENUMBER:
- splittext = toktext.split(self.LINENUMHOLDER)
- else:
- splittext = toktext.split(self.LINENUMHOLDER+' ')
- store = []
- store.append(splittext.pop(0))
- lstarttag, lendtag, lcolor = self._getHTMLStyles(LINENUMBER, toktext)
- count = len(splittext)
- for item in splittext:
- num = self._getLineNumber()
- if numberlinks:
- numstrip = num.strip()
- content = '<a name="%s" href="#%s">%s</a>' \
- %(numstrip,numstrip,num)
- else:
- content = num
- if count <= 1:
- endtag,starttag = '',''
- linenumber = ''.join([endtag,'<font color=', lcolor, '>',
- lstarttag, content, lendtag, '</font>' ,starttag])
- store.append(linenumber+item)
- toktext = ''.join(store)
- # send text
- ## Output optimization
- # skip font tag if black text, but styles will still be sent. (b,u,i)
- if color !='#000000':
- startfont = '<font color="%s"%s>'%(color, style)
- endfont = '</font>'
- else:
- startfont, endfont = ('','')
- if toktype != LINENUMBER:
- self.out.write(''.join([startfont,starttag,
- toktext,endtag,endfont]))
- else:
- self.out.write(toktext)
- return
-
- def _doHTMLHeader(self):
- # Optional
- if self.header != '':
- self.out.write('%s\n'%self.header)
- else:
- color = self._getForeColor(NAME)
- self.out.write('<b><font color="%s"># %s \
- <br># %s</font></b><hr>\n'%
- (color, self.title, time.ctime()))
-
- def _doHTMLFooter(self):
- # Optional
- if self.footer != '':
- self.out.write('%s\n'%self.footer)
- else:
- color = self._getForeColor(NAME)
- self.out.write('<b><font color="%s"> \
- <hr># %s<br># %s</font></b>\n'%
- (color, self.title, time.ctime()))
-
- def _doHTMLEnd(self):
- # End of html page
- self.out.write('</pre>\n')
- # Write a little info at the bottom
- self._doPageFooter()
- self.out.write('</body></html>\n')
-
- #################################################### CSS markup functions
-
- def _getCSSStyle(self, key):
- # Get the tags and colors from the dictionary
- tags, forecolor, backcolor = self._getStyle(key)
- style=[]
- border = None
- bordercolor = None
- tags = tags.lower()
- if tags:
- # get the border color if specified
- # the border color will be appended to
- # the list after we define a border
- if '#' in tags:# border color
- start = tags.find('#')
- end = start + 7
- bordercolor = tags[start:end]
- tags.replace(bordercolor,'',1)
- # text styles
- if 'b' in tags:# Bold
- style.append('font-weight:bold;')
- else:
- style.append('font-weight:normal;')
- if 'i' in tags:# Italic
- style.append('font-style:italic;')
- if 'u' in tags:# Underline
- style.append('text-decoration:underline;')
- # border size
- if 'l' in tags:# thick border
- size='thick'
- elif 'm' in tags:# medium border
- size='medium'
- elif 't' in tags:# thin border
- size='thin'
- else:# default
- size='medium'
- # border styles
- if 'n' in tags:# inset border
- border='inset'
- elif 'o' in tags:# outset border
- border='outset'
- elif 'r' in tags:# ridge border
- border='ridge'
- elif 'g' in tags:# groove border
- border='groove'
- elif '=' in tags:# double border
- border='double'
- elif '.' in tags:# dotted border
- border='dotted'
- elif '-' in tags:# dashed border
- border='dashed'
- elif 's' in tags:# solid border
- border='solid'
- # border type check
- seperate_sides=0
- for side in ['<','>','^','v']:
- if side in tags:
- seperate_sides+=1
- # border box or seperate sides
- if seperate_sides==0 and border:
- style.append('border: %s %s;'%(border,size))
- else:
- if border == None:
- border = 'solid'
- if 'v' in tags:# bottom border
- style.append('border-bottom:%s %s;'%(border,size))
- if '<' in tags:# left border
- style.append('border-left:%s %s;'%(border,size))
- if '>' in tags:# right border
- style.append('border-right:%s %s;'%(border,size))
- if '^' in tags:# top border
- style.append('border-top:%s %s;'%(border,size))
- else:
- style.append('font-weight:normal;')# css inherited style fix
- # we have to define our borders before we set colors
- if bordercolor:
- style.append('border-color:%s;'%bordercolor)
- # text forecolor
- style.append('color:%s;'% forecolor)
- # text backcolor
- if backcolor:
- style.append('background-color:%s;'%backcolor)
- return (self._getMarkupClass(key),' '.join(style))
-
- def _sendCSSStyle(self, external=0):
- """ create external and internal style sheets"""
- styles = []
- external += self.external
- if not external:
- styles.append('<style type="text/css">\n<!--\n')
- # Get page background color and write styles ignore any we don't know
- styles.append('body { background:%s; }\n'%self._getPageColor())
- # write out the various css styles
- for key in MARKUPDICT:
- styles.append('.%s { %s }\n'%self._getCSSStyle(key))
- # If you want to style the pre tag you must modify the color dict.
- # Example:
- # lite[PY] = .py {border: solid thin #000000;background:#555555}\n'''
- styles.append(self.colors.get(PY, '.py { }\n'))
- # Extra css can be added here
- # add CSSHOOK to the color dict if you need it.
- # Example:
- #lite[CSSHOOK] = """.mytag { border: solid thin #000000; } \n
- # .myothertag { font-weight:bold; )\n"""
- styles.append(self.colors.get(CSSHOOK,''))
- if not self.external:
- styles.append('--></style>\n')
- return ''.join(styles)
-
- def _doCSSStart(self):
- # Start of css/html 4.01 page
- self.out.write('<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">\n')
- self.out.write('<html><head><title>%s</title>\n'%(self.title))
- self.out.write(self._getDocumentCreatedBy())
- self.out.write('<meta http-equiv="Content-Type" \
-content="text/html;charset=iso-8859-1">\n')
- self._doCSSStyleSheet()
- self.out.write('</head>\n<body>\n')
- # Write a little info at the top.
- self._doPageHeader()
- self.out.write(self.colors.get(CODESTART,'<pre class="py">\n'))
- return
-
- def _doCSSStyleSheet(self):
- if not self.external:
- # write an embedded style sheet
- self.out.write(self._sendCSSStyle())
- else:
- # write a link to an external style sheet
- self.out.write('<link rel="stylesheet" \
-href="pystyle.css" type="text/css">')
- return
-
- def _sendCSSText(self, toktype, toktext):
- # This is a hack to 'fix' multi-line strings.
- # Multi-line strings are treated as only one token
- # even though they can be several physical lines.
- # That makes it hard to spot the start of a line,
- # because at this level all we know about are tokens.
- markupclass = MARKUPDICT.get(toktype, MARKUPDICT[NAME])
- # if it is a LINENUMBER type then we can skip the rest
- if toktext == self.LINESTART and toktype == LINENUMBER:
- self.out.write('<span class="py_line">')
- return
- if toktext.count(self.LINENUMHOLDER):
- # rip apart the string and separate it by line
- # count lines and change all linenum token to line numbers
- # also convert linestart and lineend tokens
- # <linestart> <lnumstart> lnum <lnumend> text <lineend>
- #################################################
- newmarkup = MARKUPDICT.get(LINENUMBER, MARKUPDICT[NAME])
- lstartspan = '<span class="%s">'%(newmarkup)
- if toktype == LINENUMBER:
- splittext = toktext.split(self.LINENUMHOLDER)
- else:
- splittext = toktext.split(self.LINENUMHOLDER+' ')
- store = []
- # we have already seen the first linenumber token
- # so we can skip the first one
- store.append(splittext.pop(0))
- for item in splittext:
- num = self._getLineNumber()
- if self.numberlinks:
- numstrip = num.strip()
- content= '<a name="%s" href="#%s">%s</a>' \
- %(numstrip,numstrip,num)
- else:
- content = num
- linenumber= ''.join([lstartspan,content,'</span>'])
- store.append(linenumber+item)
- toktext = ''.join(store)
- if toktext.count(self.LINESTART):
- # wraps the textline in a line span
- # this adds a lot of kludges, is it really worth it?
- store = []
- parts = toktext.split(self.LINESTART+' ')
- # handle the first part differently
- # the whole token gets wraqpped in a span later on
- first = parts.pop(0)
- # place spans before the newline
- pos = first.rfind('\n')
- if pos != -1:
- first=first[:pos]+'</span></span>'+first[pos:]
- store.append(first)
- #process the rest of the string
- for item in parts:
- #handle line numbers if present
- if self.dolinenums:
- item = item.replace('</span>',
- '</span><span class="%s">'%(markupclass))
- else:
- item = '<span class="%s">%s'%(markupclass,item)
- # add endings for line and string tokens
- pos = item.rfind('\n')
- if pos != -1:
- item=item[:pos]+'</span></span>\n'
- store.append(item)
- # add start tags for lines
- toktext = '<span class="py_line">'.join(store)
- # Send text
- if toktype != LINENUMBER:
- if toktype == TEXT and self.textFlag == 'DIV':
- startspan = '<div class="%s">'%(markupclass)
- endspan = '</div>'
- elif toktype == TEXT and self.textFlag == 'RAW':
- startspan,endspan = ('','')
- else:
- startspan = '<span class="%s">'%(markupclass)
- endspan = '</span>'
- self.out.write(''.join([startspan, toktext, endspan]))
- else:
- self.out.write(toktext)
- return
-
- def _doCSSHeader(self):
- if self.header != '':
- self.out.write('%s\n'%self.header)
- else:
- name = MARKUPDICT.get(NAME)
- self.out.write('<div class="%s"># %s <br> \
-# %s</div><hr>\n'%(name, self.title, time.ctime()))
-
- def _doCSSFooter(self):
- # Optional
- if self.footer != '':
- self.out.write('%s\n'%self.footer)
- else:
- self.out.write('<hr><div class="%s"># %s <br> \
-# %s</div>\n'%(MARKUPDICT.get(NAME),self.title, time.ctime()))
-
- def _doCSSEnd(self):
- # End of css/html page
- self.out.write(self.colors.get(CODEEND,'</pre>\n'))
- # Write a little info at the bottom
- self._doPageFooter()
- self.out.write('</body></html>\n')
- return
-
- ################################################## XHTML markup functions
-
- def _doXHTMLStart(self):
- # XHTML is really just XML + HTML 4.01.
- # We only need to change the page headers,
- # and a few tags to get valid XHTML.
- # Start of xhtml page
- self.out.write('<?xml version="1.0"?>\n \
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"\n \
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n \
-<html xmlns="http://www.w3.org/1999/xhtml">\n')
- self.out.write('<head><title>%s</title>\n'%(self.title))
- self.out.write(self._getDocumentCreatedBy())
- self.out.write('<meta http-equiv="Content-Type" \
-content="text/html;charset=iso-8859-1"/>\n')
- self._doXHTMLStyleSheet()
- self.out.write('</head>\n<body>\n')
- # Write a little info at the top.
- self._doPageHeader()
- self.out.write(self.colors.get(CODESTART,'<pre class="py">\n'))
- return
-
- def _doXHTMLStyleSheet(self):
- if not self.external:
- # write an embedded style sheet
- self.out.write(self._sendCSSStyle())
- else:
- # write a link to an external style sheet
- self.out.write('<link rel="stylesheet" \
-href="pystyle.css" type="text/css"/>\n')
- return
-
- def _sendXHTMLText(self, toktype, toktext):
- self._sendCSSText(toktype, toktext)
-
- def _doXHTMLHeader(self):
- # Optional
- if self.header:
- self.out.write('%s\n'%self.header)
- else:
- name = MARKUPDICT.get(NAME)
- self.out.write('<div class="%s"># %s <br/> \
-# %s</div><hr/>\n '%(
- name, self.title, time.ctime()))
-
- def _doXHTMLFooter(self):
- # Optional
- if self.footer:
- self.out.write('%s\n'%self.footer)
- else:
- self.out.write('<hr/><div class="%s"># %s <br/> \
-# %s</div>\n'%(MARKUPDICT.get(NAME), self.title, time.ctime()))
-
- def _doXHTMLEnd(self):
- self._doCSSEnd()
-
-#############################################################################
-
-if __name__ == '__main__':
- cli()
-
-#############################################################################
-# PySourceColor.py
-# 2004, 2005 M.E.Farmer Jr.
-# Python license
+# -*- coding: Latin-1 -*-
+"""
+PySourceColor: color Python source code
+"""
+
+"""
+ PySourceColor.py
+
+----------------------------------------------------------------------------
+
+ A python source to colorized html/css/xhtml converter.
+ Hacked by M.E.Farmer Jr. 2004, 2005
+ Python license
+
+----------------------------------------------------------------------------
+
+ - HTML markup does not create w3c valid html, but it works on every
+ browser i've tried so far.(I.E.,Mozilla/Firefox,Opera,Konqueror,wxHTML).
+ - CSS markup is w3c validated html 4.01 strict,
+ but will not render correctly on all browsers.
+ - XHTML markup is w3c validated xhtml 1.0 strict,
+ like html 4.01, will not render correctly on all browsers.
+
+----------------------------------------------------------------------------
+
+Features:
+
+ -Three types of markup:
+ html (default)
+ css/html 4.01 strict
+ xhtml 1.0 strict
+
+ -Can tokenize and colorize:
+ 12 types of strings
+ 2 comment types
+ numbers
+ operators
+ brackets
+ math operators
+ class / name
+ def / name
+ decorator / name
+ keywords
+ arguments class/def/decorator
+ linenumbers
+ names
+ text
+
+ -Eight colorschemes built-in:
+ null
+ mono
+ lite (default)
+ dark
+ dark2
+ idle
+ viewcvs
+ pythonwin
+
+ -Header and footer
+ set to '' for builtin header / footer.
+ give path to a file containing the html
+ you want added as header or footer.
+
+ -Arbitrary text and html
+ html markup converts all to raw (TEXT token)
+ #@# for raw -> send raw text.
+ #$# for span -> inline html and text.
+ #%# for div -> block level html and text.
+
+ -Linenumbers
+ Supports all styles. New token is called LINENUMBER.
+ Defaults to NAME if not defined.
+
+ Style options
+
+ -ALL markups support these text styles:
+ b = bold
+ i = italic
+ u = underline
+ -CSS and XHTML has limited support for borders:
+ HTML markup functions will ignore these.
+ Optional: Border color in RGB hex
+ Defaults to the text forecolor.
+ #rrggbb = border color
+ Border size:
+ l = thick
+ m = medium
+ t = thin
+ Border type:
+ - = dashed
+ . = dotted
+ s = solid
+ d = double
+ g = groove
+ r = ridge
+ n = inset
+ o = outset
+ You can specify multiple sides,
+ they will all use the same style.
+ Optional: Default is full border.
+ v = bottom
+ < = left
+ > = right
+ ^ = top
+ NOTE: Specify the styles you want.
+ The markups will ignore unsupported styles
+ Also note not all browsers can show these options
+
+ -All tokens default to NAME if not defined
+ so the only absolutely critical ones to define are:
+ NAME, ERRORTOKEN, PAGEBACKGROUND
+
+----------------------------------------------------------------------------
+
+Example usage::
+
+ # import
+ import PySourceColor as psc
+ psc.convert('c:/Python22/PySourceColor.py', colors=psc.idle, show=1)
+
+ # from module import *
+ from PySourceColor import *
+ convert('c:/Python22/Lib', colors=lite, markup="css",
+ header='#$#<b>This is a simpe heading</b><hr/>')
+
+ # How to use a custom colorscheme, and most of the 'features'
+ from PySourceColor import *
+ new = {
+ ERRORTOKEN: ('bui','#FF8080',''),
+ DECORATOR_NAME: ('s','#AACBBC',''),
+ DECORATOR: ('n','#333333',''),
+ NAME: ('t.<v','#1133AA','#DDFF22'),
+ NUMBER: ('','#236676','#FF5555'),
+ OPERATOR: ('b','#454567','#BBBB11'),
+ MATH_OPERATOR: ('','#935623','#423afb'),
+ BRACKETS: ('b','#ac34bf','#6457a5'),
+ COMMENT: ('t-#0022FF','#545366','#AABBFF'),
+ DOUBLECOMMENT: ('<l#553455','#553455','#FF00FF'),
+ CLASS_NAME: ('m^v-','#000000','#FFFFFF'),
+ DEF_NAME: ('l=<v','#897845','#000022'),
+ KEYWORD: ('.b','#345345','#FFFF22'),
+ SINGLEQUOTE: ('mn','#223344','#AADDCC'),
+ SINGLEQUOTE_R: ('','#344522',''),
+ SINGLEQUOTE_U: ('','#234234',''),
+ DOUBLEQUOTE: ('m#0022FF','#334421',''),
+ DOUBLEQUOTE_R: ('','#345345',''),
+ DOUBLEQUOTE_U: ('','#678673',''),
+ TRIPLESINGLEQUOTE: ('tv','#FFFFFF','#000000'),
+ TRIPLESINGLEQUOTE_R: ('tbu','#443256','#DDFFDA'),
+ TRIPLESINGLEQUOTE_U: ('','#423454','#DDFFDA'),
+ TRIPLEDOUBLEQUOTE: ('li#236fd3b<>','#000000','#FFFFFF'),
+ TRIPLEDOUBLEQUOTE_R: ('tub','#000000','#FFFFFF'),
+ TRIPLEDOUBLEQUOTE_U: ('-', '#CCAABB','#FFFAFF'),
+ LINENUMBER: ('ib-','#ff66aa','#7733FF'),]
+ TEXT: ('','#546634',''),
+ PAGEBACKGROUND: '#FFFAAA',
+ }
+ if __name__ == '__main__':
+ import sys
+ convert(sys.argv[1], './xhtml.html', colors=new, markup='xhtml', show=1,
+ linenumbers=1)
+ convert(sys.argv[1], './html.html', colors=new, markup='html', show=1,
+ linenumbers=1)
+
+"""
+
+__all__ = ['ERRORTOKEN','DECORATOR_NAME', 'DECORATOR', 'ARGS', 'EXTRASPACE',
+ 'NAME', 'NUMBER', 'OPERATOR', 'COMMENT', 'MATH_OPERATOR',
+ 'DOUBLECOMMENT', 'CLASS_NAME', 'DEF_NAME', 'KEYWORD', 'BRACKETS',
+ 'SINGLEQUOTE','SINGLEQUOTE_R','SINGLEQUOTE_U','DOUBLEQUOTE',
+ 'DOUBLEQUOTE_R', 'DOUBLEQUOTE_U', 'TRIPLESINGLEQUOTE', 'TEXT',
+ 'TRIPLESINGLEQUOTE_R', 'TRIPLESINGLEQUOTE_U', 'TRIPLEDOUBLEQUOTE',
+ 'TRIPLEDOUBLEQUOTE_R', 'TRIPLEDOUBLEQUOTE_U', 'PAGEBACKGROUND',
+ 'LINENUMBER', 'CODESTART', 'CODEEND', 'PY', 'TOKEN_NAMES', 'CSSHOOK',
+ 'null', 'mono', 'lite', 'dark','dark2', 'pythonwin','idle',
+ 'viewcvs', 'Usage', 'cli', 'str2stdout', 'path2stdout', 'Parser',
+ 'str2file', 'str2html', 'str2css', 'str2markup', 'path2file',
+ 'path2html', 'convert', 'walkdir', 'defaultColors', 'showpage',
+ 'pageconvert','tagreplace', 'MARKUPDICT']
+__title__ = 'PySourceColor'
+__version__ = "2.1a"
+__date__ = '25 April 2005'
+__author__ = "M.E.Farmer Jr."
+__credits__ = '''This was originally based on a python recipe
+submitted by Jürgen Hermann to ASPN. Now based on the voices in my head.
+M.E.Farmer 2004, 2005
+Python license
+'''
+import os
+import sys
+import time
+import glob
+import getopt
+import keyword
+import token
+import tokenize
+import traceback
+from six.moves import cStringIO as StringIO
+# Do not edit
+NAME = token.NAME
+NUMBER = token.NUMBER
+COMMENT = tokenize.COMMENT
+OPERATOR = token.OP
+ERRORTOKEN = token.ERRORTOKEN
+ARGS = token.NT_OFFSET + 1
+DOUBLECOMMENT = token.NT_OFFSET + 2
+CLASS_NAME = token.NT_OFFSET + 3
+DEF_NAME = token.NT_OFFSET + 4
+KEYWORD = token.NT_OFFSET + 5
+SINGLEQUOTE = token.NT_OFFSET + 6
+SINGLEQUOTE_R = token.NT_OFFSET + 7
+SINGLEQUOTE_U = token.NT_OFFSET + 8
+DOUBLEQUOTE = token.NT_OFFSET + 9
+DOUBLEQUOTE_R = token.NT_OFFSET + 10
+DOUBLEQUOTE_U = token.NT_OFFSET + 11
+TRIPLESINGLEQUOTE = token.NT_OFFSET + 12
+TRIPLESINGLEQUOTE_R = token.NT_OFFSET + 13
+TRIPLESINGLEQUOTE_U = token.NT_OFFSET + 14
+TRIPLEDOUBLEQUOTE = token.NT_OFFSET + 15
+TRIPLEDOUBLEQUOTE_R = token.NT_OFFSET + 16
+TRIPLEDOUBLEQUOTE_U = token.NT_OFFSET + 17
+PAGEBACKGROUND = token.NT_OFFSET + 18
+DECORATOR = token.NT_OFFSET + 19
+DECORATOR_NAME = token.NT_OFFSET + 20
+BRACKETS = token.NT_OFFSET + 21
+MATH_OPERATOR = token.NT_OFFSET + 22
+LINENUMBER = token.NT_OFFSET + 23
+TEXT = token.NT_OFFSET + 24
+PY = token.NT_OFFSET + 25
+CODESTART = token.NT_OFFSET + 26
+CODEEND = token.NT_OFFSET + 27
+CSSHOOK = token.NT_OFFSET + 28
+EXTRASPACE = token.NT_OFFSET + 29
+
+# markup classname lookup
+MARKUPDICT = {
+ ERRORTOKEN: 'py_err',
+ DECORATOR_NAME: 'py_decn',
+ DECORATOR: 'py_dec',
+ ARGS: 'py_args',
+ NAME: 'py_name',
+ NUMBER: 'py_num',
+ OPERATOR: 'py_op',
+ COMMENT: 'py_com',
+ DOUBLECOMMENT: 'py_dcom',
+ CLASS_NAME: 'py_clsn',
+ DEF_NAME: 'py_defn',
+ KEYWORD: 'py_key',
+ SINGLEQUOTE: 'py_sq',
+ SINGLEQUOTE_R: 'py_sqr',
+ SINGLEQUOTE_U: 'py_squ',
+ DOUBLEQUOTE: 'py_dq',
+ DOUBLEQUOTE_R: 'py_dqr',
+ DOUBLEQUOTE_U: 'py_dqu',
+ TRIPLESINGLEQUOTE: 'py_tsq',
+ TRIPLESINGLEQUOTE_R: 'py_tsqr',
+ TRIPLESINGLEQUOTE_U: 'py_tsqu',
+ TRIPLEDOUBLEQUOTE: 'py_tdq',
+ TRIPLEDOUBLEQUOTE_R: 'py_tdqr',
+ TRIPLEDOUBLEQUOTE_U: 'py_tdqu',
+ BRACKETS: 'py_bra',
+ MATH_OPERATOR: 'py_mop',
+ LINENUMBER: 'py_lnum',
+ TEXT: 'py_text',
+ }
+# might help users that want to create custom schemes
+TOKEN_NAMES= {
+ ERRORTOKEN:'ERRORTOKEN',
+ DECORATOR_NAME:'DECORATOR_NAME',
+ DECORATOR:'DECORATOR',
+ ARGS:'ARGS',
+ NAME:'NAME',
+ NUMBER:'NUMBER',
+ OPERATOR:'OPERATOR',
+ COMMENT:'COMMENT',
+ DOUBLECOMMENT:'DOUBLECOMMENT',
+ CLASS_NAME:'CLASS_NAME',
+ DEF_NAME:'DEF_NAME',
+ KEYWORD:'KEYWORD',
+ SINGLEQUOTE:'SINGLEQUOTE',
+ SINGLEQUOTE_R:'SINGLEQUOTE_R',
+ SINGLEQUOTE_U:'SINGLEQUOTE_U',
+ DOUBLEQUOTE:'DOUBLEQUOTE',
+ DOUBLEQUOTE_R:'DOUBLEQUOTE_R',
+ DOUBLEQUOTE_U:'DOUBLEQUOTE_U',
+ TRIPLESINGLEQUOTE:'TRIPLESINGLEQUOTE',
+ TRIPLESINGLEQUOTE_R:'TRIPLESINGLEQUOTE_R',
+ TRIPLESINGLEQUOTE_U:'TRIPLESINGLEQUOTE_U',
+ TRIPLEDOUBLEQUOTE:'TRIPLEDOUBLEQUOTE',
+ TRIPLEDOUBLEQUOTE_R:'TRIPLEDOUBLEQUOTE_R',
+ TRIPLEDOUBLEQUOTE_U:'TRIPLEDOUBLEQUOTE_U',
+ BRACKETS:'BRACKETS',
+ MATH_OPERATOR:'MATH_OPERATOR',
+ LINENUMBER:'LINENUMBER',
+ TEXT:'TEXT',
+ PAGEBACKGROUND:'PAGEBACKGROUND',
+ }
+
+######################################################################
+# Edit colors and styles to taste
+# Create your own scheme, just copy one below , rename and edit.
+# Custom styles must at least define NAME, ERRORTOKEN, PAGEBACKGROUND,
+# all missing elements will default to NAME.
+# See module docstring for details on style attributes.
+######################################################################
+# Copy null and use it as a starter colorscheme.
+null = {# tokentype: ('tags border_color', 'textforecolor', 'textbackcolor')
+ ERRORTOKEN: ('','#000000',''),# Error token
+ DECORATOR_NAME: ('','#000000',''),# Decorator name
+ DECORATOR: ('','#000000',''),# @ symbol
+ ARGS: ('','#000000',''),# class,def,deco arguments
+ NAME: ('','#000000',''),# All other python text
+ NUMBER: ('','#000000',''),# 0->10
+ OPERATOR: ('','#000000',''),# ':','<=',';',',','.','==', etc
+ MATH_OPERATOR: ('','#000000',''),# '+','-','=','','**',etc
+ BRACKETS: ('','#000000',''),# '[',']','(',')','{','}'
+ COMMENT: ('','#000000',''),# Single comment
+ DOUBLECOMMENT: ('','#000000',''),## Double comment
+ CLASS_NAME: ('','#000000',''),# Class name
+ DEF_NAME: ('','#000000',''),# Def name
+ KEYWORD: ('','#000000',''),# Python keywords
+ SINGLEQUOTE: ('','#000000',''),# 'SINGLEQUOTE'
+ SINGLEQUOTE_R: ('','#000000',''),# r'SINGLEQUOTE'
+ SINGLEQUOTE_U: ('','#000000',''),# u'SINGLEQUOTE'
+ DOUBLEQUOTE: ('','#000000',''),# "DOUBLEQUOTE"
+ DOUBLEQUOTE_R: ('','#000000',''),# r"DOUBLEQUOTE"
+ DOUBLEQUOTE_U: ('','#000000',''),# u"DOUBLEQUOTE"
+ TRIPLESINGLEQUOTE: ('','#000000',''),# '''TRIPLESINGLEQUOTE'''
+ TRIPLESINGLEQUOTE_R: ('','#000000',''),# r'''TRIPLESINGLEQUOTE'''
+ TRIPLESINGLEQUOTE_U: ('','#000000',''),# u'''TRIPLESINGLEQUOTE'''
+ TRIPLEDOUBLEQUOTE: ('','#000000',''),# """TRIPLEDOUBLEQUOTE"""
+ TRIPLEDOUBLEQUOTE_R: ('','#000000',''),# r"""TRIPLEDOUBLEQUOTE"""
+ TRIPLEDOUBLEQUOTE_U: ('','#000000',''),# u"""TRIPLEDOUBLEQUOTE"""
+ TEXT: ('','#000000',''),# non python text
+ LINENUMBER: ('>ti#555555','#000000',''),# Linenumbers
+ PAGEBACKGROUND: '#FFFFFF'# set the page background
+ }
+
+mono = {
+ ERRORTOKEN: ('s#FF0000','#FF8080',''),
+ DECORATOR_NAME: ('bu','#000000',''),
+ DECORATOR: ('b','#000000',''),
+ ARGS: ('b','#555555',''),
+ NAME: ('','#000000',''),
+ NUMBER: ('b','#000000',''),
+ OPERATOR: ('b','#000000',''),
+ MATH_OPERATOR: ('b','#000000',''),
+ BRACKETS: ('b','#000000',''),
+ COMMENT: ('i','#999999',''),
+ DOUBLECOMMENT: ('b','#999999',''),
+ CLASS_NAME: ('bu','#000000',''),
+ DEF_NAME: ('b','#000000',''),
+ KEYWORD: ('b','#000000',''),
+ SINGLEQUOTE: ('','#000000',''),
+ SINGLEQUOTE_R: ('','#000000',''),
+ SINGLEQUOTE_U: ('','#000000',''),
+ DOUBLEQUOTE: ('','#000000',''),
+ DOUBLEQUOTE_R: ('','#000000',''),
+ DOUBLEQUOTE_U: ('','#000000',''),
+ TRIPLESINGLEQUOTE: ('','#000000',''),
+ TRIPLESINGLEQUOTE_R: ('','#000000',''),
+ TRIPLESINGLEQUOTE_U: ('','#000000',''),
+ TRIPLEDOUBLEQUOTE: ('i','#000000',''),
+ TRIPLEDOUBLEQUOTE_R: ('i','#000000',''),
+ TRIPLEDOUBLEQUOTE_U: ('i','#000000',''),
+ TEXT: ('','#000000',''),
+ LINENUMBER: ('>ti#555555','#000000',''),
+ PAGEBACKGROUND: '#FFFFFF'
+ }
+
+dark = {
+ ERRORTOKEN: ('s#FF0000','#FF8080',''),
+ DECORATOR_NAME: ('b','#FFBBAA',''),
+ DECORATOR: ('b','#CC5511',''),
+ ARGS: ('b','#DDDDFF',''),
+ NAME: ('','#DDDDDD',''),
+ NUMBER: ('','#FF0000',''),
+ OPERATOR: ('b','#FAF785',''),
+ MATH_OPERATOR: ('b','#FAF785',''),
+ BRACKETS: ('b','#FAF785',''),
+ COMMENT: ('','#45FCA0',''),
+ DOUBLECOMMENT: ('i','#A7C7A9',''),
+ CLASS_NAME: ('b','#B666FD',''),
+ DEF_NAME: ('b','#EBAE5C',''),
+ KEYWORD: ('b','#8680FF',''),
+ SINGLEQUOTE: ('','#F8BAFE',''),
+ SINGLEQUOTE_R: ('','#F8BAFE',''),
+ SINGLEQUOTE_U: ('','#F8BAFE',''),
+ DOUBLEQUOTE: ('','#FF80C0',''),
+ DOUBLEQUOTE_R: ('','#FF80C0',''),
+ DOUBLEQUOTE_U: ('','#FF80C0',''),
+ TRIPLESINGLEQUOTE: ('','#FF9595',''),
+ TRIPLESINGLEQUOTE_R: ('','#FF9595',''),
+ TRIPLESINGLEQUOTE_U: ('','#FF9595',''),
+ TRIPLEDOUBLEQUOTE: ('','#B3FFFF',''),
+ TRIPLEDOUBLEQUOTE_R: ('','#B3FFFF',''),
+ TRIPLEDOUBLEQUOTE_U: ('','#B3FFFF',''),
+ TEXT: ('','#FFFFFF',''),
+ LINENUMBER: ('>mi#555555','#bbccbb','#333333'),
+ PAGEBACKGROUND: '#000000'
+ }
+
+dark2 = {
+ ERRORTOKEN: ('','#FF0000',''),
+ DECORATOR_NAME: ('b','#FFBBAA',''),
+ DECORATOR: ('b','#CC5511',''),
+ ARGS: ('b','#DDDDDD',''),
+ NAME: ('','#C0C0C0',''),
+ NUMBER: ('b','#00FF00',''),
+ OPERATOR: ('b','#FF090F',''),
+ MATH_OPERATOR: ('b','#EE7020',''),
+ BRACKETS: ('b','#FFB90F',''),
+ COMMENT: ('i','#D0D000','#522000'),#'#88AA88','#11111F'),
+ DOUBLECOMMENT: ('i','#D0D000','#522000'),#'#77BB77','#11111F'),
+ CLASS_NAME: ('b','#DD4080',''),
+ DEF_NAME: ('b','#FF8040',''),
+ KEYWORD: ('b','#4726d1',''),
+ SINGLEQUOTE: ('','#8080C0',''),
+ SINGLEQUOTE_R: ('','#8080C0',''),
+ SINGLEQUOTE_U: ('','#8080C0',''),
+ DOUBLEQUOTE: ('','#ADB9F1',''),
+ DOUBLEQUOTE_R: ('','#ADB9F1',''),
+ DOUBLEQUOTE_U: ('','#ADB9F1',''),
+ TRIPLESINGLEQUOTE: ('','#00C1C1',''),#A050C0
+ TRIPLESINGLEQUOTE_R: ('','#00C1C1',''),#A050C0
+ TRIPLESINGLEQUOTE_U: ('','#00C1C1',''),#A050C0
+ TRIPLEDOUBLEQUOTE: ('','#33E3E3',''),#B090E0
+ TRIPLEDOUBLEQUOTE_R: ('','#33E3E3',''),#B090E0
+ TRIPLEDOUBLEQUOTE_U: ('','#33E3E3',''),#B090E0
+ TEXT: ('','#C0C0C0',''),
+ LINENUMBER: ('>mi#555555','#bbccbb','#333333'),
+ PAGEBACKGROUND: '#000000'
+ }
+
+lite = {
+ ERRORTOKEN: ('s#FF0000','#FF8080',''),
+ DECORATOR_NAME: ('b','#BB4422',''),
+ DECORATOR: ('b','#3333AF',''),
+ ARGS: ('b','#000000',''),
+ NAME: ('','#333333',''),
+ NUMBER: ('b','#DD2200',''),
+ OPERATOR: ('b','#000000',''),
+ MATH_OPERATOR: ('b','#000000',''),
+ BRACKETS: ('b','#000000',''),
+ COMMENT: ('','#007F00',''),
+ DOUBLECOMMENT: ('','#608060',''),
+ CLASS_NAME: ('b','#0000DF',''),
+ DEF_NAME: ('b','#9C7A00',''),#f09030
+ KEYWORD: ('b','#0000AF',''),
+ SINGLEQUOTE: ('','#600080',''),
+ SINGLEQUOTE_R: ('','#600080',''),
+ SINGLEQUOTE_U: ('','#600080',''),
+ DOUBLEQUOTE: ('','#A0008A',''),
+ DOUBLEQUOTE_R: ('','#A0008A',''),
+ DOUBLEQUOTE_U: ('','#A0008A',''),
+ TRIPLESINGLEQUOTE: ('','#337799',''),
+ TRIPLESINGLEQUOTE_R: ('','#337799',''),
+ TRIPLESINGLEQUOTE_U: ('','#337799',''),
+ TRIPLEDOUBLEQUOTE: ('','#1166AA',''),
+ TRIPLEDOUBLEQUOTE_R: ('','#1166AA',''),
+ TRIPLEDOUBLEQUOTE_U: ('','#1166AA',''),
+ TEXT: ('','#000000',''),
+ LINENUMBER: ('>ti#555555','#000000',''),
+ PAGEBACKGROUND: '#FFFFFF'
+ }
+
+idle = {
+ ERRORTOKEN: ('s#FF0000','#FF8080',''),
+ DECORATOR_NAME: ('','#900090',''),
+ DECORATOR: ('','#FF7700',''),
+ NAME: ('','#000000',''),
+ NUMBER: ('','#000000',''),
+ OPERATOR: ('','#000000',''),
+ MATH_OPERATOR: ('','#000000',''),
+ BRACKETS: ('','#000000',''),
+ COMMENT: ('','#DD0000',''),
+ DOUBLECOMMENT: ('','#DD0000',''),
+ CLASS_NAME: ('','#0000FF',''),
+ DEF_NAME: ('','#0000FF',''),
+ KEYWORD: ('','#FF7700',''),
+ SINGLEQUOTE: ('','#00AA00',''),
+ SINGLEQUOTE_R: ('','#00AA00',''),
+ SINGLEQUOTE_U: ('','#00AA00',''),
+ DOUBLEQUOTE: ('','#00AA00',''),
+ DOUBLEQUOTE_R: ('','#00AA00',''),
+ DOUBLEQUOTE_U: ('','#00AA00',''),
+ TRIPLESINGLEQUOTE: ('','#00AA00',''),
+ TRIPLESINGLEQUOTE_R: ('','#00AA00',''),
+ TRIPLESINGLEQUOTE_U: ('','#00AA00',''),
+ TRIPLEDOUBLEQUOTE: ('','#00AA00',''),
+ TRIPLEDOUBLEQUOTE_R: ('','#00AA00',''),
+ TRIPLEDOUBLEQUOTE_U: ('','#00AA00',''),
+ TEXT: ('','#000000',''),
+ LINENUMBER: ('>ti#555555','#000000',''),
+ PAGEBACKGROUND: '#FFFFFF'
+ }
+
+pythonwin = {
+ ERRORTOKEN: ('s#FF0000','#FF8080',''),
+ DECORATOR_NAME: ('b','#DD0080',''),
+ DECORATOR: ('b','#000080',''),
+ ARGS: ('','#000000',''),
+ NAME: ('','#303030',''),
+ NUMBER: ('','#008080',''),
+ OPERATOR: ('','#000000',''),
+ MATH_OPERATOR: ('','#000000',''),
+ BRACKETS: ('','#000000',''),
+ COMMENT: ('','#007F00',''),
+ DOUBLECOMMENT: ('','#7F7F7F',''),
+ CLASS_NAME: ('b','#0000FF',''),
+ DEF_NAME: ('b','#007F7F',''),
+ KEYWORD: ('b','#000080',''),
+ SINGLEQUOTE: ('','#808000',''),
+ SINGLEQUOTE_R: ('','#808000',''),
+ SINGLEQUOTE_U: ('','#808000',''),
+ DOUBLEQUOTE: ('','#808000',''),
+ DOUBLEQUOTE_R: ('','#808000',''),
+ DOUBLEQUOTE_U: ('','#808000',''),
+ TRIPLESINGLEQUOTE: ('','#808000',''),
+ TRIPLESINGLEQUOTE_R: ('','#808000',''),
+ TRIPLESINGLEQUOTE_U: ('','#808000',''),
+ TRIPLEDOUBLEQUOTE: ('','#808000',''),
+ TRIPLEDOUBLEQUOTE_R: ('','#808000',''),
+ TRIPLEDOUBLEQUOTE_U: ('','#808000',''),
+ TEXT: ('','#303030',''),
+ LINENUMBER: ('>ti#555555','#000000',''),
+ PAGEBACKGROUND: '#FFFFFF'
+ }
+
+viewcvs = {
+ ERRORTOKEN: ('s#FF0000','#FF8080',''),
+ DECORATOR_NAME: ('','#000000',''),
+ DECORATOR: ('','#000000',''),
+ ARGS: ('','#000000',''),
+ NAME: ('','#000000',''),
+ NUMBER: ('','#000000',''),
+ OPERATOR: ('','#000000',''),
+ MATH_OPERATOR: ('','#000000',''),
+ BRACKETS: ('','#000000',''),
+ COMMENT: ('i','#b22222',''),
+ DOUBLECOMMENT: ('i','#b22222',''),
+ CLASS_NAME: ('','#000000',''),
+ DEF_NAME: ('b','#0000ff',''),
+ KEYWORD: ('b','#a020f0',''),
+ SINGLEQUOTE: ('b','#bc8f8f',''),
+ SINGLEQUOTE_R: ('b','#bc8f8f',''),
+ SINGLEQUOTE_U: ('b','#bc8f8f',''),
+ DOUBLEQUOTE: ('b','#bc8f8f',''),
+ DOUBLEQUOTE_R: ('b','#bc8f8f',''),
+ DOUBLEQUOTE_U: ('b','#bc8f8f',''),
+ TRIPLESINGLEQUOTE: ('b','#bc8f8f',''),
+ TRIPLESINGLEQUOTE_R: ('b','#bc8f8f',''),
+ TRIPLESINGLEQUOTE_U: ('b','#bc8f8f',''),
+ TRIPLEDOUBLEQUOTE: ('b','#bc8f8f',''),
+ TRIPLEDOUBLEQUOTE_R: ('b','#bc8f8f',''),
+ TRIPLEDOUBLEQUOTE_U: ('b','#bc8f8f',''),
+ TEXT: ('','#000000',''),
+ LINENUMBER: ('>ti#555555','#000000',''),
+ PAGEBACKGROUND: '#FFFFFF'
+ }
+
+defaultColors = lite
+
+def Usage():
+ doc = """
+ -----------------------------------------------------------------------------
+ PySourceColor.py ver: %s
+ -----------------------------------------------------------------------------
+ Module summary:
+ This module is designed to colorize python source code.
+ Input--->python source
+ Output-->colorized (html, html4.01/css, xhtml1.0)
+ Standalone:
+ This module will work from the command line with options.
+ This module will work with redirected stdio.
+ Imported:
+ This module can be imported and used directly in your code.
+ -----------------------------------------------------------------------------
+ Command line options:
+ -h, --help
+ Optional-> Display this help message.
+ -t, --test
+ Optional-> Will ignore all others flags but --profile
+ test all schemes and markup combinations
+ -p, --profile
+ Optional-> Works only with --test or -t
+ runs profile.py and makes the test work in quiet mode.
+ -i, --in, --input
+ Optional-> If you give input on stdin.
+ Use any of these for the current dir (.,cwd)
+ Input can be file or dir.
+ Input from stdin use one of the following (-,stdin)
+ If stdin is used as input stdout is output unless specified.
+ -o, --out, --output
+ Optional-> output dir for the colorized source.
+ default: output dir is the input dir.
+ To output html to stdout use one of the following (-,stdout)
+ Stdout can be used without stdin if you give a file as input.
+ -c, --color
+ Optional-> null, mono, dark, dark2, lite, idle, pythonwin, viewcvs
+ default: dark
+ -s, --show
+ Optional-> Show page after creation.
+ default: no show
+ -m, --markup
+ Optional-> html, css, xhtml
+ css, xhtml also support external stylesheets (-e,--external)
+ default: HTML
+ -e, --external
+ Optional-> use with css, xhtml
+ Writes an style sheet instead of embedding it in the page
+ saves it as pystyle.css in the same directory.
+ html markup will silently ignore this flag.
+ -H, --header
+ Opional-> add a page header to the top of the output
+ -H
+ Builtin header (name,date,hrule)
+ --header
+ You must specify a filename.
+ The header file must be valid html
+ and must handle its own font colors.
+ ex. --header c:/tmp/header.txt
+ -F, --footer
+ Opional-> add a page footer to the bottom of the output
+ -F
+ Builtin footer (hrule,name,date)
+ --footer
+ You must specify a filename.
+ The footer file must be valid html
+ and must handle its own font colors.
+ ex. --footer c:/tmp/footer.txt
+ -l, --linenumbers
+ Optional-> default is no linenumbers
+ Adds line numbers to the start of each line in the code.
+ --convertpage
+ Given a webpage that has code embedded in tags it will
+ convert embedded code to colorized html.
+ (see pageconvert for details)
+ -----------------------------------------------------------------------------
+ Option usage:
+ # Test and show pages
+ python PySourceColor.py -t -s
+ # Test and only show profile results
+ python PySourceColor.py -t -p
+ # Colorize all .py,.pyw files in cwdir you can also use: (.,cwd)
+ python PySourceColor.py -i .
+ # Using long options w/ =
+ python PySourceColor.py --in=c:/myDir/my.py --color=lite --show
+ # Using short options w/out =
+ python PySourceColor.py -i c:/myDir/ -c idle -m css -e
+ # Using any mix
+ python PySourceColor.py --in . -o=c:/myDir --show
+ # Place a custom header on your files
+ python PySourceColor.py -i . -o c:/tmp -m xhtml --header c:/header.txt
+ -----------------------------------------------------------------------------
+ Stdio usage:
+ # Stdio using no options
+ python PySourceColor.py < c:/MyFile.py > c:/tmp/MyFile.html
+ # Using stdin alone automatically uses stdout for output: (stdin,-)
+ python PySourceColor.py -i- < c:/MyFile.py > c:/tmp/myfile.html
+ # Stdout can also be written to directly from a file instead of stdin
+ python PySourceColor.py -i c:/MyFile.py -m css -o- > c:/tmp/myfile.html
+ # Stdin can be used as input , but output can still be specified
+ python PySourceColor.py -i- -o c:/pydoc.py.html -s < c:/Python22/my.py
+ _____________________________________________________________________________
+ """
+ print(doc % (__version__))
+ sys.exit(1)
+
+###################################################### Command line interface
+
+def cli():
+ """Handle command line args and redirections"""
+ try:
+ # try to get command line args
+ opts, args = getopt.getopt(sys.argv[1:],
+ "hseqtplHFi:o:c:m:h:f:",["help", "show", "quiet",
+ "test", "external", "linenumbers", "convertpage", "profile",
+ "input=", "output=", "color=", "markup=","header=", "footer="])
+ except getopt.GetoptError:
+ # on error print help information and exit:
+ Usage()
+ # init some names
+ input = None
+ output = None
+ colorscheme = None
+ markup = 'html'
+ header = None
+ footer = None
+ linenumbers = 0
+ show = 0
+ quiet = 0
+ test = 0
+ profile = 0
+ convertpage = 0
+ form = None
+ # if we have args then process them
+ for o, a in opts:
+ if o in ["-h", "--help"]:
+ Usage()
+ sys.exit()
+ if o in ["-o", "--output", "--out"]:
+ output = a
+ if o in ["-i", "--input", "--in"]:
+ input = a
+ if input in [".", "cwd"]:
+ input = os.getcwd()
+ if o in ["-s", "--show"]:
+ show = 1
+ if o in ["-q", "--quiet"]:
+ quiet = 1
+ if o in ["-t", "--test"]:
+ test = 1
+ if o in ["--convertpage"]:
+ convertpage = 1
+ if o in ["-p", "--profile"]:
+ profile = 1
+ if o in ["-e", "--external"]:
+ form = 'external'
+ if o in ["-m", "--markup"]:
+ markup = str(a)
+ if o in ["-l", "--linenumbers"]:
+ linenumbers = 1
+ if o in ["--header"]:
+ header = str(a)
+ elif o == "-H":
+ header = ''
+ if o in ["--footer"]:
+ footer = str(a)
+ elif o == "-F":
+ footer = ''
+ if o in ["-c", "--color"]:
+ try:
+ colorscheme = globals().get(a.lower())
+ except:
+ traceback.print_exc()
+ Usage()
+ if test:
+ if profile:
+ import profile
+ profile.run('_test(show=%s, quiet=%s)'%(show,quiet))
+ else:
+ # Parse this script in every possible colorscheme and markup
+ _test(show,quiet)
+ elif input in [None, "-", "stdin"] or output in ["-", "stdout"]:
+ # determine if we are going to use stdio
+ if input not in [None, "-", "stdin"]:
+ if os.path.isfile(input) :
+ path2stdout(input, colors=colorscheme, markup=markup,
+ linenumbers=linenumbers, header=header,
+ footer=footer, form=form)
+ else:
+ raise PathError('File does not exists!')
+ else:
+ try:
+ if sys.stdin.isatty():
+ raise InputError('Please check input!')
+ else:
+ if output in [None,"-","stdout"]:
+ str2stdout(sys.stdin.read(), colors=colorscheme,
+ markup=markup, header=header,
+ footer=footer, linenumbers=linenumbers,
+ form=form)
+ else:
+ str2file(sys.stdin.read(), outfile=output, show=show,
+ markup=markup, header=header, footer=footer,
+ linenumbers=linenumbers, form=form)
+ except:
+ traceback.print_exc()
+ Usage()
+ else:
+ if os.path.exists(input):
+ if convertpage:
+ # if there was at least an input given we can proceed
+ pageconvert(input, out=output, colors=colorscheme,
+ show=show, markup=markup,linenumbers=linenumbers)
+ else:
+ # if there was at least an input given we can proceed
+ convert(source=input, outdir=output, colors=colorscheme,
+ show=show, markup=markup, quiet=quiet, header=header,
+ footer=footer, linenumbers=linenumbers, form=form)
+ else:
+ raise PathError('File does not exists!')
+ Usage()
+
+######################################################### Simple markup tests
+
+def _test(show=0, quiet=0):
+ """Test the parser and most of the functions.
+
+ There are 19 test total(eight colorschemes in three diffrent markups,
+ and a str2file test. Most functions are tested by this.
+ """
+ fi = sys.argv[0]
+ if not fi.endswith('.exe'):# Do not test if frozen as an archive
+ # this is a collection of test, most things are covered.
+ path2file(fi, '/tmp/null.html', null, show=show, quiet=quiet)
+ path2file(fi, '/tmp/null_css.html', null, show=show,
+ markup='css', quiet=quiet)
+ path2file(fi, '/tmp/mono.html', mono, show=show, quiet=quiet)
+ path2file(fi, '/tmp/mono_css.html', mono, show=show,
+ markup='css', quiet=quiet)
+ path2file(fi, '/tmp/lite.html', lite, show=show, quiet=quiet)
+ path2file(fi, '/tmp/lite_css.html', lite, show=show,
+ markup='css', quiet=quiet, header='', footer='',
+ linenumbers=1)
+ path2file(fi, '/tmp/lite_xhtml.html', lite, show=show,
+ markup='xhtml', quiet=quiet)
+ path2file(fi, '/tmp/dark.html', dark, show=show, quiet=quiet)
+ path2file(fi, '/tmp/dark_css.html', dark, show=show,
+ markup='css', quiet=quiet, linenumbers=1)
+ path2file(fi, '/tmp/dark2.html', dark2, show=show, quiet=quiet)
+ path2file(fi, '/tmp/dark2_css.html', dark2, show=show,
+ markup='css', quiet=quiet)
+ path2file(fi, '/tmp/dark2_xhtml.html', dark2, show=show,
+ markup='xhtml', quiet=quiet, header='', footer='',
+ linenumbers=1, form='external')
+ path2file(fi, '/tmp/idle.html', idle, show=show, quiet=quiet)
+ path2file(fi, '/tmp/idle_css.html', idle, show=show,
+ markup='css', quiet=quiet)
+ path2file(fi, '/tmp/viewcvs.html', viewcvs, show=show,
+ quiet=quiet, linenumbers=1)
+ path2file(fi, '/tmp/viewcvs_css.html', viewcvs, show=show,
+ markup='css', linenumbers=1, quiet=quiet)
+ path2file(fi, '/tmp/pythonwin.html', pythonwin, show=show,
+ quiet=quiet)
+ path2file(fi, '/tmp/pythonwin_css.html', pythonwin, show=show,
+ markup='css', quiet=quiet)
+ teststr=r'''"""This is a test of decorators and other things"""
+# This should be line 421...
+@whatever(arg,arg2)
+@A @B(arghh) @C
+def LlamaSaysNi(arg='Ni!',arg2="RALPH"):
+ """This docstring is deeply disturbed by all the llama references"""
+ print('%s The Wonder Llama says %s'% (arg2,arg))
+# So I was like duh!, and he was like ya know?!,
+# and so we were both like huh...wtf!? RTFM!! LOL!!;)
+@staticmethod## Double comments are KewL.
+def LlamasRLumpy():
+ """This docstring is too sexy to be here.
+ """
+ u"""
+=============================
+A Møøse once bit my sister...
+=============================
+ """
+ ## Relax, this won't hurt a bit, just a simple, painless procedure,
+ ## hold still while I get the anesthetizing hammer.
+ m = {'three':'1','won':'2','too':'3'}
+ o = r'fishy\fishy\fishy/fish\oh/where/is\my/little\..'
+ python = uR"""
+ No realli! She was Karving her initials øn the møøse with the sharpened end
+ of an interspace tøøthbrush given her by Svenge - her brother-in-law -an Oslo
+ dentist and star of many Norwegian møvies: "The Høt Hands of an Oslo
+ Dentist", "Fillings of Passion", "The Huge Mølars of Horst Nordfink"..."""
+ RU"""142 MEXICAN WHOOPING LLAMAS"""#<-Can you fit 142 llamas in a red box?
+ n = u' HERMSGERVØRDENBRØTBØRDA ' + """ YUTTE """
+ t = """SAMALLNIATNUOMNAIRODAUCE"""+"DENIARTYLLAICEPS04"
+ ## We apologise for the fault in the
+ ## comments. Those responsible have been
+ ## sacked.
+ y = '14 NORTH CHILEAN GUANACOS \
+(CLOSELY RELATED TO THE LLAMA)'
+ rules = [0,1,2,3,4,5]
+ print y'''
+ htmlPath = os.path.abspath('/tmp/strtest_lines.html')
+ str2file(teststr, htmlPath, colors=dark, markup='xhtml',
+ linenumbers=420, show=show)
+ _printinfo(" wrote %s" % htmlPath, quiet)
+ htmlPath = os.path.abspath('/tmp/strtest_nolines.html')
+ str2file(teststr, htmlPath, colors=dark, markup='xhtml',
+ show=show)
+ _printinfo(" wrote %s" % htmlPath, quiet)
+ else:
+ Usage()
+ return
+
+# emacs wants this: '
+
+####################################################### User funtctions
+
+def str2stdout(sourcestring, colors=None, title='', markup='html',
+ header=None, footer=None,
+ linenumbers=0, form=None):
+ """Converts a code(string) to colorized HTML. Writes to stdout.
+
+ form='code',or'snip' (for "<pre>yourcode</pre>" only)
+ colors=null,mono,lite,dark,dark2,idle,or pythonwin
+ """
+ Parser(sourcestring, colors=colors, title=title, markup=markup,
+ header=header, footer=footer,
+ linenumbers=linenumbers).format(form)
+
+def path2stdout(sourcepath, title='', colors=None, markup='html',
+ header=None, footer=None,
+ linenumbers=0, form=None):
+ """Converts code(file) to colorized HTML. Writes to stdout.
+
+ form='code',or'snip' (for "<pre>yourcode</pre>" only)
+ colors=null,mono,lite,dark,dark2,idle,or pythonwin
+ """
+ sourcestring = open(sourcepath).read()
+ Parser(sourcestring, colors=colors, title=sourcepath,
+ markup=markup, header=header, footer=footer,
+ linenumbers=linenumbers).format(form)
+
+def str2html(sourcestring, colors=None, title='',
+ markup='html', header=None, footer=None,
+ linenumbers=0, form=None):
+ """Converts a code(string) to colorized HTML. Returns an HTML string.
+
+ form='code',or'snip' (for "<pre>yourcode</pre>" only)
+ colors=null,mono,lite,dark,dark2,idle,or pythonwin
+ """
+ stringIO = StringIO.StringIO()
+ Parser(sourcestring, colors=colors, title=title, out=stringIO,
+ markup=markup, header=header, footer=footer,
+ linenumbers=linenumbers).format(form)
+ stringIO.seek(0)
+ return stringIO.read()
+
+def str2css(sourcestring, colors=None, title='',
+ markup='css', header=None, footer=None,
+ linenumbers=0, form=None):
+ """Converts a code string to colorized CSS/HTML. Returns CSS/HTML string
+
+ If form != None then this will return (stylesheet_str, code_str)
+ colors=null,mono,lite,dark,dark2,idle,or pythonwin
+ """
+ if markup.lower() not in ['css' ,'xhtml']:
+ markup = 'css'
+ stringIO = StringIO.StringIO()
+ parse = Parser(sourcestring, colors=colors, title=title,
+ out=stringIO, markup=markup,
+ header=header, footer=footer,
+ linenumbers=linenumbers)
+ parse.format(form)
+ stringIO.seek(0)
+ if form != None:
+ return parse._sendCSSStyle(external=1), stringIO.read()
+ else:
+ return None, stringIO.read()
+
+def str2markup(sourcestring, colors=None, title = '',
+ markup='xhtml', header=None, footer=None,
+ linenumbers=0, form=None):
+ """ Convert code strings into ([stylesheet or None], colorized string) """
+ if markup.lower() == 'html':
+ return None, str2html(sourcestring, colors=colors, title=title,
+ header=header, footer=footer, markup=markup,
+ linenumbers=linenumbers, form=form)
+ else:
+ return str2css(sourcestring, colors=colors, title=title,
+ header=header, footer=footer, markup=markup,
+ linenumbers=linenumbers, form=form)
+
+def str2file(sourcestring, outfile, colors=None, title='',
+ markup='html', header=None, footer=None,
+ linenumbers=0, show=0, dosheet=1, form=None):
+ """Converts a code string to a file.
+
+ makes no attempt at correcting bad pathnames
+ """
+ css , html = str2markup(sourcestring, colors=colors, title='',
+ markup=markup, header=header, footer=footer,
+ linenumbers=linenumbers, form=form)
+ # write html
+ f = open(outfile,'wt')
+ f.writelines(html)
+ f.close()
+ #write css
+ if css != None and dosheet:
+ dir = os.path.dirname(outfile)
+ outcss = os.path.join(dir,'pystyle.css')
+ f = open(outcss,'wt')
+ f.writelines(css)
+ f.close()
+ if show:
+ showpage(outfile)
+
+def path2html(sourcepath, colors=None, markup='html',
+ header=None, footer=None,
+ linenumbers=0, form=None):
+ """Converts code(file) to colorized HTML. Returns an HTML string.
+
+ form='code',or'snip' (for "<pre>yourcode</pre>" only)
+ colors=null,mono,lite,dark,dark2,idle,or pythonwin
+ """
+ stringIO = StringIO.StringIO()
+ sourcestring = open(sourcepath).read()
+ Parser(sourcestring, colors, title=sourcepath, out=stringIO,
+ markup=markup, header=header, footer=footer,
+ linenumbers=linenumbers).format(form)
+ stringIO.seek(0)
+ return stringIO.read()
+
+def convert(source, outdir=None, colors=None,
+ show=0, markup='html', quiet=0,
+ header=None, footer=None, linenumbers=0, form=None):
+ """Takes a file or dir as input and places the html in the outdir.
+
+ If outdir is none it defaults to the input dir
+ """
+ count=0
+ # If it is a filename then path2file
+ if not os.path.isdir(source):
+ if os.path.isfile(source):
+ count+=1
+ path2file(source, outdir, colors, show, markup,
+ quiet, form, header, footer, linenumbers, count)
+ else:
+ raise PathError('File does not exist!')
+ # If we pass in a dir we need to walkdir for files.
+ # Then we need to colorize them with path2file
+ else:
+ fileList = walkdir(source)
+ if fileList != None:
+ # make sure outdir is a dir
+ if outdir != None:
+ if os.path.splitext(outdir)[1] != '':
+ outdir = os.path.split(outdir)[0]
+ for item in fileList:
+ count+=1
+ path2file(item, outdir, colors, show, markup,
+ quiet, form, header, footer, linenumbers, count)
+ _printinfo('Completed colorizing %s files.'%str(count), quiet)
+ else:
+ _printinfo("No files to convert in dir.", quiet)
+
+def path2file(sourcePath, out=None, colors=None, show=0,
+ markup='html', quiet=0, form=None,
+ header=None, footer=None, linenumbers=0, count=1):
+ """ Converts python source to html file"""
+ # If no outdir is given we use the sourcePath
+ if out == None:#this is a guess
+ htmlPath = sourcePath + '.html'
+ else:
+ # If we do give an out_dir, and it does
+ # not exist , it will be created.
+ if os.path.splitext(out)[1] == '':
+ if not os.path.isdir(out):
+ os.makedirs(out)
+ sourceName = os.path.basename(sourcePath)
+ htmlPath = os.path.join(out,sourceName)+'.html'
+ # If we do give an out_name, and its dir does
+ # not exist , it will be created.
+ else:
+ outdir = os.path.split(out)[0]
+ if not os.path.isdir(outdir):
+ os.makedirs(outdir)
+ htmlPath = out
+ htmlPath = os.path.abspath(htmlPath)
+ # Open the text and do the parsing.
+ source = open(sourcePath).read()
+ parse = Parser(source, colors, sourcePath, open(htmlPath, 'wt'),
+ markup, header, footer, linenumbers)
+ parse.format(form)
+ _printinfo(" wrote %s" % htmlPath, quiet)
+ # html markup will ignore the external flag, but
+ # we need to stop the blank file from being written.
+ if form == 'external' and count == 1 and markup != 'html':
+ cssSheet = parse._sendCSSStyle(external=1)
+ cssPath = os.path.join(os.path.dirname(htmlPath),'pystyle.css')
+ css = open(cssPath, 'wt')
+ css.write(cssSheet)
+ css.close()
+ _printinfo(" wrote %s" % cssPath, quiet)
+ if show:
+ # load HTML page into the default web browser.
+ showpage(htmlPath)
+ return htmlPath
+
+def tagreplace(sourcestr, colors=lite, markup='xhtml',
+ linenumbers=0, dosheet=1, tagstart='<PY>'.lower(),
+ tagend='</PY>'.lower(), stylesheet='pystyle.css'):
+ """This is a helper function for pageconvert. Returns css, page.
+ """
+ if markup.lower() != 'html':
+ link = '<link rel="stylesheet" href="%s" type="text/css"/></head>'
+ css = link%stylesheet
+ if sourcestr.find(css) == -1:
+ sourcestr = sourcestr.replace('</head>', css, 1)
+ starttags = sourcestr.count(tagstart)
+ endtags = sourcestr.count(tagend)
+ if starttags:
+ if starttags == endtags:
+ for _ in range(starttags):
+ datastart = sourcestr.find(tagstart)
+ dataend = sourcestr.find(tagend)
+ data = sourcestr[datastart+len(tagstart):dataend]
+ data = unescape(data)
+ css , data = str2markup(data, colors=colors,
+ linenumbers=linenumbers, markup=markup, form='embed')
+ start = sourcestr[:datastart]
+ end = sourcestr[dataend+len(tagend):]
+ sourcestr = ''.join([start,data,end])
+ else:
+ raise InputError('Tag mismatch!\nCheck %s,%s tags'%tagstart,tagend)
+ if not dosheet:
+ css = None
+ return css, sourcestr
+
+def pageconvert(path, out=None, colors=lite, markup='xhtml', linenumbers=0,
+ dosheet=1, tagstart='<PY>'.lower(), tagend='</PY>'.lower(),
+ stylesheet='pystyle', show=1, returnstr=0):
+ """This function can colorize Python source
+
+ that is written in a webpage enclosed in tags.
+ """
+ if out == None:
+ out = os.path.dirname(path)
+ infile = open(path, 'r').read()
+ css,page = tagreplace(sourcestr=infile,colors=colors,
+ markup=markup, linenumbers=linenumbers, dosheet=dosheet,
+ tagstart=tagstart, tagend=tagend, stylesheet=stylesheet)
+ if not returnstr:
+ newpath = os.path.abspath(os.path.join(
+ out,'tmp', os.path.basename(path)))
+ if not os.path.exists(newpath):
+ try:
+ os.makedirs(os.path.dirname(newpath))
+ except:
+ pass#traceback.print_exc()
+ #Usage()
+ y = open(newpath, 'w')
+ y.write(page)
+ y.close()
+ if css:
+ csspath = os.path.abspath(os.path.join(
+ out,'tmp','%s.css'%stylesheet))
+ x = open(csspath,'w')
+ x.write(css)
+ x.close()
+ if show:
+ try:
+ os.startfile(newpath)
+ except:
+ traceback.print_exc()
+ return newpath
+ else:
+ return css, page
+
+##################################################################### helpers
+
+def walkdir(dir):
+ """Return a list of .py and .pyw files from a given directory.
+
+ This function can be written as a generator Python 2.3, or a genexp
+ in Python 2.4. But 2.2 and 2.1 would be left out....
+ """
+ # Get a list of files that match *.py*
+ GLOB_PATTERN = os.path.join(dir, "*.[p][y]*")
+ pathlist = glob.glob(GLOB_PATTERN)
+ # Now filter out all but py and pyw
+ filterlist = [x for x in pathlist
+ if x.endswith('.py')
+ or x.endswith('.pyw')]
+ if filterlist != []:
+ # if we have a list send it
+ return filterlist
+ else:
+ return None
+
+def showpage(path):
+ """Helper function to open webpages"""
+ try:
+ import webbrowser
+ webbrowser.open_new(os.path.abspath(path))
+ except:
+ traceback.print_exc()
+
+def _printinfo(message, quiet):
+ """Helper to print messages"""
+ if not quiet:
+ print(message)
+
+def escape(text):
+ """escape text for html. similar to cgi.escape"""
+ text = text.replace("&", "&amp;")
+ text = text.replace("<", "&lt;")
+ text = text.replace(">", "&gt;")
+ return text
+
+def unescape(text):
+ """unsecape escaped text"""
+ text = text.replace("&quot;", '"')
+ text = text.replace("&gt;", ">")
+ text = text.replace("&lt;", "<")
+ text = text.replace("&amp;", "&")
+ return text
+
+########################################################### Custom Exceptions
+
+class PySourceColorError(Exception):
+ # Base for custom errors
+ def __init__(self, msg=''):
+ self._msg = msg
+ Exception.__init__(self, msg)
+ def __repr__(self):
+ return self._msg
+ __str__ = __repr__
+
+class PathError(PySourceColorError):
+ def __init__(self, msg):
+ PySourceColorError.__init__(self,
+ 'Path error! : %s'% msg)
+
+class InputError(PySourceColorError):
+ def __init__(self, msg):
+ PySourceColorError.__init__(self,
+ 'Input error! : %s'% msg)
+
+########################################################## Python code parser
+
+class Parser(object):
+
+ """MoinMoin python parser heavily chopped :)"""
+
+ def __init__(self, raw, colors=None, title='', out=sys.stdout,
+ markup='html', header=None, footer=None, linenumbers=0):
+ """Store the source text & set some flags"""
+ if colors == None:
+ colors = defaultColors
+ self.raw = raw.expandtabs().rstrip()
+ self.title = os.path.basename(title)
+ self.out = out
+ self.line = ''
+ self.lasttext = ''
+ self.argFlag = 0
+ self.classFlag = 0
+ self.defFlag = 0
+ self.decoratorFlag = 0
+ self.external = 0
+ self.markup = markup.upper()
+ self.colors = colors
+ self.header = header
+ self.footer = footer
+ self.doArgs = 1 # overrides the new tokens
+ self.doNames = 1 # overrides the new tokens
+ self.doMathOps = 1 # overrides the new tokens
+ self.doBrackets = 1 # overrides the new tokens
+ self.doURL = 1 # override url conversion
+ self.LINENUMHOLDER = "___line___".upper()
+ self.LINESTART = "___start___".upper()
+ self.skip = 0
+ # add space left side of code for padding.Override in color dict.
+ self.extraspace = self.colors.get(EXTRASPACE, '')
+ # Linenumbers less then zero also have numberlinks
+ self.dolinenums = self.linenum = abs(linenumbers)
+ if linenumbers < 0:
+ self.numberlinks = 1
+ else:
+ self.numberlinks = 0
+
+ def format(self, form=None):
+ """Parse and send the colorized source"""
+ if form in ('snip','code'):
+ self.addEnds = 0
+ elif form == 'embed':
+ self.addEnds = 0
+ self.external = 1
+ else:
+ if form == 'external':
+ self.external = 1
+ self.addEnds = 1
+
+ # Store line offsets in self.lines
+ self.lines = [0, 0]
+ pos = 0
+
+ # Add linenumbers
+ if self.dolinenums:
+ start=self.LINENUMHOLDER+' '+self.extraspace
+ else:
+ start=''+self.extraspace
+ newlines = []
+ lines = self.raw.splitlines(0)
+ for l in lines:
+ # span and div escape for customizing and embedding raw text
+ if (l.startswith('#$#')
+ or l.startswith('#%#')
+ or l.startswith('#@#')):
+ newlines.append(l)
+ else:
+ # kludge for line spans in css,xhtml
+ if self.markup in ['XHTML','CSS']:
+ newlines.append(self.LINESTART+' '+start+l)
+ else:
+ newlines.append(start+l)
+ self.raw = "\n".join(newlines)+'\n'# plus an extra newline at the end
+
+ # Gather lines
+ while 1:
+ pos = self.raw.find('\n', pos) + 1
+ if not pos: break
+ self.lines.append(pos)
+ self.lines.append(len(self.raw))
+
+ # Wrap text in a filelike object
+ self.pos = 0
+ text = StringIO.StringIO(self.raw)
+
+ # Markup start
+ if self.addEnds:
+ self._doPageStart()
+ else:
+ self._doSnippetStart()
+
+ ## Tokenize calls the __call__
+ ## function for each token till done.
+ # Parse the source and write out the results.
+ try:
+ tokenize.tokenize(text.readline, self)
+ except tokenize.TokenError as ex:
+ msg = ex[0]
+ line = ex[1][0]
+ self.out.write("<h3>ERROR: %s</h3>%s\n"%
+ (msg, self.raw[self.lines[line]:]))
+ #traceback.print_exc()
+
+ # Markup end
+ if self.addEnds:
+ self._doPageEnd()
+ else:
+ self._doSnippetEnd()
+
+ def __call__(self, toktype, toktext, srow_col, erow_col, line):
+ """Token handler. Order is important do not rearrange."""
+ self.line = line
+ srow, scol = srow_col
+ erow, ecol = erow_col
+ # Calculate new positions
+ oldpos = self.pos
+ newpos = self.lines[srow] + scol
+ self.pos = newpos + len(toktext)
+ # Handle newlines
+ if toktype in (token.NEWLINE, tokenize.NL):
+ self.decoratorFlag = self.argFlag = 0
+ # kludge for line spans in css,xhtml
+ if self.markup in ['XHTML','CSS']:
+ self.out.write('</span>')
+ self.out.write('\n')
+ return
+
+ # Send the original whitespace, and tokenize backslashes if present.
+ # Tokenizer.py just sends continued line backslashes with whitespace.
+ # This is a hack to tokenize continued line slashes as operators.
+ # Should continued line backslashes be treated as operators
+ # or some other token?
+
+ if newpos > oldpos:
+ if self.raw[oldpos:newpos].isspace():
+ # consume a single space after linestarts and linenumbers
+ # had to have them so tokenizer could seperate them.
+ # multiline strings are handled by do_Text functions
+ if self.lasttext != self.LINESTART \
+ and self.lasttext != self.LINENUMHOLDER:
+ self.out.write(self.raw[oldpos:newpos])
+ else:
+ self.out.write(self.raw[oldpos+1:newpos])
+ else:
+ slash = self.raw[oldpos:newpos].find('\\')+oldpos
+ self.out.write(self.raw[oldpos:slash])
+ getattr(self, '_send%sText'%(self.markup))(OPERATOR, '\\')
+ self.linenum+=1
+ # kludge for line spans in css,xhtml
+ if self.markup in ['XHTML','CSS']:
+ self.out.write('</span>')
+ self.out.write(self.raw[slash+1:newpos])
+
+ # Skip indenting tokens
+ if toktype in (token.INDENT, token.DEDENT):
+ self.pos = newpos
+ return
+
+ # Look for operators
+ if token.LPAR <= toktype and toktype <= token.OP:
+ # Trap decorators py2.4 >
+ if toktext == '@':
+ toktype = DECORATOR
+ # Set a flag if this was the decorator start so
+ # the decorator name and arguments can be identified
+ self.decoratorFlag = self.argFlag = 1
+ else:
+ if self.doArgs:
+ # Find the start for arguments
+ if toktext == '(' and self.argFlag:
+ self.argFlag = 2
+ # Find the end for arguments
+ elif toktext == ':':
+ self.argFlag = 0
+ ## Seperate the diffrent operator types
+ # Brackets
+ if self.doBrackets and toktext in ['[',']','(',')','{','}']:
+ toktype = BRACKETS
+ # Math operators
+ elif self.doMathOps and toktext in ['*=','**=','-=','+=','|=',
+ '%=','>>=','<<=','=','^=',
+ '/=', '+','-','**','*','/','%']:
+ toktype = MATH_OPERATOR
+ # Operator
+ else:
+ toktype = OPERATOR
+ # example how flags should work.
+ # def fun(arg=argvalue,arg2=argvalue2):
+ # 0 1 2 A 1 N 2 A 1 N 0
+ if toktext == "=" and self.argFlag == 2:
+ self.argFlag = 1
+ elif toktext == "," and self.argFlag == 1:
+ self.argFlag = 2
+ # Look for keywords
+ elif toktype == NAME and keyword.iskeyword(toktext):
+ toktype = KEYWORD
+ # Set a flag if this was the class / def start so
+ # the class / def name and arguments can be identified
+ if toktext in ['class', 'def']:
+ if toktext =='class' and \
+ not line[:line.find('class')].endswith('.'):
+ self.classFlag = self.argFlag = 1
+ elif toktext == 'def' and \
+ not line[:line.find('def')].endswith('.'):
+ self.defFlag = self.argFlag = 1
+ else:
+ # must have used a keyword as a name i.e. self.class
+ toktype = ERRORTOKEN
+
+ # Look for class, def, decorator name
+ elif (self.classFlag or self.defFlag or self.decoratorFlag) \
+ and self.doNames:
+ if self.classFlag:
+ self.classFlag = 0
+ toktype = CLASS_NAME
+ elif self.defFlag:
+ self.defFlag = 0
+ toktype = DEF_NAME
+ elif self.decoratorFlag:
+ self.decoratorFlag = 0
+ toktype = DECORATOR_NAME
+
+ # Look for strings
+ # Order of evaluation is important do not change.
+ elif toktype == token.STRING:
+ text = toktext.lower()
+ # TRIPLE DOUBLE QUOTE's
+ if (text[:3] == '"""'):
+ toktype = TRIPLEDOUBLEQUOTE
+ elif (text[:4] == 'r"""'):
+ toktype = TRIPLEDOUBLEQUOTE_R
+ elif (text[:4] == 'u"""' or
+ text[:5] == 'ur"""'):
+ toktype = TRIPLEDOUBLEQUOTE_U
+ # DOUBLE QUOTE's
+ elif (text[:1] == '"'):
+ toktype = DOUBLEQUOTE
+ elif (text[:2] == 'r"'):
+ toktype = DOUBLEQUOTE_R
+ elif (text[:2] == 'u"' or
+ text[:3] == 'ur"'):
+ toktype = DOUBLEQUOTE_U
+ # TRIPLE SINGLE QUOTE's
+ elif (text[:3] == "'''"):
+ toktype = TRIPLESINGLEQUOTE
+ elif (text[:4] == "r'''"):
+ toktype = TRIPLESINGLEQUOTE_R
+ elif (text[:4] == "u'''" or
+ text[:5] == "ur'''"):
+ toktype = TRIPLESINGLEQUOTE_U
+ # SINGLE QUOTE's
+ elif (text[:1] == "'"):
+ toktype = SINGLEQUOTE
+ elif (text[:2] == "r'"):
+ toktype = SINGLEQUOTE_R
+ elif (text[:2] == "u'" or
+ text[:3] == "ur'"):
+ toktype = SINGLEQUOTE_U
+
+ # test for invalid string declaration
+ if self.lasttext.lower() == 'ru':
+ toktype = ERRORTOKEN
+
+ # Look for comments
+ elif toktype == COMMENT:
+ if toktext[:2] == "##":
+ toktype = DOUBLECOMMENT
+ elif toktext[:3] == '#$#':
+ toktype = TEXT
+ self.textFlag = 'SPAN'
+ toktext = toktext[3:]
+ elif toktext[:3] == '#%#':
+ toktype = TEXT
+ self.textFlag = 'DIV'
+ toktext = toktext[3:]
+ elif toktext[:3] == '#@#':
+ toktype = TEXT
+ self.textFlag = 'RAW'
+ toktext = toktext[3:]
+ if self.doURL:
+ # this is a 'fake helper function'
+ # url(URI,Alias_name) or url(URI)
+ url_pos = toktext.find('url(')
+ if url_pos != -1:
+ before = toktext[:url_pos]
+ url = toktext[url_pos+4:]
+ splitpoint = url.find(',')
+ endpoint = url.find(')')
+ after = url[endpoint+1:]
+ url = url[:endpoint]
+ if splitpoint != -1:
+ urlparts = url.split(',',1)
+ toktext = '%s<a href="%s">%s</a>%s'%(
+ before,urlparts[0],urlparts[1].lstrip(),after)
+ else:
+ toktext = '%s<a href="%s">%s</a>%s'%(before,url,url,after)
+
+ # Seperate errors from decorators
+ elif toktype == ERRORTOKEN:
+ # Bug fix for < py2.4
+ # space between decorators
+ if self.argFlag and toktext.isspace():
+ #toktype = NAME
+ self.out.write(toktext)
+ return
+ # Bug fix for py2.2 linenumbers with decorators
+ elif toktext.isspace():
+ # What if we have a decorator after a >>> or ...
+ #p = line.find('@')
+ #if p >= 0 and not line[:p].isspace():
+ #self.out.write(toktext)
+ #return
+ if self.skip:
+ self.skip=0
+ return
+ else:
+ self.out.write(toktext)
+ return
+ # trap decorators < py2.4
+ elif toktext == '@':
+ toktype = DECORATOR
+ # Set a flag if this was the decorator start so
+ # the decorator name and arguments can be identified
+ self.decoratorFlag = self.argFlag = 1
+
+ # Seperate args from names
+ elif (self.argFlag == 2 and
+ toktype == NAME and
+ toktext != 'None' and
+ self.doArgs):
+ toktype = ARGS
+
+ # Look for line numbers
+ # The conversion code for them is in the send_text functions.
+ if toktext in [self.LINENUMHOLDER,self.LINESTART]:
+ toktype = LINENUMBER
+ # if we don't have linenumbers set flag
+ # to skip the trailing space from linestart
+ if toktext == self.LINESTART and not self.dolinenums \
+ or toktext == self.LINENUMHOLDER:
+ self.skip=1
+
+
+ # Skip blank token that made it thru
+ ## bugfix for the last empty tag.
+ if toktext == '':
+ return
+
+ # Last token text history
+ self.lasttext = toktext
+
+ # escape all but the urls in the comments
+ if toktype in (DOUBLECOMMENT, COMMENT):
+ if toktext.find('<a href=') == -1:
+ toktext = escape(toktext)
+ else:
+ pass
+ elif toktype == TEXT:
+ pass
+ else:
+ toktext = escape(toktext)
+
+ # Send text for any markup
+ getattr(self, '_send%sText'%(self.markup))(toktype, toktext)
+ return
+
+ ################################################################# Helpers
+
+ def _doSnippetStart(self):
+ if self.markup == 'HTML':
+ # Start of html snippet
+ self.out.write('<pre>\n')
+ else:
+ # Start of css/xhtml snippet
+ self.out.write(self.colors.get(CODESTART,'<pre class="py">\n'))
+
+ def _doSnippetEnd(self):
+ # End of html snippet
+ self.out.write(self.colors.get(CODEEND,'</pre>\n'))
+
+ ######################################################## markup selectors
+
+ def _getFile(self, filepath):
+ try:
+ _file = open(filepath,'r')
+ content = _file.read()
+ _file.close()
+ except:
+ traceback.print_exc()
+ content = ''
+ return content
+
+ def _doPageStart(self):
+ getattr(self, '_do%sStart'%(self.markup))()
+
+ def _doPageHeader(self):
+ if self.header != None:
+ if self.header.find('#$#') != -1 or \
+ self.header.find('#$#') != -1 or \
+ self.header.find('#%#') != -1:
+ self.out.write(self.header[3:])
+ else:
+ if self.header != '':
+ self.header = self._getFile(self.header)
+ getattr(self, '_do%sHeader'%(self.markup))()
+
+ def _doPageFooter(self):
+ if self.footer != None:
+ if self.footer.find('#$#') != -1 or \
+ self.footer.find('#@#') != -1 or \
+ self.footer.find('#%#') != -1:
+ self.out.write(self.footer[3:])
+ else:
+ if self.footer != '':
+ self.footer = self._getFile(self.footer)
+ getattr(self, '_do%sFooter'%(self.markup))()
+
+ def _doPageEnd(self):
+ getattr(self, '_do%sEnd'%(self.markup))()
+
+ ################################################### color/style retrieval
+ ## Some of these are not used anymore but are kept for documentation
+
+ def _getLineNumber(self):
+ num = self.linenum
+ self.linenum+=1
+ return str(num).rjust(5)+" "
+
+ def _getTags(self, key):
+ # style tags
+ return self.colors.get(key, self.colors[NAME])[0]
+
+ def _getForeColor(self, key):
+ # get text foreground color, if not set to black
+ color = self.colors.get(key, self.colors[NAME])[1]
+ if color[:1] != '#':
+ color = '#000000'
+ return color
+
+ def _getBackColor(self, key):
+ # get text background color
+ return self.colors.get(key, self.colors[NAME])[2]
+
+ def _getPageColor(self):
+ # get page background color
+ return self.colors.get(PAGEBACKGROUND, '#FFFFFF')
+
+ def _getStyle(self, key):
+ # get the token style from the color dictionary
+ return self.colors.get(key, self.colors[NAME])
+
+ def _getMarkupClass(self, key):
+ # get the markup class name from the markup dictionary
+ return MARKUPDICT.get(key, MARKUPDICT[NAME])
+
+ def _getDocumentCreatedBy(self):
+ return '<!--This document created by %s ver.%s on: %s-->\n'%(
+ __title__,__version__,time.ctime())
+
+ ################################################### HTML markup functions
+
+ def _doHTMLStart(self):
+ # Start of html page
+ self.out.write('<!DOCTYPE html PUBLIC \
+"-//W3C//DTD HTML 4.01//EN">\n')
+ self.out.write('<html><head><title>%s</title>\n'%(self.title))
+ self.out.write(self._getDocumentCreatedBy())
+ self.out.write('<meta http-equiv="Content-Type" \
+content="text/html;charset=iso-8859-1">\n')
+ # Get background
+ self.out.write('</head><body bgcolor="%s">\n'%self._getPageColor())
+ self._doPageHeader()
+ self.out.write('<pre>')
+
+ def _getHTMLStyles(self, toktype, toktext):
+ # Get styles
+ tags, color = self.colors.get(toktype, self.colors[NAME])[:2]#
+ tagstart=[]
+ tagend=[]
+ # check for styles and set them if needed.
+ if 'b' in tags:#Bold
+ tagstart.append('<b>')
+ tagend.append('</b>')
+ if 'i' in tags:#Italics
+ tagstart.append('<i>')
+ tagend.append('</i>')
+ if 'u' in tags:#Underline
+ tagstart.append('<u>')
+ tagend.append('</u>')
+ # HTML tags should be paired like so : <b><i><u>Doh!</u></i></b>
+ tagend.reverse()
+ starttags="".join(tagstart)
+ endtags="".join(tagend)
+ return starttags,endtags,color
+
+ def _sendHTMLText(self, toktype, toktext):
+ numberlinks = self.numberlinks
+
+ # If it is an error, set a red box around the bad tokens
+ # older browsers should ignore it
+ if toktype == ERRORTOKEN:
+ style = ' style="border: solid 1.5pt #FF0000;"'
+ else:
+ style = ''
+ # Get styles
+ starttag, endtag, color = self._getHTMLStyles(toktype, toktext)
+ # This is a hack to 'fix' multi-line strings.
+ # Multi-line strings are treated as only one token
+ # even though they can be several physical lines.
+ # That makes it hard to spot the start of a line,
+ # because at this level all we know about are tokens.
+
+ if toktext.count(self.LINENUMHOLDER):
+ # rip apart the string and separate it by line.
+ # count lines and change all linenum token to line numbers.
+ # embedded all the new font tags inside the current one.
+ # Do this by ending the tag first then writing our new tags,
+ # then starting another font tag exactly like the first one.
+ if toktype == LINENUMBER:
+ splittext = toktext.split(self.LINENUMHOLDER)
+ else:
+ splittext = toktext.split(self.LINENUMHOLDER+' ')
+ store = []
+ store.append(splittext.pop(0))
+ lstarttag, lendtag, lcolor = self._getHTMLStyles(LINENUMBER, toktext)
+ count = len(splittext)
+ for item in splittext:
+ num = self._getLineNumber()
+ if numberlinks:
+ numstrip = num.strip()
+ content = '<a name="%s" href="#%s">%s</a>' \
+ %(numstrip,numstrip,num)
+ else:
+ content = num
+ if count <= 1:
+ endtag,starttag = '',''
+ linenumber = ''.join([endtag,'<font color=', lcolor, '>',
+ lstarttag, content, lendtag, '</font>' ,starttag])
+ store.append(linenumber+item)
+ toktext = ''.join(store)
+ # send text
+ ## Output optimization
+ # skip font tag if black text, but styles will still be sent. (b,u,i)
+ if color !='#000000':
+ startfont = '<font color="%s"%s>'%(color, style)
+ endfont = '</font>'
+ else:
+ startfont, endfont = ('','')
+ if toktype != LINENUMBER:
+ self.out.write(''.join([startfont,starttag,
+ toktext,endtag,endfont]))
+ else:
+ self.out.write(toktext)
+ return
+
+ def _doHTMLHeader(self):
+ # Optional
+ if self.header != '':
+ self.out.write('%s\n'%self.header)
+ else:
+ color = self._getForeColor(NAME)
+ self.out.write('<b><font color="%s"># %s \
+ <br># %s</font></b><hr>\n'%
+ (color, self.title, time.ctime()))
+
+ def _doHTMLFooter(self):
+ # Optional
+ if self.footer != '':
+ self.out.write('%s\n'%self.footer)
+ else:
+ color = self._getForeColor(NAME)
+ self.out.write('<b><font color="%s"> \
+ <hr># %s<br># %s</font></b>\n'%
+ (color, self.title, time.ctime()))
+
+ def _doHTMLEnd(self):
+ # End of html page
+ self.out.write('</pre>\n')
+ # Write a little info at the bottom
+ self._doPageFooter()
+ self.out.write('</body></html>\n')
+
+ #################################################### CSS markup functions
+
+ def _getCSSStyle(self, key):
+ # Get the tags and colors from the dictionary
+ tags, forecolor, backcolor = self._getStyle(key)
+ style=[]
+ border = None
+ bordercolor = None
+ tags = tags.lower()
+ if tags:
+ # get the border color if specified
+ # the border color will be appended to
+ # the list after we define a border
+ if '#' in tags:# border color
+ start = tags.find('#')
+ end = start + 7
+ bordercolor = tags[start:end]
+ tags.replace(bordercolor,'',1)
+ # text styles
+ if 'b' in tags:# Bold
+ style.append('font-weight:bold;')
+ else:
+ style.append('font-weight:normal;')
+ if 'i' in tags:# Italic
+ style.append('font-style:italic;')
+ if 'u' in tags:# Underline
+ style.append('text-decoration:underline;')
+ # border size
+ if 'l' in tags:# thick border
+ size='thick'
+ elif 'm' in tags:# medium border
+ size='medium'
+ elif 't' in tags:# thin border
+ size='thin'
+ else:# default
+ size='medium'
+ # border styles
+ if 'n' in tags:# inset border
+ border='inset'
+ elif 'o' in tags:# outset border
+ border='outset'
+ elif 'r' in tags:# ridge border
+ border='ridge'
+ elif 'g' in tags:# groove border
+ border='groove'
+ elif '=' in tags:# double border
+ border='double'
+ elif '.' in tags:# dotted border
+ border='dotted'
+ elif '-' in tags:# dashed border
+ border='dashed'
+ elif 's' in tags:# solid border
+ border='solid'
+ # border type check
+ seperate_sides=0
+ for side in ['<','>','^','v']:
+ if side in tags:
+ seperate_sides+=1
+ # border box or seperate sides
+ if seperate_sides==0 and border:
+ style.append('border: %s %s;'%(border,size))
+ else:
+ if border == None:
+ border = 'solid'
+ if 'v' in tags:# bottom border
+ style.append('border-bottom:%s %s;'%(border,size))
+ if '<' in tags:# left border
+ style.append('border-left:%s %s;'%(border,size))
+ if '>' in tags:# right border
+ style.append('border-right:%s %s;'%(border,size))
+ if '^' in tags:# top border
+ style.append('border-top:%s %s;'%(border,size))
+ else:
+ style.append('font-weight:normal;')# css inherited style fix
+ # we have to define our borders before we set colors
+ if bordercolor:
+ style.append('border-color:%s;'%bordercolor)
+ # text forecolor
+ style.append('color:%s;'% forecolor)
+ # text backcolor
+ if backcolor:
+ style.append('background-color:%s;'%backcolor)
+ return (self._getMarkupClass(key),' '.join(style))
+
+ def _sendCSSStyle(self, external=0):
+ """ create external and internal style sheets"""
+ styles = []
+ external += self.external
+ if not external:
+ styles.append('<style type="text/css">\n<!--\n')
+ # Get page background color and write styles ignore any we don't know
+ styles.append('body { background:%s; }\n'%self._getPageColor())
+ # write out the various css styles
+ for key in MARKUPDICT:
+ styles.append('.%s { %s }\n'%self._getCSSStyle(key))
+ # If you want to style the pre tag you must modify the color dict.
+ # Example:
+ # lite[PY] = .py {border: solid thin #000000;background:#555555}\n'''
+ styles.append(self.colors.get(PY, '.py { }\n'))
+ # Extra css can be added here
+ # add CSSHOOK to the color dict if you need it.
+ # Example:
+ #lite[CSSHOOK] = """.mytag { border: solid thin #000000; } \n
+ # .myothertag { font-weight:bold; )\n"""
+ styles.append(self.colors.get(CSSHOOK,''))
+ if not self.external:
+ styles.append('--></style>\n')
+ return ''.join(styles)
+
+ def _doCSSStart(self):
+ # Start of css/html 4.01 page
+ self.out.write('<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">\n')
+ self.out.write('<html><head><title>%s</title>\n'%(self.title))
+ self.out.write(self._getDocumentCreatedBy())
+ self.out.write('<meta http-equiv="Content-Type" \
+content="text/html;charset=iso-8859-1">\n')
+ self._doCSSStyleSheet()
+ self.out.write('</head>\n<body>\n')
+ # Write a little info at the top.
+ self._doPageHeader()
+ self.out.write(self.colors.get(CODESTART,'<pre class="py">\n'))
+ return
+
+ def _doCSSStyleSheet(self):
+ if not self.external:
+ # write an embedded style sheet
+ self.out.write(self._sendCSSStyle())
+ else:
+ # write a link to an external style sheet
+ self.out.write('<link rel="stylesheet" \
+href="pystyle.css" type="text/css">')
+ return
+
+ def _sendCSSText(self, toktype, toktext):
+ # This is a hack to 'fix' multi-line strings.
+ # Multi-line strings are treated as only one token
+ # even though they can be several physical lines.
+ # That makes it hard to spot the start of a line,
+ # because at this level all we know about are tokens.
+ markupclass = MARKUPDICT.get(toktype, MARKUPDICT[NAME])
+ # if it is a LINENUMBER type then we can skip the rest
+ if toktext == self.LINESTART and toktype == LINENUMBER:
+ self.out.write('<span class="py_line">')
+ return
+ if toktext.count(self.LINENUMHOLDER):
+ # rip apart the string and separate it by line
+ # count lines and change all linenum token to line numbers
+ # also convert linestart and lineend tokens
+ # <linestart> <lnumstart> lnum <lnumend> text <lineend>
+ #################################################
+ newmarkup = MARKUPDICT.get(LINENUMBER, MARKUPDICT[NAME])
+ lstartspan = '<span class="%s">'%(newmarkup)
+ if toktype == LINENUMBER:
+ splittext = toktext.split(self.LINENUMHOLDER)
+ else:
+ splittext = toktext.split(self.LINENUMHOLDER+' ')
+ store = []
+ # we have already seen the first linenumber token
+ # so we can skip the first one
+ store.append(splittext.pop(0))
+ for item in splittext:
+ num = self._getLineNumber()
+ if self.numberlinks:
+ numstrip = num.strip()
+ content= '<a name="%s" href="#%s">%s</a>' \
+ %(numstrip,numstrip,num)
+ else:
+ content = num
+ linenumber= ''.join([lstartspan,content,'</span>'])
+ store.append(linenumber+item)
+ toktext = ''.join(store)
+ if toktext.count(self.LINESTART):
+ # wraps the textline in a line span
+ # this adds a lot of kludges, is it really worth it?
+ store = []
+ parts = toktext.split(self.LINESTART+' ')
+ # handle the first part differently
+ # the whole token gets wraqpped in a span later on
+ first = parts.pop(0)
+ # place spans before the newline
+ pos = first.rfind('\n')
+ if pos != -1:
+ first=first[:pos]+'</span></span>'+first[pos:]
+ store.append(first)
+ #process the rest of the string
+ for item in parts:
+ #handle line numbers if present
+ if self.dolinenums:
+ item = item.replace('</span>',
+ '</span><span class="%s">'%(markupclass))
+ else:
+ item = '<span class="%s">%s'%(markupclass,item)
+ # add endings for line and string tokens
+ pos = item.rfind('\n')
+ if pos != -1:
+ item=item[:pos]+'</span></span>\n'
+ store.append(item)
+ # add start tags for lines
+ toktext = '<span class="py_line">'.join(store)
+ # Send text
+ if toktype != LINENUMBER:
+ if toktype == TEXT and self.textFlag == 'DIV':
+ startspan = '<div class="%s">'%(markupclass)
+ endspan = '</div>'
+ elif toktype == TEXT and self.textFlag == 'RAW':
+ startspan,endspan = ('','')
+ else:
+ startspan = '<span class="%s">'%(markupclass)
+ endspan = '</span>'
+ self.out.write(''.join([startspan, toktext, endspan]))
+ else:
+ self.out.write(toktext)
+ return
+
+ def _doCSSHeader(self):
+ if self.header != '':
+ self.out.write('%s\n'%self.header)
+ else:
+ name = MARKUPDICT.get(NAME)
+ self.out.write('<div class="%s"># %s <br> \
+# %s</div><hr>\n'%(name, self.title, time.ctime()))
+
+ def _doCSSFooter(self):
+ # Optional
+ if self.footer != '':
+ self.out.write('%s\n'%self.footer)
+ else:
+ self.out.write('<hr><div class="%s"># %s <br> \
+# %s</div>\n'%(MARKUPDICT.get(NAME),self.title, time.ctime()))
+
+ def _doCSSEnd(self):
+ # End of css/html page
+ self.out.write(self.colors.get(CODEEND,'</pre>\n'))
+ # Write a little info at the bottom
+ self._doPageFooter()
+ self.out.write('</body></html>\n')
+ return
+
+ ################################################## XHTML markup functions
+
+ def _doXHTMLStart(self):
+ # XHTML is really just XML + HTML 4.01.
+ # We only need to change the page headers,
+ # and a few tags to get valid XHTML.
+ # Start of xhtml page
+ self.out.write('<?xml version="1.0"?>\n \
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"\n \
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n \
+<html xmlns="http://www.w3.org/1999/xhtml">\n')
+ self.out.write('<head><title>%s</title>\n'%(self.title))
+ self.out.write(self._getDocumentCreatedBy())
+ self.out.write('<meta http-equiv="Content-Type" \
+content="text/html;charset=iso-8859-1"/>\n')
+ self._doXHTMLStyleSheet()
+ self.out.write('</head>\n<body>\n')
+ # Write a little info at the top.
+ self._doPageHeader()
+ self.out.write(self.colors.get(CODESTART,'<pre class="py">\n'))
+ return
+
+ def _doXHTMLStyleSheet(self):
+ if not self.external:
+ # write an embedded style sheet
+ self.out.write(self._sendCSSStyle())
+ else:
+ # write a link to an external style sheet
+ self.out.write('<link rel="stylesheet" \
+href="pystyle.css" type="text/css"/>\n')
+ return
+
+ def _sendXHTMLText(self, toktype, toktext):
+ self._sendCSSText(toktype, toktext)
+
+ def _doXHTMLHeader(self):
+ # Optional
+ if self.header:
+ self.out.write('%s\n'%self.header)
+ else:
+ name = MARKUPDICT.get(NAME)
+ self.out.write('<div class="%s"># %s <br/> \
+# %s</div><hr/>\n '%(
+ name, self.title, time.ctime()))
+
+ def _doXHTMLFooter(self):
+ # Optional
+ if self.footer:
+ self.out.write('%s\n'%self.footer)
+ else:
+ self.out.write('<hr/><div class="%s"># %s <br/> \
+# %s</div>\n'%(MARKUPDICT.get(NAME), self.title, time.ctime()))
+
+ def _doXHTMLEnd(self):
+ self._doCSSEnd()
+
+#############################################################################
+
+if __name__ == '__main__':
+ cli()
+
+#############################################################################
+# PySourceColor.py
+# 2004, 2005 M.E.Farmer Jr.
+# Python license
diff --git a/paste/util/datetimeutil.py b/paste/util/datetimeutil.py
index c19e001..87474d7 100644
--- a/paste/util/datetimeutil.py
+++ b/paste/util/datetimeutil.py
@@ -1,361 +1,361 @@
-# (c) 2005 Clark C. Evans and contributors
-# This module is part of the Python Paste Project and is released under
-# the MIT License: http://www.opensource.org/licenses/mit-license.php
-# Some of this code was funded by: http://prometheusresearch.com
-"""
-Date, Time, and Timespan Parsing Utilities
-
-This module contains parsing support to create "human friendly"
-``datetime`` object parsing. The explicit goal of these routines is
-to provide a multi-format date/time support not unlike that found in
-Microsoft Excel. In most approaches, the input is very "strict" to
-prevent errors -- however, this approach is much more liberal since we
-are assuming the user-interface is parroting back the normalized value
-and thus the user has immediate feedback if the data is not typed in
-correctly.
-
- ``parse_date`` and ``normalize_date``
-
- These functions take a value like '9 jan 2007' and returns either an
- ``date`` object, or an ISO 8601 formatted date value such
- as '2007-01-09'. There is an option to provide an Oracle database
- style output as well, ``09 JAN 2007``, but this is not the default.
-
- This module always treats '/' delimiters as using US date order
- (since the author's clients are US based), hence '1/9/2007' is
- January 9th. Since this module treats the '-' as following
- European order this supports both modes of data-entry; together
- with immediate parroting back the result to the screen, the author
- has found this approach to work well in pratice.
-
- ``parse_time`` and ``normalize_time``
-
- These functions take a value like '1 pm' and returns either an
- ``time`` object, or an ISO 8601 formatted 24h clock time
- such as '13:00'. There is an option to provide for US style time
- values, '1:00 PM', however this is not the default.
-
- ``parse_datetime`` and ``normalize_datetime``
-
- These functions take a value like '9 jan 2007 at 1 pm' and returns
- either an ``datetime`` object, or an ISO 8601 formatted
- return (without the T) such as '2007-01-09 13:00'. There is an
- option to provide for Oracle / US style, '09 JAN 2007 @ 1:00 PM',
- however this is not the default.
-
- ``parse_delta`` and ``normalize_delta``
-
- These functions take a value like '1h 15m' and returns either an
- ``timedelta`` object, or an 2-decimal fixed-point
- numerical value in hours, such as '1.25'. The rationale is to
- support meeting or time-billing lengths, not to be an accurate
- representation in mili-seconds. As such not all valid
- ``timedelta`` values will have a normalized representation.
-
-"""
-from datetime import timedelta, time, date
-from time import localtime
-import string
-
-__all__ = ['parse_timedelta', 'normalize_timedelta',
- 'parse_time', 'normalize_time',
- 'parse_date', 'normalize_date']
-
-def _number(val):
- try:
- return string.atoi(val)
- except:
- return None
-
-#
-# timedelta
-#
-def parse_timedelta(val):
- """
- returns a ``timedelta`` object, or None
- """
- if not val:
- return None
- val = val.lower()
- if "." in val:
- val = float(val)
- return timedelta(hours=int(val), minutes=60*(val % 1.0))
- fHour = ("h" in val or ":" in val)
- fMin = ("m" in val or ":" in val)
- fFraction = "." in val
- for noise in "minu:teshour()":
- val = val.replace(noise, ' ')
- val = val.strip()
- val = val.split()
- hr = 0.0
- mi = 0
- val.reverse()
- if fHour:
- hr = int(val.pop())
- if fMin:
- mi = int(val.pop())
- if len(val) > 0 and not hr:
- hr = int(val.pop())
- return timedelta(hours=hr, minutes=mi)
-
-def normalize_timedelta(val):
- """
- produces a normalized string value of the timedelta
-
- This module returns a normalized time span value consisting of the
- number of hours in fractional form. For example '1h 15min' is
- formatted as 01.25.
- """
- if type(val) == str:
- val = parse_timedelta(val)
- if not val:
- return ''
- hr = val.seconds/3600
- mn = (val.seconds % 3600)/60
- return "%d.%02d" % (hr, mn * 100/60)
-
-#
-# time
-#
-def parse_time(val):
- if not val:
- return None
- hr = mi = 0
- val = val.lower()
- amflag = (-1 != val.find('a')) # set if AM is found
- pmflag = (-1 != val.find('p')) # set if PM is found
- for noise in ":amp.":
- val = val.replace(noise, ' ')
- val = val.split()
- if len(val) > 1:
- hr = int(val[0])
- mi = int(val[1])
- else:
- val = val[0]
- if len(val) < 1:
- pass
- elif 'now' == val:
- tm = localtime()
- hr = tm[3]
- mi = tm[4]
- elif 'noon' == val:
- hr = 12
- elif len(val) < 3:
- hr = int(val)
- if not amflag and not pmflag and hr < 7:
- hr += 12
- elif len(val) < 5:
- hr = int(val[:-2])
- mi = int(val[-2:])
- else:
- hr = int(val[:1])
- if amflag and hr >= 12:
- hr = hr - 12
- if pmflag and hr < 12:
- hr = hr + 12
- return time(hr, mi)
-
-def normalize_time(value, ampm):
- if not value:
- return ''
- if type(value) == str:
- value = parse_time(value)
- if not ampm:
- return "%02d:%02d" % (value.hour, value.minute)
- hr = value.hour
- am = "AM"
- if hr < 1 or hr > 23:
- hr = 12
- elif hr >= 12:
- am = "PM"
- if hr > 12:
- hr = hr - 12
- return "%02d:%02d %s" % (hr, value.minute, am)
-
-#
-# Date Processing
-#
-
-_one_day = timedelta(days=1)
-
-_str2num = {'jan':1, 'feb':2, 'mar':3, 'apr':4, 'may':5, 'jun':6,
- 'jul':7, 'aug':8, 'sep':9, 'oct':10, 'nov':11, 'dec':12 }
-
-def _month(val):
- for (key, mon) in _str2num.items():
- if key in val:
- return mon
- raise TypeError("unknown month '%s'" % val)
-
-_days_in_month = {1: 31, 2: 28, 3: 31, 4: 30, 5: 31, 6: 30,
- 7: 31, 8: 31, 9: 30, 10: 31, 11: 30, 12: 31,
- }
-_num2str = {1: 'Jan', 2: 'Feb', 3: 'Mar', 4: 'Apr', 5: 'May', 6: 'Jun',
- 7: 'Jul', 8: 'Aug', 9: 'Sep', 10: 'Oct', 11: 'Nov', 12: 'Dec',
- }
-_wkdy = ("mon", "tue", "wed", "thu", "fri", "sat", "sun")
-
-def parse_date(val):
- if not(val):
- return None
- val = val.lower()
- now = None
-
- # optimized check for YYYY-MM-DD
- strict = val.split("-")
- if len(strict) == 3:
- (y, m, d) = strict
- if "+" in d:
- d = d.split("+")[0]
- if " " in d:
- d = d.split(" ")[0]
- try:
- now = date(int(y), int(m), int(d))
- val = "xxx" + val[10:]
- except ValueError:
- pass
-
- # allow for 'now', 'mon', 'tue', etc.
- if not now:
- chk = val[:3]
- if chk in ('now','tod'):
- now = date.today()
- elif chk in _wkdy:
- now = date.today()
- idx = list(_wkdy).index(chk) + 1
- while now.isoweekday() != idx:
- now += _one_day
-
- # allow dates to be modified via + or - /w number of days, so
- # that now+3 is three days from now
- if now:
- tail = val[3:].strip()
- tail = tail.replace("+"," +").replace("-"," -")
- for item in tail.split():
- try:
- days = int(item)
- except ValueError:
- pass
- else:
- now += timedelta(days=days)
- return now
-
- # ok, standard parsing
- yr = mo = dy = None
- for noise in ('/', '-', ',', '*'):
- val = val.replace(noise, ' ')
- for noise in _wkdy:
- val = val.replace(noise, ' ')
- out = []
- last = False
- ldig = False
- for ch in val:
- if ch.isdigit():
- if last and not ldig:
- out.append(' ')
- last = ldig = True
- else:
- if ldig:
- out.append(' ')
- ldig = False
- last = True
- out.append(ch)
- val = "".join(out).split()
- if 3 == len(val):
- a = _number(val[0])
- b = _number(val[1])
- c = _number(val[2])
- if len(val[0]) == 4:
- yr = a
- if b: # 1999 6 23
- mo = b
- dy = c
- else: # 1999 Jun 23
- mo = _month(val[1])
- dy = c
- elif a > 0:
- yr = c
- if len(val[2]) < 4:
- raise TypeError("four digit year required")
- if b: # 6 23 1999
- dy = b
- mo = a
- else: # 23 Jun 1999
- dy = a
- mo = _month(val[1])
- else: # Jun 23, 2000
- dy = b
- yr = c
- if len(val[2]) < 4:
- raise TypeError("four digit year required")
- mo = _month(val[0])
- elif 2 == len(val):
- a = _number(val[0])
- b = _number(val[1])
- if a > 999:
- yr = a
- dy = 1
- if b > 0: # 1999 6
- mo = b
- else: # 1999 Jun
- mo = _month(val[1])
- elif a > 0:
- if b > 999: # 6 1999
- mo = a
- yr = b
- dy = 1
- elif b > 0: # 6 23
- mo = a
- dy = b
- else: # 23 Jun
- dy = a
- mo = _month(val[1])
- else:
- if b > 999: # Jun 2001
- yr = b
- dy = 1
- else: # Jun 23
- dy = b
- mo = _month(val[0])
- elif 1 == len(val):
- val = val[0]
- if not val.isdigit():
- mo = _month(val)
- if mo is not None:
- dy = 1
- else:
- v = _number(val)
- val = str(v)
- if 8 == len(val): # 20010623
- yr = _number(val[:4])
- mo = _number(val[4:6])
- dy = _number(val[6:])
- elif len(val) in (3,4):
- if v > 1300: # 2004
- yr = v
- mo = 1
- dy = 1
- else: # 1202
- mo = _number(val[:-2])
- dy = _number(val[-2:])
- elif v < 32:
- dy = v
- else:
- raise TypeError("four digit year required")
- tm = localtime()
- if mo is None:
- mo = tm[1]
- if dy is None:
- dy = tm[2]
- if yr is None:
- yr = tm[0]
- return date(yr, mo, dy)
-
-def normalize_date(val, iso8601=True):
- if not val:
- return ''
- if type(val) == str:
- val = parse_date(val)
- if iso8601:
- return "%4d-%02d-%02d" % (val.year, val.month, val.day)
- return "%02d %s %4d" % (val.day, _num2str[val.month], val.year)
+# (c) 2005 Clark C. Evans and contributors
+# This module is part of the Python Paste Project and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+# Some of this code was funded by: http://prometheusresearch.com
+"""
+Date, Time, and Timespan Parsing Utilities
+
+This module contains parsing support to create "human friendly"
+``datetime`` object parsing. The explicit goal of these routines is
+to provide a multi-format date/time support not unlike that found in
+Microsoft Excel. In most approaches, the input is very "strict" to
+prevent errors -- however, this approach is much more liberal since we
+are assuming the user-interface is parroting back the normalized value
+and thus the user has immediate feedback if the data is not typed in
+correctly.
+
+ ``parse_date`` and ``normalize_date``
+
+ These functions take a value like '9 jan 2007' and returns either an
+ ``date`` object, or an ISO 8601 formatted date value such
+ as '2007-01-09'. There is an option to provide an Oracle database
+ style output as well, ``09 JAN 2007``, but this is not the default.
+
+ This module always treats '/' delimiters as using US date order
+ (since the author's clients are US based), hence '1/9/2007' is
+ January 9th. Since this module treats the '-' as following
+ European order this supports both modes of data-entry; together
+ with immediate parroting back the result to the screen, the author
+ has found this approach to work well in pratice.
+
+ ``parse_time`` and ``normalize_time``
+
+ These functions take a value like '1 pm' and returns either an
+ ``time`` object, or an ISO 8601 formatted 24h clock time
+ such as '13:00'. There is an option to provide for US style time
+ values, '1:00 PM', however this is not the default.
+
+ ``parse_datetime`` and ``normalize_datetime``
+
+ These functions take a value like '9 jan 2007 at 1 pm' and returns
+ either an ``datetime`` object, or an ISO 8601 formatted
+ return (without the T) such as '2007-01-09 13:00'. There is an
+ option to provide for Oracle / US style, '09 JAN 2007 @ 1:00 PM',
+ however this is not the default.
+
+ ``parse_delta`` and ``normalize_delta``
+
+ These functions take a value like '1h 15m' and returns either an
+ ``timedelta`` object, or an 2-decimal fixed-point
+ numerical value in hours, such as '1.25'. The rationale is to
+ support meeting or time-billing lengths, not to be an accurate
+ representation in mili-seconds. As such not all valid
+ ``timedelta`` values will have a normalized representation.
+
+"""
+from datetime import timedelta, time, date
+from time import localtime
+import string
+
+__all__ = ['parse_timedelta', 'normalize_timedelta',
+ 'parse_time', 'normalize_time',
+ 'parse_date', 'normalize_date']
+
+def _number(val):
+ try:
+ return string.atoi(val)
+ except:
+ return None
+
+#
+# timedelta
+#
+def parse_timedelta(val):
+ """
+ returns a ``timedelta`` object, or None
+ """
+ if not val:
+ return None
+ val = val.lower()
+ if "." in val:
+ val = float(val)
+ return timedelta(hours=int(val), minutes=60*(val % 1.0))
+ fHour = ("h" in val or ":" in val)
+ fMin = ("m" in val or ":" in val)
+ fFraction = "." in val
+ for noise in "minu:teshour()":
+ val = val.replace(noise, ' ')
+ val = val.strip()
+ val = val.split()
+ hr = 0.0
+ mi = 0
+ val.reverse()
+ if fHour:
+ hr = int(val.pop())
+ if fMin:
+ mi = int(val.pop())
+ if len(val) > 0 and not hr:
+ hr = int(val.pop())
+ return timedelta(hours=hr, minutes=mi)
+
+def normalize_timedelta(val):
+ """
+ produces a normalized string value of the timedelta
+
+ This module returns a normalized time span value consisting of the
+ number of hours in fractional form. For example '1h 15min' is
+ formatted as 01.25.
+ """
+ if type(val) == str:
+ val = parse_timedelta(val)
+ if not val:
+ return ''
+ hr = val.seconds/3600
+ mn = (val.seconds % 3600)/60
+ return "%d.%02d" % (hr, mn * 100/60)
+
+#
+# time
+#
+def parse_time(val):
+ if not val:
+ return None
+ hr = mi = 0
+ val = val.lower()
+ amflag = (-1 != val.find('a')) # set if AM is found
+ pmflag = (-1 != val.find('p')) # set if PM is found
+ for noise in ":amp.":
+ val = val.replace(noise, ' ')
+ val = val.split()
+ if len(val) > 1:
+ hr = int(val[0])
+ mi = int(val[1])
+ else:
+ val = val[0]
+ if len(val) < 1:
+ pass
+ elif 'now' == val:
+ tm = localtime()
+ hr = tm[3]
+ mi = tm[4]
+ elif 'noon' == val:
+ hr = 12
+ elif len(val) < 3:
+ hr = int(val)
+ if not amflag and not pmflag and hr < 7:
+ hr += 12
+ elif len(val) < 5:
+ hr = int(val[:-2])
+ mi = int(val[-2:])
+ else:
+ hr = int(val[:1])
+ if amflag and hr >= 12:
+ hr = hr - 12
+ if pmflag and hr < 12:
+ hr = hr + 12
+ return time(hr, mi)
+
+def normalize_time(value, ampm):
+ if not value:
+ return ''
+ if type(value) == str:
+ value = parse_time(value)
+ if not ampm:
+ return "%02d:%02d" % (value.hour, value.minute)
+ hr = value.hour
+ am = "AM"
+ if hr < 1 or hr > 23:
+ hr = 12
+ elif hr >= 12:
+ am = "PM"
+ if hr > 12:
+ hr = hr - 12
+ return "%02d:%02d %s" % (hr, value.minute, am)
+
+#
+# Date Processing
+#
+
+_one_day = timedelta(days=1)
+
+_str2num = {'jan':1, 'feb':2, 'mar':3, 'apr':4, 'may':5, 'jun':6,
+ 'jul':7, 'aug':8, 'sep':9, 'oct':10, 'nov':11, 'dec':12 }
+
+def _month(val):
+ for (key, mon) in _str2num.items():
+ if key in val:
+ return mon
+ raise TypeError("unknown month '%s'" % val)
+
+_days_in_month = {1: 31, 2: 28, 3: 31, 4: 30, 5: 31, 6: 30,
+ 7: 31, 8: 31, 9: 30, 10: 31, 11: 30, 12: 31,
+ }
+_num2str = {1: 'Jan', 2: 'Feb', 3: 'Mar', 4: 'Apr', 5: 'May', 6: 'Jun',
+ 7: 'Jul', 8: 'Aug', 9: 'Sep', 10: 'Oct', 11: 'Nov', 12: 'Dec',
+ }
+_wkdy = ("mon", "tue", "wed", "thu", "fri", "sat", "sun")
+
+def parse_date(val):
+ if not(val):
+ return None
+ val = val.lower()
+ now = None
+
+ # optimized check for YYYY-MM-DD
+ strict = val.split("-")
+ if len(strict) == 3:
+ (y, m, d) = strict
+ if "+" in d:
+ d = d.split("+")[0]
+ if " " in d:
+ d = d.split(" ")[0]
+ try:
+ now = date(int(y), int(m), int(d))
+ val = "xxx" + val[10:]
+ except ValueError:
+ pass
+
+ # allow for 'now', 'mon', 'tue', etc.
+ if not now:
+ chk = val[:3]
+ if chk in ('now','tod'):
+ now = date.today()
+ elif chk in _wkdy:
+ now = date.today()
+ idx = list(_wkdy).index(chk) + 1
+ while now.isoweekday() != idx:
+ now += _one_day
+
+ # allow dates to be modified via + or - /w number of days, so
+ # that now+3 is three days from now
+ if now:
+ tail = val[3:].strip()
+ tail = tail.replace("+"," +").replace("-"," -")
+ for item in tail.split():
+ try:
+ days = int(item)
+ except ValueError:
+ pass
+ else:
+ now += timedelta(days=days)
+ return now
+
+ # ok, standard parsing
+ yr = mo = dy = None
+ for noise in ('/', '-', ',', '*'):
+ val = val.replace(noise, ' ')
+ for noise in _wkdy:
+ val = val.replace(noise, ' ')
+ out = []
+ last = False
+ ldig = False
+ for ch in val:
+ if ch.isdigit():
+ if last and not ldig:
+ out.append(' ')
+ last = ldig = True
+ else:
+ if ldig:
+ out.append(' ')
+ ldig = False
+ last = True
+ out.append(ch)
+ val = "".join(out).split()
+ if 3 == len(val):
+ a = _number(val[0])
+ b = _number(val[1])
+ c = _number(val[2])
+ if len(val[0]) == 4:
+ yr = a
+ if b: # 1999 6 23
+ mo = b
+ dy = c
+ else: # 1999 Jun 23
+ mo = _month(val[1])
+ dy = c
+ elif a > 0:
+ yr = c
+ if len(val[2]) < 4:
+ raise TypeError("four digit year required")
+ if b: # 6 23 1999
+ dy = b
+ mo = a
+ else: # 23 Jun 1999
+ dy = a
+ mo = _month(val[1])
+ else: # Jun 23, 2000
+ dy = b
+ yr = c
+ if len(val[2]) < 4:
+ raise TypeError("four digit year required")
+ mo = _month(val[0])
+ elif 2 == len(val):
+ a = _number(val[0])
+ b = _number(val[1])
+ if a > 999:
+ yr = a
+ dy = 1
+ if b > 0: # 1999 6
+ mo = b
+ else: # 1999 Jun
+ mo = _month(val[1])
+ elif a > 0:
+ if b > 999: # 6 1999
+ mo = a
+ yr = b
+ dy = 1
+ elif b > 0: # 6 23
+ mo = a
+ dy = b
+ else: # 23 Jun
+ dy = a
+ mo = _month(val[1])
+ else:
+ if b > 999: # Jun 2001
+ yr = b
+ dy = 1
+ else: # Jun 23
+ dy = b
+ mo = _month(val[0])
+ elif 1 == len(val):
+ val = val[0]
+ if not val.isdigit():
+ mo = _month(val)
+ if mo is not None:
+ dy = 1
+ else:
+ v = _number(val)
+ val = str(v)
+ if 8 == len(val): # 20010623
+ yr = _number(val[:4])
+ mo = _number(val[4:6])
+ dy = _number(val[6:])
+ elif len(val) in (3,4):
+ if v > 1300: # 2004
+ yr = v
+ mo = 1
+ dy = 1
+ else: # 1202
+ mo = _number(val[:-2])
+ dy = _number(val[-2:])
+ elif v < 32:
+ dy = v
+ else:
+ raise TypeError("four digit year required")
+ tm = localtime()
+ if mo is None:
+ mo = tm[1]
+ if dy is None:
+ dy = tm[2]
+ if yr is None:
+ yr = tm[0]
+ return date(yr, mo, dy)
+
+def normalize_date(val, iso8601=True):
+ if not val:
+ return ''
+ if type(val) == str:
+ val = parse_date(val)
+ if iso8601:
+ return "%4d-%02d-%02d" % (val.year, val.month, val.day)
+ return "%02d %s %4d" % (val.day, _num2str[val.month], val.year)