DebugClients/Python/coverage/html.py

changeset 5051
3586ebd9fac8
parent 4491
0d8612e24fef
diff -r 04e5dfbd3f3d -r 3586ebd9fac8 DebugClients/Python/coverage/html.py
--- a/DebugClients/Python/coverage/html.py	Sat Jul 23 13:33:54 2016 +0200
+++ b/DebugClients/Python/coverage/html.py	Sun Jul 24 12:01:01 2016 +0200
@@ -6,18 +6,19 @@
 import datetime
 import json
 import os
-import re
 import shutil
 
 import coverage
 from coverage import env
 from coverage.backward import iitems
 from coverage.files import flat_rootname
-from coverage.misc import CoverageException, Hasher
+from coverage.misc import CoverageException, Hasher, isolate_module
 from coverage.report import Reporter
 from coverage.results import Numbers
 from coverage.templite import Templite
 
+os = isolate_module(os)
+
 
 # Static files are looked for in a list of places.
 STATIC_PATH = [
@@ -57,12 +58,18 @@
     )
 
 
-def data(fname):
+def read_data(fname):
     """Return the contents of a data file of ours."""
     with open(data_filename(fname)) as data_file:
         return data_file.read()
 
 
+def write_html(fname, html):
+    """Write `html` to `fname`, properly encoded."""
+    with open(fname, "wb") as fout:
+        fout.write(html.encode('ascii', 'xmlcharrefreplace'))
+
+
 class HtmlReporter(Reporter):
     """HTML reporting."""
 
@@ -93,9 +100,7 @@
             '__url__': coverage.__url__,
             '__version__': coverage.__version__,
         }
-        self.source_tmpl = Templite(
-            data("pyfile.html"), self.template_globals
-        )
+        self.source_tmpl = Templite(read_data("pyfile.html"), self.template_globals)
 
         self.coverage = cov
 
@@ -157,11 +162,6 @@
                 os.path.join(self.directory, self.extra_css)
             )
 
-    def write_html(self, fname, html):
-        """Write `html` to `fname`, properly encoded."""
-        with open(fname, "wb") as fout:
-            fout.write(html.encode('ascii', 'xmlcharrefreplace'))
-
     def file_hash(self, source, fr):
         """Compute a hash that changes if the file needs to be re-reported."""
         m = Hasher()
@@ -189,6 +189,7 @@
 
         if self.has_arcs:
             missing_branch_arcs = analysis.missing_branch_arcs()
+            arcs_executed = analysis.arcs_executed()
 
         # These classes determine which lines are highlighted by default.
         c_run = "run hide_run"
@@ -202,7 +203,7 @@
             # Figure out how to mark this line.
             line_class = []
             annotate_html = ""
-            annotate_title = ""
+            annotate_long = ""
             if lineno in analysis.statements:
                 line_class.append("stm")
             if lineno in analysis.excluded:
@@ -216,23 +217,22 @@
                 for b in missing_branch_arcs[lineno]:
                     if b < 0:
                         shorts.append("exit")
-                        longs.append("the function exit")
                     else:
                         shorts.append(b)
-                        longs.append("line %d" % b)
+                    longs.append(fr.missing_arc_description(lineno, b, arcs_executed))
                 # 202F is NARROW NO-BREAK SPACE.
                 # 219B is RIGHTWARDS ARROW WITH STROKE.
                 short_fmt = "%s&#x202F;&#x219B;&#x202F;%s"
                 annotate_html = ",&nbsp;&nbsp; ".join(short_fmt % (lineno, d) for d in shorts)
-                annotate_html += " [?]"
 
-                annotate_title = "Line %d was executed, but never jumped to " % lineno
                 if len(longs) == 1:
-                    annotate_title += longs[0]
-                elif len(longs) == 2:
-                    annotate_title += longs[0] + " or " + longs[1]
+                    annotate_long = longs[0]
                 else:
-                    annotate_title += ", ".join(longs[:-1]) + ", or " + longs[-1]
+                    annotate_long = "%d missed branches: %s" % (
+                        len(longs),
+                        ", ".join("%d) %s" % (num, ann_long)
+                            for num, ann_long in enumerate(longs, start=1)),
+                    )
             elif lineno in analysis.statements:
                 line_class.append(c_run)
 
@@ -252,21 +252,26 @@
                 'number': lineno,
                 'class': ' '.join(line_class) or "pln",
                 'annotate': annotate_html,
-                'annotate_title': annotate_title,
+                'annotate_long': annotate_long,
             })
 
         # Write the HTML page for this file.
-        template_values = {
-            'c_exc': c_exc, 'c_mis': c_mis, 'c_par': c_par, 'c_run': c_run,
-            'has_arcs': self.has_arcs, 'extra_css': self.extra_css,
-            'fr': fr, 'nums': nums, 'lines': lines,
+        html = self.source_tmpl.render({
+            'c_exc': c_exc,
+            'c_mis': c_mis,
+            'c_par': c_par,
+            'c_run': c_run,
+            'has_arcs': self.has_arcs,
+            'extra_css': self.extra_css,
+            'fr': fr,
+            'nums': nums,
+            'lines': lines,
             'time_stamp': self.time_stamp,
-        }
-        html = spaceless(self.source_tmpl.render(template_values))
+        })
 
         html_filename = rootname + ".html"
         html_path = os.path.join(self.directory, html_filename)
-        self.write_html(html_path, html)
+        write_html(html_path, html)
 
         # Save this file's information for the index file.
         index_info = {
@@ -279,7 +284,7 @@
 
     def index_file(self):
         """Write the index.html file for this report."""
-        index_tmpl = Templite(data("index.html"), self.template_globals)
+        index_tmpl = Templite(read_data("index.html"), self.template_globals)
 
         self.totals = sum(f['nums'] for f in self.files)
 
@@ -291,7 +296,7 @@
             'time_stamp': self.time_stamp,
         })
 
-        self.write_html(os.path.join(self.directory, "index.html"), html)
+        write_html(os.path.join(self.directory, "index.html"), html)
 
         # Write the latest hashes for next time.
         self.status.write(self.directory)
@@ -416,29 +421,13 @@
 # Helpers for templates and generating HTML
 
 def escape(t):
-    """HTML-escape the text in `t`."""
-    return (
-        t
-        # Convert HTML special chars into HTML entities.
-        .replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
-        .replace("'", "&#39;").replace('"', "&quot;")
-        # Convert runs of spaces: "......" -> "&nbsp;.&nbsp;.&nbsp;."
-        .replace("  ", "&nbsp; ")
-        # To deal with odd-length runs, convert the final pair of spaces
-        # so that "....." -> "&nbsp;.&nbsp;&nbsp;."
-        .replace("  ", "&nbsp; ")
-    )
+    """HTML-escape the text in `t`.
 
-
-def spaceless(html):
-    """Squeeze out some annoying extra space from an HTML string.
-
-    Nicely-formatted templates mean lots of extra space in the result.
-    Get rid of some.
+    This is only suitable for HTML text, not attributes.
 
     """
-    html = re.sub(r">\s+<p ", ">\n<p ", html)
-    return html
+    # Convert HTML special chars into HTML entities.
+    return t.replace("&", "&amp;").replace("<", "&lt;")
 
 
 def pair(ratio):

eric ide

mercurial