fix generators after raise-from merge
[cython.git] / Cython / Compiler / Annotate.py
index 21ad7b3da689666b0201e819ab6a0059b90791b7..98823fe0caddd5894796d315bc08e436aee914fe 100644 (file)
@@ -3,52 +3,59 @@
 import os
 import re
 import time
+import codecs
 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 = [('<', '\xF0', '&lt;'),
-                 ('>', '\xF1', '&gt;'), 
-                 ('&', '\xF2', '&amp;')]
+special_chars = [(u'<', u'\xF0', u'&lt;'),
+                 (u'>', u'\xF1', u'&gt;'),
+                 (u'&', u'\xF2', u'&amp;')]
+
+line_pos_comment = re.compile(r'/\*.*?<<<<<<<<<<<<<<.*?\*/\n*', re.DOTALL)
 
 class AnnotationCCodeWriter(CCodeWriter):
 
-    def __init__(self, f):
-        CCodeWriter.__init__(self, self)
-        self.buffer = StringIO()
-        self.real_f = f
-        self.annotations = []
-        self.last_pos = None
-        self.code = {}
-        
-    def getvalue(self):
-        return self.real_f.getvalue()
-        
+    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):
-        self.real_f.write(s)
-        self.buffer.write(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)
-#        return
+        if pos is not None:
+            CCodeWriter.mark_pos(self, pos)
         if self.last_pos:
-            try:
-                code = self.code[self.last_pos[1]]
-            except KeyError:
-                code = ""
-            self.code[self.last_pos[1]] = code + self.buffer.getvalue()
-        self.buffer = StringIO()
+            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, filename):
+
+    def save_annotation(self, source_filename, target_filename):
         self.mark_pos(None)
-        f = open(filename)
+        f = Utils.open_source_file(source_filename)
         lines = f.readlines()
         for k in range(len(lines)):
             line = lines[k]
@@ -57,16 +64,17 @@ class AnnotationCCodeWriter(CCodeWriter):
             lines[k] = line
         f.close()
         all = []
-        for pos, item in self.annotations:
-            if pos[0] == filename:
-                start = item.start()
-                size, end = item.end()
-                if size:
-                    all.append((pos, start))
-                    all.append(((filename, pos[1], pos[2]+size), end))
-                else:
-                    all.append((pos, start+end))
-                
+        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()
         all.reverse()
         for pos, item in all:
@@ -75,20 +83,26 @@ class AnnotationCCodeWriter(CCodeWriter):
             col += 1
             line = lines[line_no]
             lines[line_no] = line[:col] + item + line[col:]
-        
-        f = open("%s.html" % filename, "w")
-        f.write('<html>\n')
-        f.write("""
+
+        html_filename = os.path.splitext(target_filename)[0] + ".html"
+        f = codecs.open(html_filename, "w", encoding="UTF-8")
+        f.write(u'<!-- Generated by Cython %s on %s -->\n' % (Version.version, time.asctime()))
+        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_api  { color: red; }
-.pyx_api  { color: #FF3000; }
-.py_macro_api  { color: #FF8000; }
-.error_goto  { color: #FF8000; }
+.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  {  }
 
@@ -112,65 +126,74 @@ function toggleDiv(id) {
 </script>
 </head>
         """)
-        f.write('<body>\n')
-        f.write('<p>Generated by Cython %s on %s\n' % (Version.version, time.asctime()))
-        c_file = os.path.basename(filename)[:-3] + 'c'
-        f.write('<p>Raw output: <a href="%s">%s</a>\n' % (c_file, c_file))
+        f.write(u'<body>\n')
+        f.write(u'<p>Generated by Cython %s on %s\n' % (Version.version, time.asctime()))
+        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))
         k = 0
