""" 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'