1 # Note: Work in progress
6 from xml
.sax
.saxutils
import escape
as html_escape
7 from StringIO
import StringIO
10 from Code
import CCodeWriter
11 from Cython
import Utils
13 # need one-characters subsitutions (for now) so offsets aren't off
15 (u
'&', u
'\xF2', u
'&'),
16 (u
'<', u
'\xF0', u
'<'),
17 (u
'>', u
'\xF1', u
'>'),
21 class AnnotationCCodeWriter(CCodeWriter
):
23 def __init__(self
, create_from
=None, buffer=None, copy_formatting
=True):
24 CCodeWriter
.__init
__(self
, create_from
, buffer, copy_formatting
=True)
25 if create_from
is None:
26 self
.annotation_buffer
= StringIO()
31 # When creating an insertion point, keep references to the same database
32 self
.annotation_buffer
= create_from
.annotation_buffer
33 self
.annotations
= create_from
.annotations
34 self
.code
= create_from
.code
35 self
.last_pos
= create_from
.last_pos
37 def create_new(self
, create_from
, buffer, copy_formatting
):
38 return AnnotationCCodeWriter(create_from
, buffer, copy_formatting
)
41 CCodeWriter
.write(self
, s
)
42 self
.annotation_buffer
.write(s
)
44 def mark_pos(self
, pos
):
46 CCodeWriter
.mark_pos(self
, pos
)
48 pos_code
= self
.code
.setdefault(self
.last_pos
[0].filename
,{})
49 code
= pos_code
.get(self
.last_pos
[1], "")
50 pos_code
[self
.last_pos
[1]] = code
+ self
.annotation_buffer
.getvalue()
51 self
.annotation_buffer
= StringIO()
54 def annotate(self
, pos
, item
):
55 self
.annotations
.append((pos
, item
))
57 def save_annotation(self
, source_filename
, target_filename
):
59 f
= Utils
.open_source_file(source_filename
)
61 for k
, line
in enumerate(lines
):
62 for c
, cc
, html
in special_chars
:
63 line
= line
.replace(c
, cc
)
68 for pos
, item
in self
.annotations
:
69 if pos
[0].filename
== source_filename
:
71 size
, end
= item
.end()
73 all
.append((pos
, start
))
74 all
.append(((source_filename
, pos
[1], pos
[2]+size
), end
))
76 all
.append((pos
, start
+end
))
78 all
.sort(reverse
=True)
84 lines
[line_no
] = line
[:col
] + item
+ line
[col
:]
86 html_filename
= os
.path
.splitext(target_filename
)[0] + ".html"
87 f
= codecs
.open(html_filename
, "w", encoding
="UTF-8")
88 f
.write(u
'<!DOCTYPE html>\n')
89 f
.write(u
'<!-- Generated by Cython %s -->\n' % Version
.watermark
)
93 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
94 <style type="text/css">
96 body { font-family: courier; font-size: 12; }
98 .code { font-size: 9; color: #444444; display: none; margin-left: 20px; }
99 .py_c_api { color: red; }
100 .py_macro_api { color: #FF7000; }
101 .pyx_c_api { color: #FF3000; }
102 .pyx_macro_api { color: #FF7000; }
103 .refnanny { color: #FFA000; }
105 .error_goto { color: #FFA000; }
109 .coerce { color: #008000; border: 1px dotted #008000 }
111 .py_attr { color: #FF0000; font-weight: bold; }
112 .c_attr { color: #0000FF; }
114 .py_call { color: #FF0000; font-weight: bold; }
115 .c_call { color: #0000FF; }
117 .line { margin: 0em }
121 function toggleDiv(id) {
122 theDiv = document.getElementById(id);
123 if (theDiv.style.display != 'block') theDiv.style.display = 'block';
124 else theDiv.style.display = 'none';
130 f
.write(u
'<p>Generated by Cython %s\n' % Version
.watermark
)
131 c_file
= Utils
.decode_filename(os
.path
.basename(target_filename
))
132 f
.write(u
'<p>Raw output: <a href="%s">%s</a>\n' % (c_file
, c_file
))
134 zero_calls
= dict((name
, 0) for name
in
135 'refnanny py_macro_api py_c_api pyx_macro_api pyx_c_api error_goto'.split())
138 group_name
= match
.lastgroup
139 calls
[group_name
] += 1
140 return ur
"<span class='%s'>%s</span>" % (
141 group_name
, match
.group(group_name
))
143 pos_comment_marker
= u
'/* \N{HORIZONTAL ELLIPSIS} */\n'
145 code_source_file
= self
.code
.get(source_filename
, {})
149 code
= code_source_file
[k
]
153 code
= _replace_pos_comment(pos_comment_marker
, code
)
154 if code
.startswith(pos_comment_marker
):
155 code
= code
[len(pos_comment_marker
):]
156 code
= html_escape(code
)
158 calls
= zero_calls
.copy()
159 code
= _parse_code(annotate
, code
)
160 score
= (5 * calls
['py_c_api'] + 2 * calls
['pyx_c_api'] +
161 calls
['py_macro_api'] + calls
['pyx_macro_api'])
162 color
= u
"FFFF%02x" % int(255/(1+score
/10.0))
163 f
.write(u
"<pre class='line' style='background-color: #%s' onclick='toggleDiv(\"line%s\")'>" % (color
, k
))
165 f
.write(u
" %d: " % k
)
166 for c
, cc
, html
in special_chars
:
167 line
= line
.replace(cc
, html
)
168 f
.write(line
.rstrip())
171 f
.write(u
"<pre id='line%s' class='code' style='background-color: #%s'>%s</pre>" % (k
, color
, code
))
172 f
.write(u
'</body></html>\n')
176 _parse_code
= re
.compile(
177 ur
'(?P<refnanny>__Pyx_X?(?:GOT|GIVE)REF|__Pyx_RefNanny[A-Za-z]+)|'
179 ur
'(?P<pyx_macro_api>__Pyx_[A-Z][A-Z_]+)|'
180 ur
'(?P<pyx_c_api>__Pyx_[A-Z][a-z_][A-Za-z_]+)|'
181 ur
'(?P<py_macro_api>Py[A-Z][a-z]+_[A-Z][A-Z_]+)|'
182 ur
'(?P<py_c_api>Py[A-Z][a-z]+_[A-Z][a-z][A-Za-z_]+)'
183 ur
')(?=\()|' # look-ahead to exclude subsequent '(' from replacement
184 ur
'(?P<error_goto>(?:(?<=;) *if .* +)?\{__pyx_filename = .*goto __pyx_L\w+;\})'
188 _replace_pos_comment
= re
.compile(
189 # this matches what Cython generates as code line marker comment
190 ur
'^\s*/\*(?:(?:[^*]|\*[^/])*\n)+\s*\*/\s*\n',
195 class AnnotationItem(object):
197 def __init__(self
, style
, text
, tag
="", size
=0):
204 return u
"<span class='tag %s' title='%s'>%s" % (self
.style
, self
.text
, self
.tag
)
207 return self
.size
, u
"</span>"