"""HTML reporting for coverage.py""" import os from coverage import __version__ # pylint: disable-msg=W0611 from coverage.report import Reporter from coverage.templite import Templite class HtmlReporter(Reporter): """HTML reporting. """ def __init__(self, coverage, ignore_errors=False): super(HtmlReporter, self).__init__(coverage, ignore_errors) self.directory = None self.source_tmpl = Templite(SOURCE, globals()) def report(self, morfs, directory=None, omit_prefixes=None): assert directory, "must provide a directory for html reporting" self.report_files(self.html_file, morfs, directory, omit_prefixes) def html_file(self, cu, statements, excluded, missing): """Generate an HTML file for one source file.""" lines = [] source = cu.source_file() for lineno, line in enumerate(source.readlines()): lineno += 1 css_class = "" if lineno in statements: css_class += " stm" if lineno not in missing and lineno not in excluded: css_class += " run" if lineno in excluded: css_class += " exc" if lineno in missing: css_class += " mis" lineinfo = { 'text': line, 'number': lineno, 'class': css_class.strip() or "pln" } lines.append(lineinfo) html_filename = os.path.join(self.directory, cu.flat_rootname()) html_filename += ".html" fhtml = open(html_filename, 'w') fhtml.write(self.source_tmpl.render(locals())) fhtml.close() # Helpers for templates def escape(t): """HTML-escape the text in t.""" return (t .replace("&", "&").replace("<", "<").replace(">", ">") .replace("'", "'").replace('"', """) .replace(" ", "  ") ) def not_empty(t): """Make sure HTML content is not completely empty.""" return t or " " # Templates # For making line numbers and text different fonts: # http://24ways.org/2006/compose-to-a-vertical-rhythm SOURCE = """\ Coverage for {{cu.name|escape}}
{% for line in lines %}

{{line.number}}

{% endfor %}
{% for line in lines %}

{{line.text.rstrip|escape|not_empty}}

{% endfor %}
"""