py2html.py
'''\
Python to HTML.
commands:
main - Main function (normal use)
demo - Demo function (show off and test app)
test - Test function (development)
usage:
module.dispatch({command}, args={arguments}) -- calling module from code
$PYTHON module_name {command} {arguments} -- executing module externally
Developer@Sonnack.com
April 2016
'''
from __future__ import print_function
from sys import argv, exc_info, stdout, stderr
from traceback import extract_tb
from os import path
from keyword import iskeyword
from htmlpage import htmlpage
from logger import logger, info, debug, trace
Log = logger('app')
BasePath = r'C:\CJS\prj\Python\app'
HtmlPath = r'C:\CJS\www\root\pub\python'
class TextSource (object):
def __init__ (self, txt):
self.src = txt
self.cp = 0
def rewind (self):
self.cp = 0
def get_ch (self):
if self.cp < len(self.src):
ch = self.src[self.cp]
self.cp += 1
return ch
return None
def unget (self):
if 0 < self.cp:
self.cp -= 1
class TextParser (object):
SpclWords = ['self', 'other', 'this', 'that']
SpcChr = ' '
TabChr = ' ' * 4
def __init__ (self):
self.src = None
self.buf = ''
def parse (self, txt, fp):
self.src = TextSource(txt)
while True:
ch = self.src.get_ch()
if not ch:
break
if ch == ' ':
fp.write(self.SpcChr)
continue
if ch == '\t':
fp.write(self.TabChr)
continue
if ch == '<':
fp.write('<')
continue
if ch == '>':
fp.write('>')
continue
if ch == '&':
fp.write('&')
continue
if ch == ':':
fp.write('<strong>:</strong>')
continue
if (ch == '"') or (ch == "'"):
self._quoted_string(ch, fp)
continue
if ch == '#':
self._line_comment(ch, fp)
continue
if ch.isdigit():
self._number(ch, fp)
continue
if ch.isalpha() or (ch == '_'):
self._keyword(ch, fp)
continue
fp.write(ch)
def _keyword (self, ch, fp):
self.buf = ch
ch = self.src.get_ch()
while ch and (ch.isalnum() or (ch == '_')):
self.buf += ch
ch = self.src.get_ch()
if ch:
self.src.unget()
if self.buf in self.SpclWords:
fp.write('<span class="Special">%s</span>' % self.buf)
return
if iskeyword(self.buf):
fp.write('<span class="Keyword">%s</span>' % self.buf)
return
fp.write('<span class="Userdef">%s</span>' % self.buf)
def _number (self, ch, fp):
self.buf = ch
ch = self.src.get_ch()
while ch and ch.isdigit():
self.buf += ch
ch = self.src.get_ch()
if ch:
self.src.unget()
fp.write('<span class="Number">%s</span>' % self.buf)
def _line_comment (self, ch, fp):
self.buf = ch
ch = self.src.get_ch()
while ch and (ch != '\n'):
self.buf += ch
ch = self.src.get_ch()
if ch:
self.src.unget()
fp.write('<span class="Comment">%s</span>' % self.buf)
def _quoted_string (self, ch, fp):
ch1 = ch
ch2 = self.src.get_ch()
if ch2 == ch1:
ch3 = self.src.get_ch()
if ch3 == ch2:
self._triple_quoted_text(ch, fp)
return
self.src.unget()
fp.write('<span class="String">%s%s</span>' % (ch1,ch2))
return
self.buf = ch1
self.buf += self._htmlize(ch2)
ch = self.src.get_ch()
while ch and (ch != ch1):
self.buf += self._htmlize(ch)
ch = self.src.get_ch()
if ch:
self.buf += ch
fp.write('<span class="String">%s</span>' % self.buf)
def _triple_quoted_text (self, ch, fp):
self.buf = ch1 = ch * 3
ch = self.src.get_ch()
while ch:
self.buf += self._htmlize(ch)
if self.buf[-3:] == ch1:
break
ch = self.src.get_ch()
fp.write('<span class="TripleQuote">%s</span>' % self.buf)
def _htmlize (self, ch):
if ch == '<': return '<'
if ch == '>': return '>'
if ch == '&': return '&'
return ch
class PythonPage (object):
def __init__ (self, data):
self.data = data
def writepage (self, fp):
fp.write('<pre style="background-image:url(/etc/bg_computer_paper.png)">')
tp = TextParser()
tp.parse(self.data, fp)
fp.write('</pre>\n')
def python_to_html (py_filename, html_filename):
Log.info('Python-2-HTML: py=%s' % py_filename)
Log.info('Python-2-HTML: html=%s' % html_filename)
bname = path.basename(py_filename)
if not py_filename.endswith('.py'):
Log.error('Invalid Extension: "%s"' % bname)
raise RuntimeError('Not a Python source file: "%s"' % py_filename)
fp = open(py_filename, 'r')
try:
txt = fp.read()
Log.info('read: %s (%d bytes)' % (bname, len(txt)))
except:
raise
finally:
fp.close()
css = ['/basic.css', '/pub/python/python.css']
pw = PythonPage(txt)
pg = htmlpage(bname, h2='Python Page', csspages=css)
pg.writefile(html_filename, pw)
Log.info('wrote: %s' % path.basename(html_filename))
return 'Done!'
def do_demo (*args):
Log.info('demo: %s' % str(args))
pname = path.join(BasePath, 'py2html.py')
hname = path.join(HtmlPath, 'py2html.html')
return python_to_html(pname, hname)
def do_test (*args):
Log.info('test: %s' % str(args))
pname = args[0] if 0 < len(args) else path.join(BasePath,'@.py')
hname = args[1] if 1 < len(args) else path.join(HtmlPath,'py2html.html')
return python_to_html(pname, hname)
def do_main (*args):
Log.info('main: %s' % str(args))
pname = args[0]
hname = args[1] if 1 < len(args) else path.join(HtmlName,'%s.html' % path.basename(pname))
return python_to_html(pname, hname)
def dispatch (cmd, *args):
Log.info('command: %s' % cmd)
Log.info('arguments: %d' % len(args))
if cmd == 'main': return do_main(*args)
if cmd == 'test': return do_test(*args)
if cmd == 'demo': return do_demo(*args)
return 'Nothing to do!'
if __name__ == '__main__':
print('autorun: %s' % argv[0])
Log.start(path.join(BasePath,'py2html.log'))
Log.level(info())
cmd = argv[1] if 1 < len(argv) else 'demo'
etc = argv[2:] if 2 < len(argv) else []
try:
obj = dispatch(cmd, *etc)
print(obj)
Log.info(obj)
except:
etype, evalue, tb = exc_info()
ts = extract_tb(tb)
Log.error('%s: %s' % (etype.__name__,str(evalue)))
for t in ts[-3:]:
Log.error('[%d] %s (%s)' % (t[1], t[2], t[0]))
Log.error(' %s' % t[3])
raise
finally:
Log.end()
'''eof'''