277 lines
7.6 KiB
Python
277 lines
7.6 KiB
Python
|
|
""" 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'
|
|
|