| Index: third_party/cython/src/Cython/Compiler/Annotate.py
|
| diff --git a/third_party/cython/src/Cython/Compiler/Annotate.py b/third_party/cython/src/Cython/Compiler/Annotate.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..4bbdeccfb3f81a1f6768f212ff67ed481bed2642
|
| --- /dev/null
|
| +++ b/third_party/cython/src/Cython/Compiler/Annotate.py
|
| @@ -0,0 +1,207 @@
|
| +# Note: Work in progress
|
| +
|
| +import os
|
| +import re
|
| +import codecs
|
| +from xml.sax.saxutils import escape as html_escape
|
| +from StringIO import StringIO
|
| +
|
| +import Version
|
| +from Code import CCodeWriter
|
| +from Cython import Utils
|
| +
|
| +# need one-characters subsitutions (for now) so offsets aren't off
|
| +special_chars = [
|
| + (u'&', u'\xF2', u'&'),
|
| + (u'<', u'\xF0', u'<'),
|
| + (u'>', u'\xF1', u'>'),
|
| +]
|
| +
|
| +
|
| +class AnnotationCCodeWriter(CCodeWriter):
|
| +
|
| + def __init__(self, create_from=None, buffer=None, copy_formatting=True):
|
| + CCodeWriter.__init__(self, create_from, buffer, copy_formatting=True)
|
| + if create_from is None:
|
| + self.annotation_buffer = StringIO()
|
| + self.annotations = []
|
| + self.last_pos = None
|
| + self.code = {}
|
| + else:
|
| + # When creating an insertion point, keep references to the same database
|
| + self.annotation_buffer = create_from.annotation_buffer
|
| + self.annotations = create_from.annotations
|
| + self.code = create_from.code
|
| + self.last_pos = create_from.last_pos
|
| +
|
| + def create_new(self, create_from, buffer, copy_formatting):
|
| + return AnnotationCCodeWriter(create_from, buffer, copy_formatting)
|
| +
|
| + def write(self, s):
|
| + CCodeWriter.write(self, s)
|
| + self.annotation_buffer.write(s)
|
| +
|
| + def mark_pos(self, pos):
|
| + if pos is not None:
|
| + CCodeWriter.mark_pos(self, pos)
|
| + if self.last_pos:
|
| + pos_code = self.code.setdefault(self.last_pos[0].filename,{})
|
| + code = pos_code.get(self.last_pos[1], "")
|
| + pos_code[self.last_pos[1]] = code + self.annotation_buffer.getvalue()
|
| + self.annotation_buffer = StringIO()
|
| + self.last_pos = pos
|
| +
|
| + def annotate(self, pos, item):
|
| + self.annotations.append((pos, item))
|
| +
|
| + def save_annotation(self, source_filename, target_filename):
|
| + self.mark_pos(None)
|
| + f = Utils.open_source_file(source_filename)
|
| + lines = f.readlines()
|
| + for k, line in enumerate(lines):
|
| + for c, cc, html in special_chars:
|
| + line = line.replace(c, cc)
|
| + lines[k] = line
|
| + f.close()
|
| + all = []
|
| + if False:
|
| + for pos, item in self.annotations:
|
| + if pos[0].filename == source_filename:
|
| + start = item.start()
|
| + size, end = item.end()
|
| + if size:
|
| + all.append((pos, start))
|
| + all.append(((source_filename, pos[1], pos[2]+size), end))
|
| + else:
|
| + all.append((pos, start+end))
|
| +
|
| + all.sort(reverse=True)
|
| + for pos, item in all:
|
| + _, line_no, col = pos
|
| + line_no -= 1
|
| + col += 1
|
| + line = lines[line_no]
|
| + lines[line_no] = line[:col] + item + line[col:]
|
| +
|
| + html_filename = os.path.splitext(target_filename)[0] + ".html"
|
| + f = codecs.open(html_filename, "w", encoding="UTF-8")
|
| + f.write(u'<!DOCTYPE html>\n')
|
| + f.write(u'<!-- Generated by Cython %s -->\n' % Version.watermark)
|
| + f.write(u'<html>\n')
|
| + f.write(u"""
|
| +<head>
|
| +<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
| +<style type="text/css">
|
| +
|
| +body { font-family: courier; font-size: 12; }
|
| +
|
| +.code { font-size: 9; color: #444444; display: none; margin-left: 20px; }
|
| +.py_c_api { color: red; }
|
| +.py_macro_api { color: #FF7000; }
|
| +.pyx_c_api { color: #FF3000; }
|
| +.pyx_macro_api { color: #FF7000; }
|
| +.refnanny { color: #FFA000; }
|
| +
|
| +.error_goto { color: #FFA000; }
|
| +
|
| +.tag { }
|
| +
|
| +.coerce { color: #008000; border: 1px dotted #008000 }
|
| +
|
| +.py_attr { color: #FF0000; font-weight: bold; }
|
| +.c_attr { color: #0000FF; }
|
| +
|
| +.py_call { color: #FF0000; font-weight: bold; }
|
| +.c_call { color: #0000FF; }
|
| +
|
| +.line { margin: 0em }
|
| +
|
| +</style>
|
| +<script>
|
| +function toggleDiv(id) {
|
| + theDiv = document.getElementById(id);
|
| + if (theDiv.style.display != 'block') theDiv.style.display = 'block';
|
| + else theDiv.style.display = 'none';
|
| +}
|
| +</script>
|
| +</head>
|
| + """)
|
| + f.write(u'<body>\n')
|
| + f.write(u'<p>Generated by Cython %s\n' % Version.watermark)
|
| + c_file = Utils.decode_filename(os.path.basename(target_filename))
|
| + f.write(u'<p>Raw output: <a href="%s">%s</a>\n' % (c_file, c_file))
|
| +
|
| + zero_calls = dict((name, 0) for name in
|
| + 'refnanny py_macro_api py_c_api pyx_macro_api pyx_c_api error_goto'.split())
|
| +
|
| + def annotate(match):
|
| + group_name = match.lastgroup
|
| + calls[group_name] += 1
|
| + return ur"<span class='%s'>%s</span>" % (
|
| + group_name, match.group(group_name))
|
| +
|
| + pos_comment_marker = u'/* \N{HORIZONTAL ELLIPSIS} */\n'
|
| + k = 0
|
| + code_source_file = self.code.get(source_filename, {})
|
| + for line in lines:
|
| + k += 1
|
| + try:
|
| + code = code_source_file[k]
|
| + except KeyError:
|
| + code = ''
|
| + else:
|
| + code = _replace_pos_comment(pos_comment_marker, code)
|
| + if code.startswith(pos_comment_marker):
|
| + code = code[len(pos_comment_marker):]
|
| + code = html_escape(code)
|
| +
|
| + calls = zero_calls.copy()
|
| + code = _parse_code(annotate, code)
|
| + score = (5 * calls['py_c_api'] + 2 * calls['pyx_c_api'] +
|
| + calls['py_macro_api'] + calls['pyx_macro_api'])
|
| + color = u"FFFF%02x" % int(255/(1+score/10.0))
|
| + f.write(u"<pre class='line' style='background-color: #%s' onclick='toggleDiv(\"line%s\")'>" % (color, k))
|
| +
|
| + f.write(u" %d: " % k)
|
| + for c, cc, html in special_chars:
|
| + line = line.replace(cc, html)
|
| + f.write(line.rstrip())
|
| +
|
| + f.write(u'</pre>\n')
|
| + f.write(u"<pre id='line%s' class='code' style='background-color: #%s'>%s</pre>" % (k, color, code))
|
| + f.write(u'</body></html>\n')
|
| + f.close()
|
| +
|
| +
|
| +_parse_code = re.compile(
|
| + ur'(?P<refnanny>__Pyx_X?(?:GOT|GIVE)REF|__Pyx_RefNanny[A-Za-z]+)|'
|
| + ur'(?:'
|
| + ur'(?P<pyx_macro_api>__Pyx_[A-Z][A-Z_]+)|'
|
| + ur'(?P<pyx_c_api>__Pyx_[A-Z][a-z_][A-Za-z_]+)|'
|
| + ur'(?P<py_macro_api>Py[A-Z][a-z]+_[A-Z][A-Z_]+)|'
|
| + ur'(?P<py_c_api>Py[A-Z][a-z]+_[A-Z][a-z][A-Za-z_]+)'
|
| + ur')(?=\()|' # look-ahead to exclude subsequent '(' from replacement
|
| + ur'(?P<error_goto>(?:(?<=;) *if .* +)?\{__pyx_filename = .*goto __pyx_L\w+;\})'
|
| +).sub
|
| +
|
| +
|
| +_replace_pos_comment = re.compile(
|
| + # this matches what Cython generates as code line marker comment
|
| + ur'^\s*/\*(?:(?:[^*]|\*[^/])*\n)+\s*\*/\s*\n',
|
| + re.M
|
| +).sub
|
| +
|
| +
|
| +class AnnotationItem(object):
|
| +
|
| + def __init__(self, style, text, tag="", size=0):
|
| + self.style = style
|
| + self.text = text
|
| + self.tag = tag
|
| + self.size = size
|
| +
|
| + def start(self):
|
| + return u"<span class='tag %s' title='%s'>%s" % (self.style, self.text, self.tag)
|
| +
|
| + def end(self):
|
| + return self.size, u"</span>"
|
|
|