""" html - generating ad-hoc html out of source browser
"""
import py
from py.xml import html, raw
from compiler import ast
import time
from py.__.apigen.source.color import Tokenizer, PythonSchema
class HtmlEnchanter(object):
def __init__(self, mod):
self.mod = mod
self.create_caches()
def create_caches(self):
mod = self.mod
linecache = {}
for item in mod.get_children():
linecache[item.firstlineno] = item
self.linecache = linecache
def enchant_row(self, num, row):
# add some informations to row, like functions defined in that
# line, etc.
try:
item = self.linecache[num]
# XXX: this should not be assertion, rather check, but we want to
# know if stuff is working
pos = row.find(item.name)
assert pos != -1
end = len(item.name) + pos
chunk = html.a(row[pos:end], href="#" + item.listnames(),
name=item.listnames())
return [row[:pos], chunk, row[end:]]
except KeyError:
return [row] # no more info
def prepare_line(text, tokenizer, encoding):
""" adds html formatting to text items (list)
only processes items if they're of a string type (or unicode)
"""
ret = []
for item in text:
if type(item) in [str, unicode]:
tokens = tokenizer.tokenize(item)
for t in tokens:
if not isinstance(t.data, unicode):
data = unicode(t.data, encoding)
else:
data = t.data
if t.type in ['keyword', 'alt_keyword', 'number',
'string', 'comment']:
ret.append(html.span(data, class_=t.type))
else:
ret.append(data)
else:
ret.append(item)
return ret
class HTMLDocument(object):
def __init__(self, encoding, tokenizer=None):
self.encoding = encoding
self.html = root = html.html()
self.head = head = self.create_head()
root.append(head)
self.body = body = self.create_body()
root.append(body)
self.table, self.tbody = table, tbody = self.create_table()
body.append(table)
if tokenizer is None:
tokenizer = Tokenizer(PythonSchema)
self.tokenizer = tokenizer
def create_head(self):
return html.head(
html.title('source view'),
html.style("""
body, td {
background-color: #FFF;
color: black;
font-family: monospace, Monaco;
}
table, tr {
margin: 0px;
padding: 0px;
border-width: 0px;
}
a {
color: blue;
font-weight: bold;
text-decoration: none;
}
a:hover {
color: #005;
}
.lineno {
text-align: right;
color: #555;
width: 3em;
padding-right: 1em;
border: 0px solid black;
border-right-width: 1px;
}
.code {
padding-left: 1em;
white-space: pre;
}
.comment {
color: purple;
}
.string {
color: #777;
}
.keyword {
color: blue;
}
.alt_keyword {
color: green;
}
""", type='text/css'),
)
def create_body(self):
return html.body()
def create_table(self):
table = html.table(cellpadding='0', cellspacing='0')
tbody = html.tbody()
table.append(tbody)
return table, tbody
def add_row(self, lineno, text):
if text == ['']:
text = [raw(' ')]
else:
text = prepare_line(text, self.tokenizer, self.encoding)
self.tbody.append(html.tr(html.td(str(lineno), class_='lineno'),
html.td(class_='code', *text)))
def __unicode__(self):
# XXX don't like to use indent=0 here, but else py.xml's indentation
# messes up the html inside the table cells (which displays formatting)
return self.html.unicode(indent=0)
def create_html(mod):
# out is some kind of stream
#*[html.tr(html.td(i.name)) for i in mod.get_children()]
lines = mod.path.open().readlines()
enchanter = HtmlEnchanter(mod)
enc = get_module_encoding(mod.path)
doc = HTMLDocument(enc)
for i, row in enumerate(lines):
row = enchanter.enchant_row(i + 1, row)
doc.add_row(i + 1, row)
return unicode(doc)
style = html.style("""
body, p, td {
background-color: #FFF;
color: black;
font-family: monospace, Monaco;
}
td.type {
width: 2em;
}
td.name {
width: 30em;
}
td.mtime {
width: 13em;
}
td.size {
text-alignment: right;
}
""")
def create_dir_html(path, href_prefix=''):
h = html.html(
html.head(
html.title('directory listing of %s' % (path,)),
style,
),
)
body = html.body(
html.h1('directory listing of %s' % (path,)),
)
h.append(body)
table = html.table()
body.append(table)
tbody = html.tbody()
table.append(tbody)
items = list(path.listdir())
items.sort(key=lambda p: p.basename)
items.sort(key=lambda p: not p.check(dir=True))
for fpath in items:
tr = html.tr()
tbody.append(tr)
td1 = html.td(fpath.check(dir=True) and 'D' or 'F', class_='type')
tr.append(td1)
href = fpath.basename
if href_prefix:
href = '%s%s' % (href_prefix, href)
if fpath.check(dir=True):
href += '/'
td2 = html.td(html.a(fpath.basename, href=href), class_='name')
tr.append(td2)
td3 = html.td(time.strftime('%Y-%m-%d %H:%M:%S',
time.gmtime(fpath.mtime())), class_='mtime')
tr.append(td3)
if fpath.check(dir=True):
size = ''
unit = ''
else:
size = fpath.size()
unit = 'B'
for u in ['kB', 'MB', 'GB', 'TB']:
if size > 1024:
size = round(size / 1024.0, 2)
unit = u
td4 = html.td('%s %s' % (size, unit), class_='size')
tr.append(td4)
return unicode(h)
def create_unknown_html(path):
h = html.html(
html.head(
html.title('Can not display page'),
style,
),
html.body(
html.p('The data URL (%s) does not contain Python code.' % (path,))
),
)
return h.unicode()
_reg_enc = py.std.re.compile(r'coding[:=]\s*([-\w.]+)')
def get_module_encoding(path):
if hasattr(path, 'strpath'):
path = path.strpath
if path[-1] in ['c', 'o']:
path = path[:-1]
fpath = py.path.local(path)
fp = fpath.open()
lines = []
try:
# encoding is only allowed in the first two lines
for i in range(2):
lines.append(fp.readline())
finally:
fp.close()
match = _reg_enc.search('\n'.join(lines))
if match:
return match.group(1)
return 'ISO-8859-1'