-        
-        py_c_api = re.compile('(Py[A-Z][a-z]+_[A-Z][a-z][A-Za-z_]+)')
-        pyx_api = re.compile('(__Pyx[A-Za-z_]+)\(')
-        py_marco_api = re.compile('(Py[A-Za-z]*_[A-Z][A-Z_]+)')
-        error_goto = re.compile(r'((; *if .*)? \{__pyx_filename = .*goto __pyx_L\w+;\})')
-        
+
+        py_c_api = re.compile(u'(Py[A-Z][a-z]+_[A-Z][a-z][A-Za-z_]+)\(')
+        py_marco_api = re.compile(u'(Py[A-Z][a-z]+_[A-Z][A-Z_]+)\(')
+        pyx_c_api = re.compile(u'(__Pyx_[A-Z][a-z_][A-Za-z_]+)\(')
+        pyx_macro_api = re.compile(u'(__Pyx_[A-Z][A-Z_]+)\(')
+        error_goto = re.compile(ur'((; *if .*)? \{__pyx_filename = .*goto __pyx_L\w+;\})')
+        refnanny = re.compile(u'(__Pyx_X?(GOT|GIVE)REF|__Pyx_RefNanny[A-Za-z]+)')
+
+        code_source_file = self.code[source_filename]
         for line in lines:
 
             k += 1
             try:
-                code = self.code[k]
+                code = code_source_file[k]
             except KeyError:
                 code = ''
-                
-            code, c_api_calls = py_c_api.subn(r"<span class='py_api'>\1</span>", code)
-            code, pyx_api_calls = pyx_api.subn(r"<span class='pyx_api'>\1</span>(", code)
-            code, macro_api_calls = py_marco_api.subn(r"<span class='py_macro_api'>\1</span>", code)
-            code, error_goto_calls = error_goto.subn(r"<span class='error_goto'>\1</span>", code)
-            
-            code = code.replace("<span class='error_goto'>;", ";<span class='error_goto'>")
-            
-            color = "FFFF%02x" % int(255/(1+(5*c_api_calls+2*pyx_api_calls+macro_api_calls)/10.0))
-            f.write("<pre class='line' style='background-color: #%s' onclick='toggleDiv(\"line%s\")'>" % (color, k))
-
-            f.write(" %d: " % k)
+
+            code = code.replace('<', '<code><</code>')
+
+            code, py_c_api_calls = py_c_api.subn(ur"<span class='py_c_api'>\1</span>(", code)
+            code, pyx_c_api_calls = pyx_c_api.subn(ur"<span class='pyx_c_api'>\1</span>(", code)
+            code, py_macro_api_calls = py_marco_api.subn(ur"<span class='py_macro_api'>\1</span>(", code)
+            code, pyx_macro_api_calls = pyx_macro_api.subn(ur"<span class='pyx_macro_api'>\1</span>(", code)
+            code, refnanny_calls = refnanny.subn(ur"<span class='refnanny'>\1</span>", code)
+            code, error_goto_calls = error_goto.subn(ur"<span class='error_goto'>\1</span>", code)
+
+            code = code.replace(u"<span class='error_goto'>;", u";<span class='error_goto'>")
+
+            score = 5*py_c_api_calls + 2*pyx_c_api_calls + py_macro_api_calls + pyx_macro_api_calls - refnanny_calls
+            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 = str(line).replace(cc, html)
+                line = line.replace(cc, html)
             f.write(line.rstrip())
-                
-            f.write('</pre>\n')
-            f.write("<pre id='line%s' class='code' style='background-color: #%s'>%s</pre>" % (k, color, code))
-        f.write('</body></html>\n')
+
+            f.write(u'</pre>\n')
+            code = re.sub(line_pos_comment, '', code) # inline annotations are redundant
+            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()
-        
+
 
 # TODO: make this cleaner
 def escape(raw_string):
-    raw_string = raw_string.replace("\'", r"&#146;")
-    raw_string = raw_string.replace('\"', r'&quot;')
-    raw_string = raw_string.replace('\n', r'<br>\n')
-    raw_string = raw_string.replace('\t', r'\t')
+    raw_string = raw_string.replace(u"\'", ur"&#146;")
+    raw_string = raw_string.replace(u'\"', ur'&quot;')
+    raw_string = raw_string.replace(u'\n', ur'<br>\n')
+    raw_string = raw_string.replace(u'\t', ur'\t')
     return raw_string
 
 
-class AnnotationItem:
-    
+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 "<span class='tag %s' title='%s'>%s" % (self.style, self.text, self.tag)
-    
+        return u"<span class='tag %s' title='%s'>%s" % (self.style, self.text, self.tag)
+
     def end(self):
-        return self.size, "</span>"
+        return self.size, u"</span>"