--- 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 ↛ %s" annotate_html = ", ".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("&", "&").replace("<", "<").replace(">", ">") - .replace("'", "'").replace('"', """) - # Convert runs of spaces: "......" -> " . . ." - .replace(" ", " ") - # To deal with odd-length runs, convert the final pair of spaces - # so that "....." -> " . ." - .replace(" ", " ") - ) + """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("&", "&").replace("<", "<") def pair(ratio):