Beispiel #1
0
 def test_passthrough(self):
     # Strings without variables are passed through unchanged.
     self.assertEqual(Templite("Hello").render(), "Hello")
     self.assertEqual(
         Templite("Hello, 20% fun time!").render(),
         "Hello, 20% fun time!"
         )
Beispiel #2
0
    def index_file(self):
        """Write the index.html file for this report."""
        index_tmpl = Templite(read_data("index.html"), self.template_globals)

        skipped_covered_msg = skipped_empty_msg = ""
        if self.skipped_covered_count:
            msg = "{} {} skipped due to complete coverage."
            skipped_covered_msg = msg.format(
                self.skipped_covered_count,
                "file" if self.skipped_covered_count == 1 else "files",
            )
        if self.skipped_empty_count:
            msg = "{} empty {} skipped."
            skipped_empty_msg = msg.format(
                self.skipped_empty_count,
                "file" if self.skipped_empty_count == 1 else "files",
            )

        html = index_tmpl.render({
            'files': self.file_summaries,
            'totals': self.totals,
            'skipped_covered_msg': skipped_covered_msg,
            'skipped_empty_msg': skipped_empty_msg,
        })

        index_file = os.path.join(self.directory, "index.html")
        write_html(index_file, html)
        self.coverage._message(f"Wrote HTML report to {index_file}")

        # Write the latest hashes for next time.
        self.incr.write()
Beispiel #3
0
    def __init__(self, cov, config):
        super(HtmlReporter, self).__init__(cov, config)
        self.directory = None
        title = self.config.html_title
        if env.PY2:
            title = title.decode("utf8")
        self.template_globals = {
            'escape': escape,
            'pair': pair,
            'title': title,
            '__url__': coverage.__url__,
            '__version__': coverage.__version__,
        }
        self.source_tmpl = Templite(read_data("pyfile.html"),
                                    self.template_globals)

        self.coverage = cov

        self.files = []
        self.all_files_nums = []
        self.has_arcs = self.coverage.data.has_arcs()
        self.status = HtmlStatus()
        self.extra_css = None
        self.totals = Numbers()
        self.time_stamp = datetime.datetime.now().strftime('%Y-%m-%d %H:%M')
Beispiel #4
0
 def __init__(self, coverage, ignore_errors=False):
     super(HtmlReporter, self).__init__(coverage, ignore_errors)
     self.directory = None
     self.source_tmpl = Templite(data("htmlfiles/pyfile.html"), globals())
     
     self.files = []
     self.arcs = coverage.data.has_arcs()
Beispiel #5
0
    def __init__(self, cov):
        self.coverage = cov
        self.config = self.coverage.config
        self.directory = self.config.html_dir

        self.skip_covered = self.config.html_skip_covered
        if self.skip_covered is None:
            self.skip_covered = self.config.skip_covered
        self.skip_empty = self.config.html_skip_empty
        if self.skip_empty is None:
            self.skip_empty = self.config.skip_empty
        self.skipped_covered_count = 0
        self.skipped_empty_count = 0

        title = self.config.html_title

        if self.config.extra_css:
            self.extra_css = os.path.basename(self.config.extra_css)
        else:
            self.extra_css = None

        self.data = self.coverage.get_data()
        self.has_arcs = self.data.has_arcs()

        self.file_summaries = []
        self.all_files_nums = []
        self.incr = IncrementalChecker(self.directory)
        self.datagen = HtmlDataGeneration(self.coverage)
        self.totals = Numbers(precision=self.config.precision)
        self.directory_was_empty = False

        self.template_globals = {
            # Functions available in the templates.
            'escape': escape,
            'pair': pair,
            'len': len,

            # Constants for this report.
            '__url__': coverage.__url__,
            '__version__': coverage.__version__,
            'title': title,
            'time_stamp': format_local_datetime(datetime.datetime.now()),
            'extra_css': self.extra_css,
            'has_arcs': self.has_arcs,
            'show_contexts': self.config.show_contexts,

            # Constants for all reports.
            # These css classes determine which lines are highlighted by default.
            'category': {
                'exc': 'exc show_exc',
                'mis': 'mis show_mis',
                'par': 'par run show_par',
                'run': 'run',
            }
        }
        self.pyfile_html_source = read_data("pyfile.html")
        self.source_tmpl = Templite(self.pyfile_html_source,
                                    self.template_globals)
Beispiel #6
0
    def test_reusability(self):
        # A single Templite can be used more than once with different data.
        globs = {
            'upper': lambda x: x.upper(),
            'punct': '!',
        }

        template = Templite("This is {{name|upper}}{{punct}}", globs)
        self.assertEqual(template.render({'name': 'Ned'}), "This is NED!")
        self.assertEqual(template.render({'name': 'Ben'}), "This is BEN!")
Beispiel #7
0
    def test_reusability(self):
        # A single Templite can be used more than once with different data.
        globs = {
            'upper': lambda x: x.upper(),
            'punct': '!',
            }

        template = Templite("This is {{name|upper}}{{punct}}", globs)
        self.assertEqual(template.render({'name':'Ned'}), "This is NED!")
        self.assertEqual(template.render({'name':'Ben'}), "This is BEN!")
Beispiel #8
0
 def index_file(self):
     index_tmpl = Templite(data('index.html'), self.template_globals)
     self.totals = sum([ f['nums'] for f in self.files ])
     html = index_tmpl.render({'arcs': self.arcs,
      'extra_css': self.extra_css,
      'files': self.files,
      'totals': self.totals})
     if sys.version_info < (3, 0):
         html = html.decode('utf-8')
     self.write_html(os.path.join(self.directory, 'index.html'), html)
     self.status.write(self.directory)
Beispiel #9
0
    def index_file(self):
        """Write the index.html file for this report."""
        index_tmpl = Templite(data("htmlfiles/index.html"), globals())

        files = self.files
        arcs = self.arcs

        totals = sum([f['nums'] for f in files])

        fhtml = open(os.path.join(self.directory, "index.html"), "w")
        fhtml.write(index_tmpl.render(locals()))
        fhtml.close()
Beispiel #10
0
    def index_file(self):
        """Write the index.html file for this report."""
        index_tmpl = Templite(read_data("index.html"), self.template_globals)

        html = index_tmpl.render({
            'files': self.file_summaries,
            'totals': self.totals,
        })

        write_html(os.path.join(self.directory, "index.html"), html)

        # Write the latest hashes for next time.
        self.incr.write()
Beispiel #11
0
 def index_file(self):
     index_tmpl = Templite(data('index.html'), self.template_globals)
     self.totals = sum([f['nums'] for f in self.files])
     html = index_tmpl.render({
         'arcs': self.arcs,
         'extra_css': self.extra_css,
         'files': self.files,
         'totals': self.totals
     })
     if sys.version_info < (3, 0):
         html = html.decode('utf-8')
     self.write_html(os.path.join(self.directory, 'index.html'), html)
     self.status.write(self.directory)
Beispiel #12
0
Datei: html.py Projekt: th0/test2
    def index_file(self):
        """Write the index.html file for this report."""
        index_tmpl = Templite(data("index.html"), self.template_globals)

        self.totals = sum(f["nums"] for f in self.files)

        html = index_tmpl.render(
            {"arcs": self.arcs, "extra_css": self.extra_css, "files": self.files, "totals": self.totals}
        )

        self.write_html(os.path.join(self.directory, "index.html"), html)

        # Write the latest hashes for next time.
        self.status.write(self.directory)
Beispiel #13
0
    def __init__(self, cov):
        self.coverage = cov
        self.config = self.coverage.config
        self.directory = self.config.html_dir
        title = self.config.html_title
        if env.PY2:
            title = title.decode("utf8")

        if self.config.extra_css:
            self.extra_css = os.path.basename(self.config.extra_css)
        else:
            self.extra_css = None

        self.data = self.coverage.get_data()
        self.has_arcs = self.data.has_arcs()

        self.file_summaries = []
        self.all_files_nums = []
        self.incr = IncrementalChecker(self.directory)
        self.datagen = HtmlDataGeneration(self.coverage)
        self.totals = Numbers()

        self.template_globals = {
            # Functions available in the templates.
            'escape': escape,
            'pair': pair,
            'len': len,

            # Constants for this report.
            '__url__': coverage.__url__,
            '__version__': coverage.__version__,
            'title': title,
            'time_stamp': datetime.datetime.now().strftime('%Y-%m-%d %H:%M'),
            'extra_css': self.extra_css,
            'has_arcs': self.has_arcs,
            'show_contexts': self.config.show_contexts,

            # Constants for all reports.
            # These css classes determine which lines are highlighted by default.
            'category': {
                'exc': 'exc',
                'mis': 'mis',
                'par': 'par run hide_run',
                'run': 'run hide_run',
            }
        }
        self.pyfile_html_source = read_data("pyfile.html")
        self.source_tmpl = Templite(self.pyfile_html_source,
                                    self.template_globals)
Beispiel #14
0
    def __init__(self, cov, config):
        super(HtmlReporter, self).__init__(cov, config)
        self.directory = None
        title = self.config.html_title
        if env.PY2:
            title = title.decode("utf8")
        self.template_globals = {
            'escape': escape,
            'pair': pair,
            'title': title,
            '__url__': coverage.__url__,
            '__version__': coverage.__version__,
            }
        self.source_tmpl = Templite(
            data("pyfile.html"), self.template_globals
            )

        self.coverage = cov

        self.files = []
        self.arcs = self.coverage.data.has_arcs()
        self.status = HtmlStatus()
        self.extra_css = None
        self.totals = Numbers()
        self.time_stamp = datetime.datetime.now().strftime('%Y-%m-%d %H:%M')
Beispiel #15
0
    def index_file(self):
        """Write the index.html file for this report."""
        index_tmpl = Templite(data("htmlfiles/index.html"),
                              self.template_globals)

        files = self.files
        arcs = self.arcs

        totals = sum([f['nums'] for f in files])
        extra_css = self.extra_css

        self.write_html(os.path.join(self.directory, "index.html"),
                        index_tmpl.render(locals()))

        # Write the latest hashes for next time.
        self.status.write(self.directory)
Beispiel #16
0
    def __init__(self, cov, ignore_errors=False):
        super(HtmlReporter, self).__init__(cov, ignore_errors)
        self.directory = None
        self.template_globals = {
            'escape': escape,
            '__url__': coverage.__url__,
            '__version__': coverage.__version__,
        }
        self.source_tmpl = Templite(data("htmlfiles/pyfile.html"),
                                    self.template_globals)

        self.coverage = cov

        self.files = []
        self.arcs = self.coverage.data.has_arcs()
        self.status = HtmlStatus()
Beispiel #17
0
 def __init__(self, cov, config):
     super(HtmlReporter, self).__init__(cov, config)
     self.directory = None
     self.template_globals = {
         'escape': escape,
         'title': self.config.html_title,
         '__url__': coverage.__url__,
         '__version__': coverage.__version__
     }
     self.source_tmpl = Templite(data('pyfile.html'), self.template_globals)
     self.coverage = cov
     self.files = []
     self.arcs = self.coverage.data.has_arcs()
     self.status = HtmlStatus()
     self.extra_css = None
     self.totals = Numbers()
Beispiel #18
0
    def try_render(self, text, ctx=None, result=None):
        """Render `text` through `ctx`, and it had better be `result`.

        Result defaults to None so we can shorten the calls where we expect
        an exception and never get to the result comparison.
        """
        actual = Templite(text).render(ctx or {})
        if result:
            self.assertEqual(actual, result)
Beispiel #19
0
    def index_file(self):
        """Write the index.html file for this report."""
        index_tmpl = Templite(data("htmlfiles/index.html"),
                              self.template_globals)

        files = self.files
        arcs = self.arcs

        totals = sum([f['nums'] for f in files])

        fhtml = open(os.path.join(self.directory, "index.html"), "w")
        try:
            fhtml.write(index_tmpl.render(locals()))
        finally:
            fhtml.close()

        # Write the latest hashes for next time.
        self.status.write(self.directory)
Beispiel #20
0
    def index_file(self):
        """Write the index.html file for this report."""
        index_tmpl = Templite(
            data("htmlfiles/index.html"), self.template_globals
            )

        files = self.files
        arcs = self.arcs

        totals = sum([f['nums'] for f in files])

        self.write_html(
            os.path.join(self.directory, "index.html"),
            index_tmpl.render(locals())
            )

        # Write the latest hashes for next time.
        self.status.write(self.directory)
Beispiel #21
0
    def index_file(self):
        """Write the index.html file for this report."""
        index_tmpl = Templite(data("index.html"), self.template_globals)

        self.totals = sum(f['nums'] for f in self.files)

        html = index_tmpl.render({
            'has_arcs': self.has_arcs,
            'extra_css': self.extra_css,
            'files': self.files,
            'totals': self.totals,
            'time_stamp': self.time_stamp,
        })

        self.write_html(os.path.join(self.directory, "index.html"), html)

        # Write the latest hashes for next time.
        self.status.write(self.directory)
Beispiel #22
0
    def index_file(self):
        """Write the index.html file for this report."""
        index_tmpl = Templite(read_data("index.html"), self.template_globals)

        self.totals = sum(f['nums'] for f in self.files)

        html = index_tmpl.render({
            'has_arcs': self.has_arcs,
            'extra_css': self.extra_css,
            'files': self.files,
            'totals': self.totals,
            'time_stamp': self.time_stamp,
        })

        write_html(os.path.join(self.directory, "index.html"), html)

        # Write the latest hashes for next time.
        self.status.write(self.directory)
Beispiel #23
0
    def index_file(self):
        """Write the index.html file for this report."""
        index_tmpl = Templite(data("index.html"), self.template_globals)

        self.totals = sum([f['nums'] for f in self.files])

        html = index_tmpl.render({
            'arcs': self.arcs,
            'extra_css': self.extra_css,
            'files': self.files,
            'totals': self.totals,
        })

        if sys.version_info < (3, 0):
            html = html.decode("utf-8")
        self.write_html(os.path.join(self.directory, "index.html"), html)

        # Write the latest hashes for next time.
        self.status.write(self.directory)
Beispiel #24
0
    def __init__(self, cov, config):
        super(HtmlReporter, self).__init__(cov, config)
        self.directory = None
        title = self.config.html_title
        try:
            self.inline_styles = self.config.inline_styles
            self.not_inline_styles = False

            # reading the css stylesheet
            f = open(
                os.path.join(os.path.dirname(__file__),
                             *["htmlfiles", "style.css"]), "rb")
            self.css_styles = f.read().decode('utf-8').strip()
            f.flush()
            f.close()
        except Exception as e:
            print(e)
            self.inline_styles = False
            self.not_inline_styles = True
            self.css_styles = None
        if env.PY2:
            title = title.decode("utf8")
        self.template_globals = {
            'escape': escape,
            'pair': pair,
            'title': title,
            '__url__': coverage.__url__,
            '__version__': coverage.__version__,
        }

        self.source_tmpl = Templite(read_data("pyfile.html"),
                                    self.template_globals)

        self.data = cov.get_data()

        self.files = []
        self.all_files_nums = []
        self.has_arcs = self.data.has_arcs()
        self.status = HtmlStatus()
        self.extra_css = None
        self.totals = Numbers()
        self.time_stamp = datetime.datetime.now().strftime('%Y-%m-%d %H:%M')
Beispiel #25
0
    def try_render(self, text, ctx=None, result=None):
        """Render `text` through `ctx`, and it had better be `result`.

        Result defaults to None so we can shorten the calls where we expect
        an exception and never get to the result comparison.
        """
        actual = Templite(text).render(ctx or {})
        # If result is None, then an exception should have prevented us getting
        # to here.
        assert result is not None
        self.assertEqual(actual, result)
Beispiel #26
0
    def index_file(self):
        """Write the index.html file for this report."""
        index_tmpl = Templite(
            data("htmlfiles/index.html"), self.template_globals
            )

        files = self.files
        arcs = self.arcs

        self.totals = totals = sum([f['nums'] for f in files])
        extra_css = self.extra_css

        html = index_tmpl.render(locals())
        if sys.version_info < (3, 0):
            html = html.decode("utf-8")
        self.write_html(
            os.path.join(self.directory, "index.html"),
            html
            )

        # Write the latest hashes for next time.
        self.status.write(self.directory)
Beispiel #27
0
 def __init__(self, cov, config):
     super(HtmlReporter, self).__init__(cov, config)
     self.directory = None
     self.template_globals = {'escape': escape,
      'title': self.config.html_title,
      '__url__': coverage.__url__,
      '__version__': coverage.__version__}
     self.source_tmpl = Templite(data('pyfile.html'), self.template_globals)
     self.coverage = cov
     self.files = []
     self.arcs = self.coverage.data.has_arcs()
     self.status = HtmlStatus()
     self.extra_css = None
     self.totals = Numbers()
Beispiel #28
0
    def index_file(self):
        """Write the index.html file for this report."""
        index_tmpl = Templite(
            data("index.html"), self.template_globals
            )

        self.totals = sum(f['nums'] for f in self.files)

        html = index_tmpl.render({
            'arcs': self.arcs,
            'extra_css': self.extra_css,
            'files': self.files,
            'totals': self.totals,
        })

        if sys.version_info < (3, 0):
            html = html.decode("utf-8")
        self.write_html(
            os.path.join(self.directory, "index.html"),
            html
            )

        # Write the latest hashes for next time.
        self.status.write(self.directory)
Beispiel #29
0
    def __init__(self, cov, ignore_errors=False):
        super(HtmlReporter, self).__init__(cov, ignore_errors)
        self.directory = None
        self.template_globals = {
            'escape': escape,
            '__url__': coverage.__url__,
            '__version__': coverage.__version__,
            }
        self.source_tmpl = Templite(
            data("htmlfiles/pyfile.html"), self.template_globals
            )

        self.coverage = cov

        self.files = []
        self.arcs = self.coverage.data.has_arcs()
        self.status = HtmlStatus()
Beispiel #30
0
Datei: html.py Projekt: th0/test2
    def __init__(self, cov, config):
        super(HtmlReporter, self).__init__(cov, config)
        self.directory = None
        title = self.config.html_title
        if env.PY2:
            title = title.decode("utf8")
        self.template_globals = {
            "escape": escape,
            "pair": pair,
            "title": title,
            "__url__": coverage.__url__,
            "__version__": coverage.__version__,
        }
        self.source_tmpl = Templite(data("pyfile.html"), self.template_globals)

        self.coverage = cov

        self.files = []
        self.arcs = self.coverage.data.has_arcs()
        self.status = HtmlStatus()
        self.extra_css = None
        self.totals = Numbers()
Beispiel #31
0
class HtmlReporter(Reporter):
    STATIC_FILES = ['style.css',
     'jquery.min.js',
     'jquery.hotkeys.js',
     'jquery.isonscreen.js',
     'jquery.tablesorter.min.js',
     'coverage_html.js',
     'keybd_closed.png',
     'keybd_open.png']

    def __init__(self, cov, config):
        super(HtmlReporter, self).__init__(cov, config)
        self.directory = None
        self.template_globals = {'escape': escape,
         'title': self.config.html_title,
         '__url__': coverage.__url__,
         '__version__': coverage.__version__}
        self.source_tmpl = Templite(data('pyfile.html'), self.template_globals)
        self.coverage = cov
        self.files = []
        self.arcs = self.coverage.data.has_arcs()
        self.status = HtmlStatus()
        self.extra_css = None
        self.totals = Numbers()

    def report(self, morfs):
        self.status.read(self.config.html_dir)
        m = Hasher()
        m.update(self.config)
        these_settings = m.digest()
        if self.status.settings_hash() != these_settings:
            self.status.reset()
            self.status.set_settings_hash(these_settings)
        if self.config.extra_css:
            self.extra_css = os.path.basename(self.config.extra_css)
        self.report_files(self.html_file, morfs, self.config.html_dir)
        if not self.files:
            raise CoverageException('No data to report.')
        self.index_file()
        self.make_local_static_report_files()
        return self.totals.pc_covered

    def make_local_static_report_files(self):
        for static in self.STATIC_FILES:
            shutil.copyfile(data_filename(static), os.path.join(self.directory, static))

        if self.extra_css:
            shutil.copyfile(self.config.extra_css, os.path.join(self.directory, self.extra_css))

    def write_html(self, fname, html):
        fout = open(fname, 'wb')
        try:
            fout.write(html.encode('ascii', 'xmlcharrefreplace'))
        finally:
            fout.close()

    def file_hash(self, source, cu):
        m = Hasher()
        m.update(source)
        self.coverage.data.add_to_hash(cu.filename, m)
        return m.digest()

    def html_file(self, cu, analysis):
        source_file = cu.source_file()
        try:
            source = source_file.read()
        finally:
            source_file.close()

        flat_rootname = cu.flat_rootname()
        this_hash = self.file_hash(source, cu)
        that_hash = self.status.file_hash(flat_rootname)
        if this_hash == that_hash:
            self.files.append(self.status.index_info(flat_rootname))
            return
        self.status.set_file_hash(flat_rootname, this_hash)
        if sys.version_info < (3, 0):
            encoding = source_encoding(source)
            if encoding.startswith('utf-8') and source[:3] == '\xef\xbb\xbf':
                source = source[3:]
                encoding = 'utf-8'
        nums = analysis.numbers
        missing_branch_arcs = analysis.missing_branch_arcs()
        c_run = 'run hide_run'
        c_exc = 'exc'
        c_mis = 'mis'
        c_par = 'par ' + c_run
        lines = []
        for lineno, line in enumerate(source_token_lines(source)):
            lineno += 1
            line_class = []
            annotate_html = ''
            annotate_title = ''
            if lineno in analysis.statements:
                line_class.append('stm')
            if lineno in analysis.excluded:
                line_class.append(c_exc)
            elif lineno in analysis.missing:
                line_class.append(c_mis)
            elif self.arcs and lineno in missing_branch_arcs:
                line_class.append(c_par)
                annlines = []
                for b in missing_branch_arcs[lineno]:
                    if b < 0:
                        annlines.append('exit')
                    else:
                        annlines.append(str(b))

                annotate_html = '&nbsp;&nbsp; '.join(annlines)
                if len(annlines) > 1:
                    annotate_title = 'no jumps to these line numbers'
                elif len(annlines) == 1:
                    annotate_title = 'no jump to this line number'
            elif lineno in analysis.statements:
                line_class.append(c_run)
            html = []
            for tok_type, tok_text in line:
                if tok_type == 'ws':
                    html.append(escape(tok_text))
                else:
                    tok_html = escape(tok_text) or '&nbsp;'
                    html.append("<span class='%s'>%s</span>" % (tok_type, tok_html))

            lines.append({'html': ''.join(html),
             'number': lineno,
             'class': ' '.join(line_class) or 'pln',
             'annotate': annotate_html,
             'annotate_title': annotate_title})

        html = spaceless(self.source_tmpl.render({'c_exc': c_exc,
         'c_mis': c_mis,
         'c_par': c_par,
         'c_run': c_run,
         'arcs': self.arcs,
         'extra_css': self.extra_css,
         'cu': cu,
         'nums': nums,
         'lines': lines}))
        if sys.version_info < (3, 0):
            html = html.decode(encoding)
        html_filename = flat_rootname + '.html'
        html_path = os.path.join(self.directory, html_filename)
        self.write_html(html_path, html)
        index_info = {'nums': nums,
         'html_filename': html_filename,
         'name': cu.name}
        self.files.append(index_info)
        self.status.set_index_info(flat_rootname, index_info)

    def index_file(self):
        index_tmpl = Templite(data('index.html'), self.template_globals)
        self.totals = sum([ f['nums'] for f in self.files ])
        html = index_tmpl.render({'arcs': self.arcs,
         'extra_css': self.extra_css,
         'files': self.files,
         'totals': self.totals})
        if sys.version_info < (3, 0):
            html = html.decode('utf-8')
        self.write_html(os.path.join(self.directory, 'index.html'), html)
        self.status.write(self.directory)
Beispiel #32
0
class HtmlReporter(Reporter):
    """HTML reporting."""

    # These files will be copied from the htmlfiles dir to the output dir.
    STATIC_FILES = [
        ("style.css", ""),
        ("jquery.min.js", "jquery"),
        ("jquery.hotkeys.js", "jquery-hotkeys"),
        ("jquery.isonscreen.js", "jquery-isonscreen"),
        ("jquery.tablesorter.min.js", "jquery-tablesorter"),
        ("coverage_html.js", ""),
        ("keybd_closed.png", ""),
        ("keybd_open.png", ""),
    ]

    def __init__(self, cov, config):
        super(HtmlReporter, self).__init__(cov, config)
        self.directory = None
        self.template_globals = {
            'escape': escape,
            'title': self.config.html_title,
            '__url__': coverage.__url__,
            '__version__': coverage.__version__,
        }
        self.source_tmpl = Templite(data("pyfile.html"), self.template_globals)

        self.coverage = cov

        self.files = []
        self.arcs = self.coverage.data.has_arcs()
        self.status = HtmlStatus()
        self.extra_css = None
        self.totals = Numbers()

    def report(self, morfs):
        """Generate an HTML report for `morfs`.

        `morfs` is a list of modules or filenames.

        """
        assert self.config.html_dir, "must give a directory for html reporting"

        # Read the status data.
        self.status.read(self.config.html_dir)

        # Check that this run used the same settings as the last run.
        m = Hasher()
        m.update(self.config)
        these_settings = m.digest()
        if self.status.settings_hash() != these_settings:
            self.status.reset()
            self.status.set_settings_hash(these_settings)

        # The user may have extra CSS they want copied.
        if self.config.extra_css:
            self.extra_css = os.path.basename(self.config.extra_css)

        # Process all the files.
        self.report_files(self.html_file, morfs, self.config.html_dir)

        if not self.files:
            raise CoverageException("No data to report.")

        # Write the index file.
        self.index_file()

        self.make_local_static_report_files()

        return self.totals.pc_covered

    def make_local_static_report_files(self):
        """Make local instances of static files for HTML report."""
        # The files we provide must always be copied.
        for static, pkgdir in self.STATIC_FILES:
            shutil.copyfile(data_filename(static, pkgdir),
                            os.path.join(self.directory, static))

        # The user may have extra CSS they want copied.
        if self.extra_css:
            shutil.copyfile(self.config.extra_css,
                            os.path.join(self.directory, self.extra_css))

    def write_html(self, fname, html):
        """Write `html` to `fname`, properly encoded."""
        fout = open(fname, "wb")
        try:
            fout.write(html.encode('ascii', 'xmlcharrefreplace'))
        finally:
            fout.close()

    def file_hash(self, source, cu):
        """Compute a hash that changes if the file needs to be re-reported."""
        m = Hasher()
        m.update(source)
        self.coverage.data.add_to_hash(cu.filename, m)
        return m.digest()

    def html_file(self, cu, analysis):
        """Generate an HTML file for one source file."""
        source_file = cu.source_file()
        try:
            source = source_file.read()
        finally:
            source_file.close()

        # Find out if the file on disk is already correct.
        flat_rootname = cu.flat_rootname()
        this_hash = self.file_hash(source, cu)
        that_hash = self.status.file_hash(flat_rootname)
        if this_hash == that_hash:
            # Nothing has changed to require the file to be reported again.
            self.files.append(self.status.index_info(flat_rootname))
            return

        self.status.set_file_hash(flat_rootname, this_hash)

        # If need be, determine the encoding of the source file. We use it
        # later to properly write the HTML.
        if sys.version_info < (3, 0):
            encoding = source_encoding(source)
            # Some UTF8 files have the dreaded UTF8 BOM. If so, junk it.
            if encoding.startswith("utf-8") and source[:3] == "\xef\xbb\xbf":
                source = source[3:]
                encoding = "utf-8"

        # Get the numbers for this file.
        nums = analysis.numbers

        if self.arcs:
            missing_branch_arcs = analysis.missing_branch_arcs()

        # These classes determine which lines are highlighted by default.
        c_run = "run hide_run"
        c_exc = "exc"
        c_mis = "mis"
        c_par = "par " + c_run

        lines = []

        for lineno, line in enumerate(source_token_lines(source)):
            lineno += 1  # 1-based line numbers.
            # Figure out how to mark this line.
            line_class = []
            annotate_html = ""
            annotate_title = ""
            if lineno in analysis.statements:
                line_class.append("stm")
            if lineno in analysis.excluded:
                line_class.append(c_exc)
            elif lineno in analysis.missing:
                line_class.append(c_mis)
            elif self.arcs and lineno in missing_branch_arcs:
                line_class.append(c_par)
                annlines = []
                for b in missing_branch_arcs[lineno]:
                    if b < 0:
                        annlines.append("exit")
                    else:
                        annlines.append(str(b))
                annotate_html = "&nbsp;&nbsp; ".join(annlines)
                if len(annlines) > 1:
                    annotate_title = "no jumps to these line numbers"
                elif len(annlines) == 1:
                    annotate_title = "no jump to this line number"
            elif lineno in analysis.statements:
                line_class.append(c_run)

            # Build the HTML for the line
            html = []
            for tok_type, tok_text in line:
                if tok_type == "ws":
                    html.append(escape(tok_text))
                else:
                    tok_html = escape(tok_text) or '&nbsp;'
                    html.append("<span class='%s'>%s</span>" %
                                (tok_type, tok_html))

            lines.append({
                'html': ''.join(html),
                'number': lineno,
                'class': ' '.join(line_class) or "pln",
                'annotate': annotate_html,
                'annotate_title': annotate_title,
            })

        # Write the HTML page for this file.
        html = spaceless(
            self.source_tmpl.render({
                'c_exc': c_exc,
                'c_mis': c_mis,
                'c_par': c_par,
                'c_run': c_run,
                'arcs': self.arcs,
                'extra_css': self.extra_css,
                'cu': cu,
                'nums': nums,
                'lines': lines,
            }))

        if sys.version_info < (3, 0):
            html = html.decode(encoding)

        html_filename = flat_rootname + ".html"
        html_path = os.path.join(self.directory, html_filename)
        self.write_html(html_path, html)

        # Save this file's information for the index file.
        index_info = {
            'nums': nums,
            'html_filename': html_filename,
            'name': cu.name,
        }
        self.files.append(index_info)
        self.status.set_index_info(flat_rootname, index_info)

    def index_file(self):
        """Write the index.html file for this report."""
        index_tmpl = Templite(data("index.html"), self.template_globals)

        self.totals = sum([f['nums'] for f in self.files])

        html = index_tmpl.render({
            'arcs': self.arcs,
            'extra_css': self.extra_css,
            'files': self.files,
            'totals': self.totals,
        })

        if sys.version_info < (3, 0):
            html = html.decode("utf-8")
        self.write_html(os.path.join(self.directory, "index.html"), html)

        # Write the latest hashes for next time.
        self.status.write(self.directory)
Beispiel #33
0
class HtmlReporter(Reporter):
    """HTML reporting."""

    # These files will be copied from the htmlfiles dir to the output dir.
    STATIC_FILES = [
            ("style.css", ""),
            ("jquery.min.js", "jquery"),
            ("jquery.debounce.min.js", "jquery-debounce"),
            ("jquery.hotkeys.js", "jquery-hotkeys"),
            ("jquery.isonscreen.js", "jquery-isonscreen"),
            ("jquery.tablesorter.min.js", "jquery-tablesorter"),
            ("coverage_html.js", ""),
            ("keybd_closed.png", ""),
            ("keybd_open.png", ""),
            ]

    def __init__(self, cov, config):
        super(HtmlReporter, self).__init__(cov, config)
        self.directory = None
        title = self.config.html_title
        if env.PY2:
            title = title.decode("utf8")
        self.template_globals = {
            'escape': escape,
            'pair': pair,
            'title': title,
            '__url__': coverage.__url__,
            '__version__': coverage.__version__,
            }
        self.source_tmpl = Templite(
            data("pyfile.html"), self.template_globals
            )

        self.coverage = cov

        self.files = []
        self.arcs = self.coverage.data.has_arcs()
        self.status = HtmlStatus()
        self.extra_css = None
        self.totals = Numbers()

    def report(self, morfs):
        """Generate an HTML report for `morfs`.

        `morfs` is a list of modules or filenames.

        """
        assert self.config.html_dir, "must give a directory for html reporting"

        # Read the status data.
        self.status.read(self.config.html_dir)

        # Check that this run used the same settings as the last run.
        m = Hasher()
        m.update(self.config)
        these_settings = m.hexdigest()
        if self.status.settings_hash() != these_settings:
            self.status.reset()
            self.status.set_settings_hash(these_settings)

        # The user may have extra CSS they want copied.
        if self.config.extra_css:
            self.extra_css = os.path.basename(self.config.extra_css)

        # Process all the files.
        self.report_files(self.html_file, morfs, self.config.html_dir)

        if not self.files:
            raise CoverageException("No data to report.")

        # Write the index file.
        self.index_file()

        self.make_local_static_report_files()

        return self.totals.pc_covered

    def make_local_static_report_files(self):
        """Make local instances of static files for HTML report."""
        # The files we provide must always be copied.
        for static, pkgdir in self.STATIC_FILES:
            shutil.copyfile(
                data_filename(static, pkgdir),
                os.path.join(self.directory, static)
                )

        # The user may have extra CSS they want copied.
        if self.extra_css:
            shutil.copyfile(
                self.config.extra_css,
                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()
        m.update(source)
        self.coverage.data.add_to_hash(fr.filename, m)
        return m.hexdigest()

    def html_file(self, fr, analysis):
        """Generate an HTML file for one source file."""
        source = fr.source()

        # Find out if the file on disk is already correct.
        flat_rootname = fr.flat_rootname()
        this_hash = self.file_hash(source.encode('utf-8'), fr)
        that_hash = self.status.file_hash(flat_rootname)
        if this_hash == that_hash:
            # Nothing has changed to require the file to be reported again.
            self.files.append(self.status.index_info(flat_rootname))
            return

        self.status.set_file_hash(flat_rootname, this_hash)

        # Get the numbers for this file.
        nums = analysis.numbers

        if self.arcs:
            missing_branch_arcs = analysis.missing_branch_arcs()

        # These classes determine which lines are highlighted by default.
        c_run = "run hide_run"
        c_exc = "exc"
        c_mis = "mis"
        c_par = "par " + c_run

        lines = []

        for lineno, line in enumerate(fr.source_token_lines(), start=1):
            # Figure out how to mark this line.
            line_class = []
            annotate_html = ""
            annotate_title = ""
            if lineno in analysis.statements:
                line_class.append("stm")
            if lineno in analysis.excluded:
                line_class.append(c_exc)
            elif lineno in analysis.missing:
                line_class.append(c_mis)
            elif self.arcs and lineno in missing_branch_arcs:
                line_class.append(c_par)
                annlines = []
                for b in missing_branch_arcs[lineno]:
                    if b < 0:
                        annlines.append("exit")
                    else:
                        annlines.append(str(b))
                annotate_html = "&nbsp;&nbsp; ".join(annlines)
                if len(annlines) > 1:
                    annotate_title = "no jumps to these line numbers"
                elif len(annlines) == 1:
                    annotate_title = "no jump to this line number"
            elif lineno in analysis.statements:
                line_class.append(c_run)

            # Build the HTML for the line
            html = []
            for tok_type, tok_text in line:
                if tok_type == "ws":
                    html.append(escape(tok_text))
                else:
                    tok_html = escape(tok_text) or '&nbsp;'
                    html.append(
                        "<span class='%s'>%s</span>" % (tok_type, tok_html)
                        )

            lines.append({
                'html': ''.join(html),
                'number': lineno,
                'class': ' '.join(line_class) or "pln",
                'annotate': annotate_html,
                'annotate_title': annotate_title,
            })

        # 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,
            'arcs': self.arcs, 'extra_css': self.extra_css,
            'fr': fr, 'nums': nums, 'lines': lines,
        }
        html = spaceless(self.source_tmpl.render(template_values))

        html_filename = flat_rootname + ".html"
        html_path = os.path.join(self.directory, html_filename)
        self.write_html(html_path, html)

        # Save this file's information for the index file.
        index_info = {
            'nums': nums,
            'html_filename': html_filename,
            'name': fr.name,
            }
        self.files.append(index_info)
        self.status.set_index_info(flat_rootname, index_info)

    def index_file(self):
        """Write the index.html file for this report."""
        index_tmpl = Templite(
            data("index.html"), self.template_globals
            )

        self.totals = sum(f['nums'] for f in self.files)

        html = index_tmpl.render({
            'arcs': self.arcs,
            'extra_css': self.extra_css,
            'files': self.files,
            'totals': self.totals,
        })

        self.write_html(
            os.path.join(self.directory, "index.html"),
            html
            )

        # Write the latest hashes for next time.
        self.status.write(self.directory)
Beispiel #34
0
class HtmlReporter(object):
    """HTML reporting."""

    # These files will be copied from the htmlfiles directory to the output
    # directory.
    STATIC_FILES = [
        ("style.css", ""),
        ("jquery.min.js", "jquery"),
        ("jquery.ba-throttle-debounce.min.js", "jquery-throttle-debounce"),
        ("jquery.hotkeys.js", "jquery-hotkeys"),
        ("jquery.isonscreen.js", "jquery-isonscreen"),
        ("jquery.tablesorter.min.js", "jquery-tablesorter"),
        ("coverage_html.js", ""),
        ("keybd_closed.png", ""),
        ("keybd_open.png", ""),
    ]

    def __init__(self, cov):
        self.coverage = cov
        self.config = self.coverage.config
        self.directory = self.config.html_dir
        title = self.config.html_title
        if env.PY2:
            title = title.decode("utf8")

        if self.config.extra_css:
            self.extra_css = os.path.basename(self.config.extra_css)
        else:
            self.extra_css = None

        self.data = self.coverage.get_data()
        self.has_arcs = self.data.has_arcs()

        self.file_summaries = []
        self.all_files_nums = []
        self.incr = IncrementalChecker(self.directory)
        self.datagen = HtmlDataGeneration(self.coverage)
        self.totals = Numbers()

        self.template_globals = {
            # Functions available in the templates.
            'escape': escape,
            'pair': pair,
            'len': len,

            # Constants for this report.
            '__url__': coverage.__url__,
            '__version__': coverage.__version__,
            'title': title,
            'time_stamp': datetime.datetime.now().strftime('%Y-%m-%d %H:%M'),
            'extra_css': self.extra_css,
            'has_arcs': self.has_arcs,
            'show_contexts': self.config.show_contexts,

            # Constants for all reports.
            # These css classes determine which lines are highlighted by default.
            'category': {
                'exc': 'exc show_exc',
                'mis': 'mis show_mis',
                'par': 'par run show_par',
                'run': 'run',
            }
        }
        self.pyfile_html_source = read_data("pyfile.html")
        self.source_tmpl = Templite(self.pyfile_html_source, self.template_globals)

    def report(self, morfs):
        """Generate an HTML report for `morfs`.

        `morfs` is a list of modules or file names.

        """
        # Read the status data and check that this run used the same
        # global data as the last run.
        self.incr.read()
        self.incr.check_global_data(self.config, self.pyfile_html_source)

        # Process all the files.
        for fr, analysis in get_analysis_to_report(self.coverage, morfs):
            self.html_file(fr, analysis)

        if not self.all_files_nums:
            raise CoverageException("No data to report.")

        self.totals = sum(self.all_files_nums)

        # Write the index file.
        self.index_file()

        self.make_local_static_report_files()
        return self.totals.n_statements and self.totals.pc_covered

    def make_local_static_report_files(self):
        """Make local instances of static files for HTML report."""
        # The files we provide must always be copied.
        for static, pkgdir in self.STATIC_FILES:
            shutil.copyfile(
                data_filename(static, pkgdir),
                os.path.join(self.directory, static)
            )

        # The user may have extra CSS they want copied.
        if self.extra_css:
            shutil.copyfile(
                self.config.extra_css,
                os.path.join(self.directory, self.extra_css)
            )

    def html_file(self, fr, analysis):
        """Generate an HTML file for one source file."""
        rootname = flat_rootname(fr.relative_filename())
        html_filename = rootname + ".html"
        ensure_dir(self.directory)
        html_path = os.path.join(self.directory, html_filename)

        # Get the numbers for this file.
        nums = analysis.numbers
        self.all_files_nums.append(nums)

        if self.config.skip_covered:
            # Don't report on 100% files.
            no_missing_lines = (nums.n_missing == 0)
            no_missing_branches = (nums.n_partial_branches == 0)
            if no_missing_lines and no_missing_branches:
                # If there's an existing file, remove it.
                file_be_gone(html_path)
                return

        if self.config.skip_empty:
            # Don't report on empty files.
            if nums.n_statements == 0:
                file_be_gone(html_path)
                return

        # Find out if the file on disk is already correct.
        if self.incr.can_skip_file(self.data, fr, rootname):
            self.file_summaries.append(self.incr.index_info(rootname))
            return

        # Write the HTML page for this file.
        file_data = self.datagen.data_for_file(fr, analysis)
        for ldata in file_data.lines:
            # Build the HTML for the line.
            html = []
            for tok_type, tok_text in ldata.tokens:
                if tok_type == "ws":
                    html.append(escape(tok_text))
                else:
                    tok_html = escape(tok_text) or '&nbsp;'
                    html.append(
                        u'<span class="{}">{}</span>'.format(tok_type, tok_html)
                    )
            ldata.html = ''.join(html)

            if ldata.short_annotations:
                # 202F is NARROW NO-BREAK SPACE.
                # 219B is RIGHTWARDS ARROW WITH STROKE.
                ldata.annotate = u",&nbsp;&nbsp; ".join(
                    u"{}&#x202F;&#x219B;&#x202F;{}".format(ldata.number, d)
                    for d in ldata.short_annotations
                    )
            else:
                ldata.annotate = None

            if ldata.long_annotations:
                longs = ldata.long_annotations
                if len(longs) == 1:
                    ldata.annotate_long = longs[0]
                else:
                    ldata.annotate_long = u"{:d} missed branches: {}".format(
                        len(longs),
                        u", ".join(
                            u"{:d}) {}".format(num, ann_long)
                            for num, ann_long in enumerate(longs, start=1)
                            ),
                    )
            else:
                ldata.annotate_long = None

            css_classes = []
            if ldata.category:
                css_classes.append(self.template_globals['category'][ldata.category])
            ldata.css_class = ' '.join(css_classes) or "pln"

        html = self.source_tmpl.render(file_data.__dict__)
        write_html(html_path, html)

        # Save this file's information for the index file.
        index_info = {
            'nums': nums,
            'html_filename': html_filename,
            'relative_filename': fr.relative_filename(),
        }
        self.file_summaries.append(index_info)
        self.incr.set_index_info(rootname, index_info)

    def index_file(self):
        """Write the index.html file for this report."""
        index_tmpl = Templite(read_data("index.html"), self.template_globals)

        html = index_tmpl.render({
            'files': self.file_summaries,
            'totals': self.totals,
        })

        write_html(os.path.join(self.directory, "index.html"), html)

        # Write the latest hashes for next time.
        self.incr.write()
Beispiel #35
0
class HtmlReporter(Reporter):
    """HTML reporting."""

    # These files will be copied from the htmlfiles dir to the output dir.
    STATIC_FILES = [
        "style.css",
        "jquery-1.4.3.min.js",
        "jquery.hotkeys.js",
        "jquery.isonscreen.js",
        "jquery.tablesorter.min.js",
        "coverage_html.js",
        "keybd_closed.png",
        "keybd_open.png",
    ]

    def __init__(self, cov, ignore_errors=False):
        super(HtmlReporter, self).__init__(cov, ignore_errors)
        self.directory = None
        self.template_globals = {
            'escape': escape,
            '__url__': coverage.__url__,
            '__version__': coverage.__version__,
        }
        self.source_tmpl = Templite(data("htmlfiles/pyfile.html"),
                                    self.template_globals)

        self.coverage = cov

        self.files = []
        self.arcs = self.coverage.data.has_arcs()
        self.status = HtmlStatus()

    def report(self, morfs, config=None):
        """Generate an HTML report for `morfs`.

        `morfs` is a list of modules or filenames.  `config` is a
        CoverageConfig instance.

        """
        assert config.html_dir, "must provide a directory for html reporting"

        # Read the status data.
        self.status.read(config.html_dir)

        # Check that this run used the same settings as the last run.
        m = Hasher()
        m.update(config)
        these_settings = m.digest()
        if self.status.settings_hash() != these_settings:
            self.status.reset()
            self.status.set_settings_hash(these_settings)

        # Process all the files.
        self.report_files(self.html_file, morfs, config, config.html_dir)

        if not self.files:
            raise CoverageException("No data to report.")

        # Write the index file.
        self.index_file()

        # Create the once-per-directory files.
        for static in self.STATIC_FILES:
            shutil.copyfile(data_filename("htmlfiles/" + static),
                            os.path.join(self.directory, static))

    def file_hash(self, source, cu):
        """Compute a hash that changes if the file needs to be re-reported."""
        m = Hasher()
        m.update(source)
        self.coverage.data.add_to_hash(cu.filename, m)
        return m.digest()

    def html_file(self, cu, analysis):
        """Generate an HTML file for one source file."""
        source_file = cu.source_file()
        try:
            source = source_file.read()
        finally:
            source_file.close()

        # Find out if the file on disk is already correct.
        flat_rootname = cu.flat_rootname()
        this_hash = self.file_hash(source, cu)
        that_hash = self.status.file_hash(flat_rootname)
        if this_hash == that_hash:
            # Nothing has changed to require the file to be reported again.
            self.files.append(self.status.index_info(flat_rootname))
            return

        self.status.set_file_hash(flat_rootname, this_hash)

        nums = analysis.numbers

        missing_branch_arcs = analysis.missing_branch_arcs()
        n_par = 0  # accumulated below.
        arcs = self.arcs

        # These classes determine which lines are highlighted by default.
        c_run = "run hide_run"
        c_exc = "exc"
        c_mis = "mis"
        c_par = "par " + c_run

        lines = []

        for lineno, line in enumerate(source_token_lines(source)):
            lineno += 1  # 1-based line numbers.
            # Figure out how to mark this line.
            line_class = []
            annotate_html = ""
            annotate_title = ""
            if lineno in analysis.statements:
                line_class.append("stm")
            if lineno in analysis.excluded:
                line_class.append(c_exc)
            elif lineno in analysis.missing:
                line_class.append(c_mis)
            elif self.arcs and lineno in missing_branch_arcs:
                line_class.append(c_par)
                n_par += 1
                annlines = []
                for b in missing_branch_arcs[lineno]:
                    if b < 0:
                        annlines.append("exit")
                    else:
                        annlines.append(str(b))
                annotate_html = "&nbsp;&nbsp; ".join(annlines)
                if len(annlines) > 1:
                    annotate_title = "no jumps to these line numbers"
                elif len(annlines) == 1:
                    annotate_title = "no jump to this line number"
            elif lineno in analysis.statements:
                line_class.append(c_run)

            # Build the HTML for the line
            html = []
            for tok_type, tok_text in line:
                if tok_type == "ws":
                    html.append(escape(tok_text))
                else:
                    tok_html = escape(tok_text) or '&nbsp;'
                    html.append("<span class='%s'>%s</span>" %
                                (tok_type, tok_html))

            lines.append({
                'html': ''.join(html),
                'number': lineno,
                'class': ' '.join(line_class) or "pln",
                'annotate': annotate_html,
                'annotate_title': annotate_title,
            })

        # Write the HTML page for this file.
        html_filename = flat_rootname + ".html"
        html_path = os.path.join(self.directory, html_filename)
        html = spaceless(self.source_tmpl.render(locals()))
        fhtml = open(html_path, 'w')
        try:
            fhtml.write(html)
        finally:
            fhtml.close()

        # Save this file's information for the index file.
        index_info = {
            'nums': nums,
            'par': n_par,
            'html_filename': html_filename,
            'name': cu.name,
        }
        self.files.append(index_info)
        self.status.set_index_info(flat_rootname, index_info)

    def index_file(self):
        """Write the index.html file for this report."""
        index_tmpl = Templite(data("htmlfiles/index.html"),
                              self.template_globals)

        files = self.files
        arcs = self.arcs

        totals = sum([f['nums'] for f in files])

        fhtml = open(os.path.join(self.directory, "index.html"), "w")
        try:
            fhtml.write(index_tmpl.render(locals()))
        finally:
            fhtml.close()

        # Write the latest hashes for next time.
        self.status.write(self.directory)
Beispiel #36
0
 def try_render(self, text, ctx, result):
     """Render `text` through `ctx`, and it had better be `result`."""
     self.assertEqual(Templite(text).render(ctx), result)
Beispiel #37
0
class HtmlReporter(Reporter):
    """HTML reporting."""

    # These files will be copied from the htmlfiles directory to the output
    # directory.
    STATIC_FILES = [
        ("style.css", ""),
        ("jquery.min.js", "jquery"),
        ("jquery.ba-throttle-debounce.min.js", "jquery-throttle-debounce"),
        ("jquery.hotkeys.js", "jquery-hotkeys"),
        ("jquery.isonscreen.js", "jquery-isonscreen"),
        ("jquery.tablesorter.min.js", "jquery-tablesorter"),
        ("coverage_html.js", ""),
        ("keybd_closed.png", ""),
        ("keybd_open.png", ""),
    ]

    def __init__(self, cov, config):
        super(HtmlReporter, self).__init__(cov, config)
        self.directory = None
        title = self.config.html_title
        if env.PY2:
            title = title.decode("utf8")
        self.template_globals = {
            'escape': escape,
            'pair': pair,
            'title': title,
            '__url__': coverage.__url__,
            '__version__': coverage.__version__,
        }
        self.source_tmpl = Templite(read_data("pyfile.html"), self.template_globals)

        self.coverage = cov

        self.files = []
        self.has_arcs = self.coverage.data.has_arcs()
        self.status = HtmlStatus()
        self.extra_css = None
        self.totals = Numbers()
        self.time_stamp = datetime.datetime.now().strftime('%Y-%m-%d %H:%M')

    def report(self, morfs):
        """Generate an HTML report for `morfs`.

        `morfs` is a list of modules or file names.

        """
        assert self.config.html_dir, "must give a directory for html reporting"

        # Read the status data.
        self.status.read(self.config.html_dir)

        # Check that this run used the same settings as the last run.
        m = Hasher()
        m.update(self.config)
        these_settings = m.hexdigest()
        if self.status.settings_hash() != these_settings:
            self.status.reset()
            self.status.set_settings_hash(these_settings)

        # The user may have extra CSS they want copied.
        if self.config.extra_css:
            self.extra_css = os.path.basename(self.config.extra_css)

        # Process all the files.
        self.report_files(self.html_file, morfs, self.config.html_dir)

        if not self.files:
            raise CoverageException("No data to report.")

        # Write the index file.
        self.index_file()

        self.make_local_static_report_files()
        return self.totals.n_statements and self.totals.pc_covered

    def make_local_static_report_files(self):
        """Make local instances of static files for HTML report."""
        # The files we provide must always be copied.
        for static, pkgdir in self.STATIC_FILES:
            shutil.copyfile(
                data_filename(static, pkgdir),
                os.path.join(self.directory, static)
            )

        # The user may have extra CSS they want copied.
        if self.extra_css:
            shutil.copyfile(
                self.config.extra_css,
                os.path.join(self.directory, self.extra_css)
            )

    def file_hash(self, source, fr):
        """Compute a hash that changes if the file needs to be re-reported."""
        m = Hasher()
        m.update(source)
        self.coverage.data.add_to_hash(fr.filename, m)
        return m.hexdigest()

    def html_file(self, fr, analysis):
        """Generate an HTML file for one source file."""
        source = fr.source()

        # Find out if the file on disk is already correct.
        rootname = flat_rootname(fr.relative_filename())
        this_hash = self.file_hash(source.encode('utf-8'), fr)
        that_hash = self.status.file_hash(rootname)
        if this_hash == that_hash:
            # Nothing has changed to require the file to be reported again.
            self.files.append(self.status.index_info(rootname))
            return

        self.status.set_file_hash(rootname, this_hash)

        # Get the numbers for this file.
        nums = analysis.numbers

        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"
        c_exc = "exc"
        c_mis = "mis"
        c_par = "par " + c_run

        lines = []

        for lineno, line in enumerate(fr.source_token_lines(), start=1):
            # Figure out how to mark this line.
            line_class = []
            annotate_html = ""
            annotate_long = ""
            if lineno in analysis.statements:
                line_class.append("stm")
            if lineno in analysis.excluded:
                line_class.append(c_exc)
            elif lineno in analysis.missing:
                line_class.append(c_mis)
            elif self.has_arcs and lineno in missing_branch_arcs:
                line_class.append(c_par)
                shorts = []
                longs = []
                for b in missing_branch_arcs[lineno]:
                    if b < 0:
                        shorts.append("exit")
                    else:
                        shorts.append(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)

                if len(longs) == 1:
                    annotate_long = longs[0]
                else:
                    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)

            # Build the HTML for the line.
            html = []
            for tok_type, tok_text in line:
                if tok_type == "ws":
                    html.append(escape(tok_text))
                else:
                    tok_html = escape(tok_text) or '&nbsp;'
                    html.append(
                        '<span class="%s">%s</span>' % (tok_type, tok_html)
                    )

            lines.append({
                'html': ''.join(html),
                'number': lineno,
                'class': ' '.join(line_class) or "pln",
                'annotate': annotate_html,
                'annotate_long': annotate_long,
            })

        # Write the HTML page for this file.
        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_filename = rootname + ".html"
        html_path = os.path.join(self.directory, html_filename)
        write_html(html_path, html)

        # Save this file's information for the index file.
        index_info = {
            'nums': nums,
            'html_filename': html_filename,
            'relative_filename': fr.relative_filename(),
        }
        self.files.append(index_info)
        self.status.set_index_info(rootname, index_info)

    def index_file(self):
        """Write the index.html file for this report."""
        index_tmpl = Templite(read_data("index.html"), self.template_globals)

        self.totals = sum(f['nums'] for f in self.files)

        html = index_tmpl.render({
            'has_arcs': self.has_arcs,
            'extra_css': self.extra_css,
            'files': self.files,
            'totals': self.totals,
            'time_stamp': self.time_stamp,
        })

        write_html(os.path.join(self.directory, "index.html"), html)

        # Write the latest hashes for next time.
        self.status.write(self.directory)
Beispiel #38
0
class HtmlReporter(Reporter):
    """HTML reporting."""
    STATIC_FILES = [
        'style.css', 'jquery.min.js', 'jquery.hotkeys.js',
        'jquery.isonscreen.js', 'jquery.tablesorter.min.js',
        'coverage_html.js', 'keybd_closed.png', 'keybd_open.png'
    ]

    def __init__(self, cov, config):
        super(HtmlReporter, self).__init__(cov, config)
        self.directory = None
        self.template_globals = {
            'escape': escape,
            'title': self.config.html_title,
            '__url__': coverage.__url__,
            '__version__': coverage.__version__
        }
        self.source_tmpl = Templite(data('pyfile.html'), self.template_globals)
        self.coverage = cov
        self.files = []
        self.arcs = self.coverage.data.has_arcs()
        self.status = HtmlStatus()
        self.extra_css = None
        self.totals = Numbers()

    def report(self, morfs):
        """Generate an HTML report for `morfs`.
        
        `morfs` is a list of modules or filenames.
        
        """
        self.status.read(self.config.html_dir)
        m = Hasher()
        m.update(self.config)
        these_settings = m.digest()
        if self.status.settings_hash() != these_settings:
            self.status.reset()
            self.status.set_settings_hash(these_settings)
        if self.config.extra_css:
            self.extra_css = os.path.basename(self.config.extra_css)
        self.report_files(self.html_file, morfs, self.config.html_dir)
        if not self.files:
            raise CoverageException('No data to report.')
        self.index_file()
        self.make_local_static_report_files()
        return self.totals.pc_covered

    def make_local_static_report_files(self):
        """Make local instances of static files for HTML report."""
        for static in self.STATIC_FILES:
            shutil.copyfile(data_filename(static),
                            os.path.join(self.directory, static))

        if self.extra_css:
            shutil.copyfile(self.config.extra_css,
                            os.path.join(self.directory, self.extra_css))

    def write_html(self, fname, html):
        """Write `html` to `fname`, properly encoded."""
        fout = open(fname, 'wb')
        try:
            fout.write(html.encode('ascii', 'xmlcharrefreplace'))
        finally:
            fout.close()

    def file_hash(self, source, cu):
        """Compute a hash that changes if the file needs to be re-reported."""
        m = Hasher()
        m.update(source)
        self.coverage.data.add_to_hash(cu.filename, m)
        return m.digest()

    def html_file(self, cu, analysis):
        """Generate an HTML file for one source file."""
        source_file = cu.source_file()
        try:
            source = source_file.read()
        finally:
            source_file.close()

        flat_rootname = cu.flat_rootname()
        this_hash = self.file_hash(source, cu)
        that_hash = self.status.file_hash(flat_rootname)
        if this_hash == that_hash:
            self.files.append(self.status.index_info(flat_rootname))
            return
        self.status.set_file_hash(flat_rootname, this_hash)
        if sys.version_info < (3, 0):
            encoding = source_encoding(source)
            if encoding.startswith('utf-8') and source[:3] == '\xef\xbb\xbf':
                source = source[3:]
                encoding = 'utf-8'
        nums = analysis.numbers
        missing_branch_arcs = analysis.missing_branch_arcs()
        c_run = 'run hide_run'
        c_exc = 'exc'
        c_mis = 'mis'
        c_par = 'par ' + c_run
        lines = []
        for lineno, line in enumerate(source_token_lines(source)):
            lineno += 1
            line_class = []
            annotate_html = ''
            annotate_title = ''
            if lineno in analysis.statements:
                line_class.append('stm')
            if lineno in analysis.excluded:
                line_class.append(c_exc)
            elif lineno in analysis.missing:
                line_class.append(c_mis)
            elif self.arcs and lineno in missing_branch_arcs:
                line_class.append(c_par)
                annlines = []
                for b in missing_branch_arcs[lineno]:
                    if b < 0:
                        annlines.append('exit')
                    else:
                        annlines.append(str(b))

                annotate_html = '&nbsp;&nbsp; '.join(annlines)
                if len(annlines) > 1:
                    annotate_title = 'no jumps to these line numbers'
                elif len(annlines) == 1:
                    annotate_title = 'no jump to this line number'
            elif lineno in analysis.statements:
                line_class.append(c_run)
            html = []
            for tok_type, tok_text in line:
                if tok_type == 'ws':
                    html.append(escape(tok_text))
                else:
                    tok_html = escape(tok_text) or '&nbsp;'
                    html.append("<span class='%s'>%s</span>" %
                                (tok_type, tok_html))

            lines.append({
                'html': ''.join(html),
                'number': lineno,
                'class': ' '.join(line_class) or 'pln',
                'annotate': annotate_html,
                'annotate_title': annotate_title
            })

        html = spaceless(
            self.source_tmpl.render({
                'c_exc': c_exc,
                'c_mis': c_mis,
                'c_par': c_par,
                'c_run': c_run,
                'arcs': self.arcs,
                'extra_css': self.extra_css,
                'cu': cu,
                'nums': nums,
                'lines': lines
            }))
        if sys.version_info < (3, 0):
            html = html.decode(encoding)
        html_filename = flat_rootname + '.html'
        html_path = os.path.join(self.directory, html_filename)
        self.write_html(html_path, html)
        index_info = {
            'nums': nums,
            'html_filename': html_filename,
            'name': cu.name
        }
        self.files.append(index_info)
        self.status.set_index_info(flat_rootname, index_info)

    def index_file(self):
        """Write the index.html file for this report."""
        index_tmpl = Templite(data('index.html'), self.template_globals)
        self.totals = sum([f['nums'] for f in self.files])
        html = index_tmpl.render({
            'arcs': self.arcs,
            'extra_css': self.extra_css,
            'files': self.files,
            'totals': self.totals
        })
        if sys.version_info < (3, 0):
            html = html.decode('utf-8')
        self.write_html(os.path.join(self.directory, 'index.html'), html)
        self.status.write(self.directory)
Beispiel #39
0
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(data("htmlfiles/pyfile.html"), globals())

        self.files = []
        self.arcs = coverage.data.has_arcs()

    def report(self, morfs, config=None):
        """Generate an HTML report for `morfs`.

        `morfs` is a list of modules or filenames.  `config` is a
        CoverageConfig instance.

        """
        assert config.html_dir, "must provide a directory for html reporting"

        # Process all the files.
        self.report_files(self.html_file, morfs, config, config.html_dir)

        if not self.files:
            raise CoverageException("No data to report.")

        # Write the index file.
        self.index_file()

        # Create the once-per-directory files.
        for static in ["style.css", "coverage_html.js", "jquery-1.3.2.min.js", "jquery.tablesorter.min.js"]:
            shutil.copyfile(data_filename("htmlfiles/" + static), os.path.join(self.directory, static))

    def html_file(self, cu, analysis):
        """Generate an HTML file for one source file."""

        source = cu.source_file().read()

        nums = analysis.numbers

        missing_branch_arcs = analysis.missing_branch_arcs()
        n_par = 0  # accumulated below.
        arcs = self.arcs

        # These classes determine which lines are highlighted by default.
        c_run = "run hide_run"
        c_exc = "exc"
        c_mis = "mis"
        c_par = "par " + c_run

        lines = []

        for lineno, line in enumerate(source_token_lines(source)):
            lineno += 1  # 1-based line numbers.
            # Figure out how to mark this line.
            line_class = []
            annotate_html = ""
            annotate_title = ""
            if lineno in analysis.statements:
                line_class.append("stm")
            if lineno in analysis.excluded:
                line_class.append(c_exc)
            elif lineno in analysis.missing:
                line_class.append(c_mis)
            elif self.arcs and lineno in missing_branch_arcs:
                line_class.append(c_par)
                n_par += 1
                annlines = []
                for b in missing_branch_arcs[lineno]:
                    if b < 0:
                        annlines.append("exit")
                    else:
                        annlines.append(str(b))
                annotate_html = "&nbsp;&nbsp; ".join(annlines)
                if len(annlines) > 1:
                    annotate_title = "no jumps to these line numbers"
                elif len(annlines) == 1:
                    annotate_title = "no jump to this line number"
            elif lineno in analysis.statements:
                line_class.append(c_run)

            # Build the HTML for the line
            html = []
            for tok_type, tok_text in line:
                if tok_type == "ws":
                    html.append(escape(tok_text))
                else:
                    tok_html = escape(tok_text) or "&nbsp;"
                    html.append("<span class='%s'>%s</span>" % (tok_type, tok_html))

            lines.append(
                {
                    "html": "".join(html),
                    "number": lineno,
                    "class": " ".join(line_class) or "pln",
                    "annotate": annotate_html,
                    "annotate_title": annotate_title,
                }
            )

        # Write the HTML page for this file.
        html_filename = cu.flat_rootname() + ".html"
        html_path = os.path.join(self.directory, html_filename)
        html = spaceless(self.source_tmpl.render(locals()))
        fhtml = open(html_path, "w")
        fhtml.write(html)
        fhtml.close()

        # Save this file's information for the index file.
        self.files.append({"nums": nums, "par": n_par, "html_filename": html_filename, "cu": cu})

    def index_file(self):
        """Write the index.html file for this report."""
        index_tmpl = Templite(data("htmlfiles/index.html"), globals())

        files = self.files
        arcs = self.arcs

        totals = sum([f["nums"] for f in files])

        fhtml = open(os.path.join(self.directory, "index.html"), "w")
        fhtml.write(index_tmpl.render(locals()))
        fhtml.close()
Beispiel #40
0
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(data("htmlfiles/pyfile.html"), globals())
        
        self.files = []
        self.arcs = coverage.data.has_arcs()

    def report(self, morfs, directory, omit_prefixes=None):
        """Generate an HTML report for `morfs`.
        
        `morfs` is a list of modules or filenames.  `directory` is where to put
        the HTML files. `omit_prefixes` is a list of strings, prefixes of
        modules to omit from the report.
        
        """
        assert directory, "must provide a directory for html reporting"
        
        # Process all the files.
        self.report_files(self.html_file, morfs, directory, omit_prefixes)

        # Write the index file.
        self.index_file()

        # Create the once-per-directory files.
        for static in [
            "style.css", "coverage_html.js",
            "jquery-1.3.2.min.js", "jquery.tablesorter.min.js"
            ]:
            shutil.copyfile(
                data_filename("htmlfiles/" + static),
                os.path.join(directory, static)
                )

    def html_file(self, cu, analysis):
        """Generate an HTML file for one source file."""
        
        source = cu.source_file().read()

        nums = analysis.numbers        

        missing_branch_arcs = analysis.missing_branch_arcs()
        n_par = 0   # accumulated below.
        arcs = self.arcs

        # These classes determine which lines are highlighted by default.
        c_run = " run hide_run"
        c_exc = " exc"
        c_mis = " mis"
        c_par = " par" + c_run

        lines = []
        
        for lineno, line in enumerate(source_token_lines(source)):
            lineno += 1     # 1-based line numbers.
            # Figure out how to mark this line.
            line_class = ""
            annotate_html = ""
            annotate_title = ""
            if lineno in analysis.statements:
                line_class += " stm"
            if lineno in analysis.excluded:
                line_class += c_exc
            elif lineno in analysis.missing:
                line_class += c_mis
            elif self.arcs and lineno in missing_branch_arcs:
                line_class += c_par
                n_par += 1
                annlines = []
                for b in missing_branch_arcs[lineno]:
                    if b == -1:
                        annlines.append("exit")
                    else:
                        annlines.append(str(b))
                annotate_html = "&nbsp;&nbsp; ".join(annlines)
                if len(annlines) > 1:
                    annotate_title = "no jumps to these line numbers"
                elif len(annlines) == 1:
                    annotate_title = "no jump to this line number"
            elif lineno in analysis.statements:
                line_class += c_run
            
            # Build the HTML for the line
            html = ""
            for tok_type, tok_text in line:
                if tok_type == "ws":
                    html += escape(tok_text)
                else:
                    tok_html = escape(tok_text) or '&nbsp;'
                    html += "<span class='%s'>%s</span>" % (tok_type, tok_html)

            lines.append({
                'html': html,
                'number': lineno,
                'class': line_class.strip() or "pln",
                'annotate': annotate_html,
                'annotate_title': annotate_title,
            })

        # Write the HTML page for this file.
        html_filename = cu.flat_rootname() + ".html"
        html_path = os.path.join(self.directory, html_filename)
        html = spaceless(self.source_tmpl.render(locals()))
        fhtml = open(html_path, 'w')
        fhtml.write(html)
        fhtml.close()

        # Save this file's information for the index file.
        self.files.append({
            'nums': nums,
            'par': n_par,
            'html_filename': html_filename,
            'cu': cu,
            })

    def index_file(self):
        """Write the index.html file for this report."""
        index_tmpl = Templite(data("htmlfiles/index.html"), globals())

        files = self.files
        arcs = self.arcs

        totals = sum([f['nums'] for f in files])

        fhtml = open(os.path.join(self.directory, "index.html"), "w")
        fhtml.write(index_tmpl.render(locals()))
        fhtml.close()
Beispiel #41
0
class HtmlReporter(Reporter):
    """HTML reporting."""

    # These files will be copied from the htmlfiles directory to the output
    # directory.
    STATIC_FILES = [
        ("style.css", ""),
        ("jquery.min.js", "jquery"),
        ("jquery.debounce.min.js", "jquery-debounce"),
        ("jquery.hotkeys.js", "jquery-hotkeys"),
        ("jquery.isonscreen.js", "jquery-isonscreen"),
        ("jquery.tablesorter.min.js", "jquery-tablesorter"),
        ("coverage_html.js", ""),
        ("keybd_closed.png", ""),
        ("keybd_open.png", ""),
    ]

    def __init__(self, cov, config):
        super(HtmlReporter, self).__init__(cov, config)
        self.directory = None
        title = self.config.html_title
        if env.PY2:
            title = title.decode("utf8")
        self.template_globals = {
            "escape": escape,
            "pair": pair,
            "title": title,
            "__url__": coverage.__url__,
            "__version__": coverage.__version__,
        }
        self.source_tmpl = Templite(data("pyfile.html"), self.template_globals)

        self.coverage = cov

        self.files = []
        self.has_arcs = self.coverage.data.has_arcs()
        self.status = HtmlStatus()
        self.extra_css = None
        self.totals = Numbers()
        self.time_stamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")

    def report(self, morfs):
        """Generate an HTML report for `morfs`.

        `morfs` is a list of modules or file names.

        """
        assert self.config.html_dir, "must give a directory for html reporting"

        # Read the status data.
        self.status.read(self.config.html_dir)

        # Check that this run used the same settings as the last run.
        m = Hasher()
        m.update(self.config)
        these_settings = m.hexdigest()
        if self.status.settings_hash() != these_settings:
            self.status.reset()
            self.status.set_settings_hash(these_settings)

        # The user may have extra CSS they want copied.
        if self.config.extra_css:
            self.extra_css = os.path.basename(self.config.extra_css)

        # Process all the files.
        self.report_files(self.html_file, morfs, self.config.html_dir)

        if not self.files:
            raise CoverageException("No data to report.")

        # Write the index file.
        self.index_file()

        self.make_local_static_report_files()
        return self.totals.n_statements and self.totals.pc_covered

    def make_local_static_report_files(self):
        """Make local instances of static files for HTML report."""
        # The files we provide must always be copied.
        for static, pkgdir in self.STATIC_FILES:
            shutil.copyfile(data_filename(static, pkgdir), os.path.join(self.directory, static))

        # The user may have extra CSS they want copied.
        if self.extra_css:
            shutil.copyfile(self.config.extra_css, 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()
        m.update(source)
        self.coverage.data.add_to_hash(fr.filename, m)
        return m.hexdigest()

    def html_file(self, fr, analysis):
        """Generate an HTML file for one source file."""
        source = fr.source()

        # Find out if the file on disk is already correct.
        rootname = flat_rootname(fr.relative_filename())
        this_hash = self.file_hash(source.encode("utf-8"), fr)
        that_hash = self.status.file_hash(rootname)
        if this_hash == that_hash:
            # Nothing has changed to require the file to be reported again.
            self.files.append(self.status.index_info(rootname))
            return

        self.status.set_file_hash(rootname, this_hash)

        # Get the numbers for this file.
        nums = analysis.numbers

        if self.has_arcs:
            missing_branch_arcs = analysis.missing_branch_arcs()

        # These classes determine which lines are highlighted by default.
        c_run = "run hide_run"
        c_exc = "exc"
        c_mis = "mis"
        c_par = "par " + c_run

        lines = []

        for lineno, line in enumerate(fr.source_token_lines(), start=1):
            # Figure out how to mark this line.
            line_class = []
            annotate_html = ""
            annotate_title = ""
            if lineno in analysis.statements:
                line_class.append("stm")
            if lineno in analysis.excluded:
                line_class.append(c_exc)
            elif lineno in analysis.missing:
                line_class.append(c_mis)
            elif self.has_arcs and lineno in missing_branch_arcs:
                line_class.append(c_par)
                shorts = []
                longs = []
                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)
                # 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]
                else:
                    annotate_title += ", ".join(longs[:-1]) + ", or " + longs[-1]
            elif lineno in analysis.statements:
                line_class.append(c_run)

            # Build the HTML for the line.
            html = []
            for tok_type, tok_text in line:
                if tok_type == "ws":
                    html.append(escape(tok_text))
                else:
                    tok_html = escape(tok_text) or "&nbsp;"
                    html.append('<span class="%s">%s</span>' % (tok_type, tok_html))

            lines.append(
                {
                    "html": "".join(html),
                    "number": lineno,
                    "class": " ".join(line_class) or "pln",
                    "annotate": annotate_html,
                    "annotate_title": annotate_title,
                }
            )

        # 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,
            "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)

        # Save this file's information for the index file.
        index_info = {"nums": nums, "html_filename": html_filename, "relative_filename": fr.relative_filename()}
        self.files.append(index_info)
        self.status.set_index_info(rootname, index_info)

    def index_file(self):
        """Write the index.html file for this report."""
        index_tmpl = Templite(data("index.html"), self.template_globals)

        self.totals = sum(f["nums"] for f in self.files)

        html = index_tmpl.render(
            {
                "has_arcs": self.has_arcs,
                "extra_css": self.extra_css,
                "files": self.files,
                "totals": self.totals,
                "time_stamp": self.time_stamp,
            }
        )

        self.write_html(os.path.join(self.directory, "index.html"), html)

        # Write the latest hashes for next time.
        self.status.write(self.directory)
Beispiel #42
0
class HtmlReporter(Reporter):
    """HTML reporting."""

    # These files will be copied from the htmlfiles dir to the output dir.
    STATIC_FILES = [
            "style.css",
            "jquery-1.4.3.min.js",
            "jquery.hotkeys.js",
            "jquery.isonscreen.js",
            "jquery.tablesorter.min.js",
            "coverage_html.js",
            "keybd_closed.png",
            "keybd_open.png",
            ]

    def __init__(self, cov, config):
        super(HtmlReporter, self).__init__(cov, config)
        self.directory = None
        self.template_globals = {
            'escape': escape,
            '__url__': coverage.__url__,
            '__version__': coverage.__version__,
            }
        self.source_tmpl = Templite(
            data("htmlfiles/pyfile.html"), self.template_globals
            )

        self.coverage = cov

        self.files = []
        self.arcs = self.coverage.data.has_arcs()
        self.status = HtmlStatus()
        self.extra_css = None

    def report(self, morfs):
        """Generate an HTML report for `morfs`.

        `morfs` is a list of modules or filenames.

        """
        assert self.config.html_dir, "must give a directory for html reporting"

        # Read the status data.
        self.status.read(self.config.html_dir)

        # Check that this run used the same settings as the last run.
        m = Hasher()
        m.update(self.config)
        these_settings = m.digest()
        if self.status.settings_hash() != these_settings:
            self.status.reset()
            self.status.set_settings_hash(these_settings)

        # The user may have extra CSS they want copied.
        if self.config.extra_css:
            self.extra_css = os.path.basename(self.config.extra_css)

        # Process all the files.
        self.report_files(self.html_file, morfs, self.config.html_dir)

        if not self.files:
            raise CoverageException("No data to report.")

        # Write the index file.
        self.index_file()

        self.make_local_static_report_files()

    def make_local_static_report_files(self):
        """Make local instances of static files for HTML report."""
        # The files we provide must always be copied.
        for static in self.STATIC_FILES:
            shutil.copyfile(
                data_filename("htmlfiles/" + static),
                os.path.join(self.directory, static)
                )

        # The user may have extra CSS they want copied.
        if self.extra_css:
            shutil.copyfile(
                self.config.extra_css,
                os.path.join(self.directory, self.extra_css)
                )

    def write_html(self, fname, html):
        """Write `html` to `fname`, properly encoded."""
        fout = open(fname, "wb")
        try:
            fout.write(html.encode('ascii', 'xmlcharrefreplace'))
        finally:
            fout.close()

    def file_hash(self, source, cu):
        """Compute a hash that changes if the file needs to be re-reported."""
        m = Hasher()
        m.update(source)
        self.coverage.data.add_to_hash(cu.filename, m)
        return m.digest()

    def html_file(self, cu, analysis):
        """Generate an HTML file for one source file."""
        source_file = cu.source_file()
        try:
            source = source_file.read()
        finally:
            source_file.close()

        # Find out if the file on disk is already correct.
        flat_rootname = cu.flat_rootname()
        this_hash = self.file_hash(source, cu)
        that_hash = self.status.file_hash(flat_rootname)
        if this_hash == that_hash:
            # Nothing has changed to require the file to be reported again.
            self.files.append(self.status.index_info(flat_rootname))
            return

        self.status.set_file_hash(flat_rootname, this_hash)

        # If need be, determine the encoding of the source file. We use it
        # later to properly write the HTML.
        if sys.version_info < (3, 0):
            encoding = source_encoding(source)
            # Some UTF8 files have the dreaded UTF8 BOM. If so, junk it.
            if encoding.startswith("utf-8") and source[:3] == "\xef\xbb\xbf":
                source = source[3:]
                encoding = "utf-8"

        # Get the numbers for this file.
        nums = analysis.numbers

        missing_branch_arcs = analysis.missing_branch_arcs()
        n_par = 0   # accumulated below.
        arcs = self.arcs

        # These classes determine which lines are highlighted by default.
        c_run = "run hide_run"
        c_exc = "exc"
        c_mis = "mis"
        c_par = "par " + c_run

        lines = []

        for lineno, line in enumerate(source_token_lines(source)):
            lineno += 1     # 1-based line numbers.
            # Figure out how to mark this line.
            line_class = []
            annotate_html = ""
            annotate_title = ""
            if lineno in analysis.statements:
                line_class.append("stm")
            if lineno in analysis.excluded:
                line_class.append(c_exc)
            elif lineno in analysis.missing:
                line_class.append(c_mis)
            elif self.arcs and lineno in missing_branch_arcs:
                line_class.append(c_par)
                n_par += 1
                annlines = []
                for b in missing_branch_arcs[lineno]:
                    if b < 0:
                        annlines.append("exit")
                    else:
                        annlines.append(str(b))
                annotate_html = "&nbsp;&nbsp; ".join(annlines)
                if len(annlines) > 1:
                    annotate_title = "no jumps to these line numbers"
                elif len(annlines) == 1:
                    annotate_title = "no jump to this line number"
            elif lineno in analysis.statements:
                line_class.append(c_run)

            # Build the HTML for the line
            html = []
            for tok_type, tok_text in line:
                if tok_type == "ws":
                    html.append(escape(tok_text))
                else:
                    tok_html = escape(tok_text) or '&nbsp;'
                    html.append(
                        "<span class='%s'>%s</span>" % (tok_type, tok_html)
                        )

            lines.append({
                'html': ''.join(html),
                'number': lineno,
                'class': ' '.join(line_class) or "pln",
                'annotate': annotate_html,
                'annotate_title': annotate_title,
            })

        # Write the HTML page for this file.
        html_filename = flat_rootname + ".html"
        html_path = os.path.join(self.directory, html_filename)
        extra_css = self.extra_css

        html = spaceless(self.source_tmpl.render(locals()))
        if sys.version_info < (3, 0):
            html = html.decode(encoding)
        self.write_html(html_path, html)

        # Save this file's information for the index file.
        index_info = {
            'nums': nums,
            'par': n_par,
            'html_filename': html_filename,
            'name': cu.name,
            }
        self.files.append(index_info)
        self.status.set_index_info(flat_rootname, index_info)

    def index_file(self):
        """Write the index.html file for this report."""
        index_tmpl = Templite(
            data("htmlfiles/index.html"), self.template_globals
            )

        files = self.files
        arcs = self.arcs

        totals = sum([f['nums'] for f in files])
        extra_css = self.extra_css

        self.write_html(
            os.path.join(self.directory, "index.html"),
            index_tmpl.render(locals())
            )

        # Write the latest hashes for next time.
        self.status.write(self.directory)
Beispiel #43
0
class HtmlReporter(Reporter):
    """HTML reporting."""

    # These files will be copied from the htmlfiles directory to the output
    # directory.
    STATIC_FILES = [
        ("style.css", ""),
        ("jquery.min.js", "jquery"),
        ("jquery.debounce.min.js", "jquery-debounce"),
        ("jquery.hotkeys.js", "jquery-hotkeys"),
        ("jquery.isonscreen.js", "jquery-isonscreen"),
        ("jquery.tablesorter.min.js", "jquery-tablesorter"),
        ("coverage_html.js", ""),
        ("keybd_closed.png", ""),
        ("keybd_open.png", ""),
    ]

    def __init__(self, cov, config):
        super(HtmlReporter, self).__init__(cov, config)
        self.directory = None
        title = self.config.html_title
        if env.PY2:
            title = title.decode("utf8")
        self.template_globals = {
            'escape': escape,
            'pair': pair,
            'title': title,
            '__url__': coverage.__url__,
            '__version__': coverage.__version__,
        }
        self.source_tmpl = Templite(read_data("pyfile.html"),
                                    self.template_globals)

        self.coverage = cov

        self.files = []
        self.has_arcs = self.coverage.data.has_arcs()
        self.status = HtmlStatus()
        self.extra_css = None
        self.totals = Numbers()
        self.time_stamp = datetime.datetime.now().strftime('%Y-%m-%d %H:%M')

    def report(self, morfs):
        """Generate an HTML report for `morfs`.

        `morfs` is a list of modules or file names.

        """
        assert self.config.html_dir, "must give a directory for html reporting"

        # Read the status data.
        self.status.read(self.config.html_dir)

        # Check that this run used the same settings as the last run.
        m = Hasher()
        m.update(self.config)
        these_settings = m.hexdigest()
        if self.status.settings_hash() != these_settings:
            self.status.reset()
            self.status.set_settings_hash(these_settings)

        # The user may have extra CSS they want copied.
        if self.config.extra_css:
            self.extra_css = os.path.basename(self.config.extra_css)

        # Process all the files.
        self.report_files(self.html_file, morfs, self.config.html_dir)

        if not self.files:
            raise CoverageException("No data to report.")

        # Write the index file.
        self.index_file()

        self.make_local_static_report_files()
        return self.totals.n_statements and self.totals.pc_covered

    def make_local_static_report_files(self):
        """Make local instances of static files for HTML report."""
        # The files we provide must always be copied.
        for static, pkgdir in self.STATIC_FILES:
            shutil.copyfile(data_filename(static, pkgdir),
                            os.path.join(self.directory, static))

        # The user may have extra CSS they want copied.
        if self.extra_css:
            shutil.copyfile(self.config.extra_css,
                            os.path.join(self.directory, self.extra_css))

    def file_hash(self, source, fr):
        """Compute a hash that changes if the file needs to be re-reported."""
        m = Hasher()
        m.update(source)
        self.coverage.data.add_to_hash(fr.filename, m)
        return m.hexdigest()

    def html_file(self, fr, analysis):
        """Generate an HTML file for one source file."""
        source = fr.source()

        # Find out if the file on disk is already correct.
        rootname = flat_rootname(fr.relative_filename())
        this_hash = self.file_hash(source.encode('utf-8'), fr)
        that_hash = self.status.file_hash(rootname)
        if this_hash == that_hash:
            # Nothing has changed to require the file to be reported again.
            self.files.append(self.status.index_info(rootname))
            return

        self.status.set_file_hash(rootname, this_hash)

        # Get the numbers for this file.
        nums = analysis.numbers

        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"
        c_exc = "exc"
        c_mis = "mis"
        c_par = "par " + c_run

        lines = []

        for lineno, line in enumerate(fr.source_token_lines(), start=1):
            # Figure out how to mark this line.
            line_class = []
            annotate_html = ""
            annotate_long = ""
            if lineno in analysis.statements:
                line_class.append("stm")
            if lineno in analysis.excluded:
                line_class.append(c_exc)
            elif lineno in analysis.missing:
                line_class.append(c_mis)
            elif self.has_arcs and lineno in missing_branch_arcs:
                line_class.append(c_par)
                shorts = []
                longs = []
                for b in missing_branch_arcs[lineno]:
                    if b < 0:
                        shorts.append("exit")
                    else:
                        shorts.append(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)

                if len(longs) == 1:
                    annotate_long = longs[0]
                else:
                    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)

            # Build the HTML for the line.
            html = []
            for tok_type, tok_text in line:
                if tok_type == "ws":
                    html.append(escape(tok_text))
                else:
                    tok_html = escape(tok_text) or '&nbsp;'
                    html.append('<span class="%s">%s</span>' %
                                (tok_type, tok_html))

            lines.append({
                'html': ''.join(html),
                'number': lineno,
                'class': ' '.join(line_class) or "pln",
                'annotate': annotate_html,
                'annotate_long': annotate_long,
            })

        # Write the HTML page for this file.
        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_filename = rootname + ".html"
        html_path = os.path.join(self.directory, html_filename)
        write_html(html_path, html)

        # Save this file's information for the index file.
        index_info = {
            'nums': nums,
            'html_filename': html_filename,
            'relative_filename': fr.relative_filename(),
        }
        self.files.append(index_info)
        self.status.set_index_info(rootname, index_info)

    def index_file(self):
        """Write the index.html file for this report."""
        index_tmpl = Templite(read_data("index.html"), self.template_globals)

        self.totals = sum(f['nums'] for f in self.files)

        html = index_tmpl.render({
            'has_arcs': self.has_arcs,
            'extra_css': self.extra_css,
            'files': self.files,
            'totals': self.totals,
            'time_stamp': self.time_stamp,
        })

        write_html(os.path.join(self.directory, "index.html"), html)

        # Write the latest hashes for next time.
        self.status.write(self.directory)
Beispiel #44
0
class HtmlReporter:
    """HTML reporting."""

    # These files will be copied from the htmlfiles directory to the output
    # directory.
    STATIC_FILES = [
        "style.css",
        "coverage_html.js",
        "keybd_closed.png",
        "keybd_open.png",
        "favicon_32.png",
    ]

    def __init__(self, cov):
        self.coverage = cov
        self.config = self.coverage.config
        self.directory = self.config.html_dir

        self.skip_covered = self.config.html_skip_covered
        if self.skip_covered is None:
            self.skip_covered = self.config.skip_covered
        self.skip_empty = self.config.html_skip_empty
        if self.skip_empty is None:
            self.skip_empty = self.config.skip_empty
        self.skipped_covered_count = 0
        self.skipped_empty_count = 0

        title = self.config.html_title

        if self.config.extra_css:
            self.extra_css = os.path.basename(self.config.extra_css)
        else:
            self.extra_css = None

        self.data = self.coverage.get_data()
        self.has_arcs = self.data.has_arcs()

        self.file_summaries = []
        self.all_files_nums = []
        self.incr = IncrementalChecker(self.directory)
        self.datagen = HtmlDataGeneration(self.coverage)
        self.totals = Numbers(precision=self.config.precision)

        self.template_globals = {
            # Functions available in the templates.
            'escape': escape,
            'pair': pair,
            'len': len,

            # Constants for this report.
            '__url__': coverage.__url__,
            '__version__': coverage.__version__,
            'title': title,
            'time_stamp': format_local_datetime(datetime.datetime.now()),
            'extra_css': self.extra_css,
            'has_arcs': self.has_arcs,
            'show_contexts': self.config.show_contexts,

            # Constants for all reports.
            # These css classes determine which lines are highlighted by default.
            'category': {
                'exc': 'exc show_exc',
                'mis': 'mis show_mis',
                'par': 'par run show_par',
                'run': 'run',
            }
        }
        self.pyfile_html_source = read_data("pyfile.html")
        self.source_tmpl = Templite(self.pyfile_html_source,
                                    self.template_globals)

    def report(self, morfs):
        """Generate an HTML report for `morfs`.

        `morfs` is a list of modules or file names.

        """
        # Read the status data and check that this run used the same
        # global data as the last run.
        self.incr.read()
        self.incr.check_global_data(self.config, self.pyfile_html_source)

        # Process all the files.
        for fr, analysis in get_analysis_to_report(self.coverage, morfs):
            self.html_file(fr, analysis)

        if not self.all_files_nums:
            raise CoverageException("No data to report.")

        self.totals = sum(self.all_files_nums)

        # Write the index file.
        self.index_file()

        self.make_local_static_report_files()
        return self.totals.n_statements and self.totals.pc_covered

    def make_local_static_report_files(self):
        """Make local instances of static files for HTML report."""
        # The files we provide must always be copied.
        for static in self.STATIC_FILES:
            shutil.copyfile(data_filename(static),
                            os.path.join(self.directory, static))

        # .gitignore can't be copied from the source tree because it would
        # prevent the static files from being checked in.
        with open(os.path.join(self.directory, ".gitignore"), "w") as fgi:
            fgi.write("# Created by coverage.py\n*\n")

        # The user may have extra CSS they want copied.
        if self.extra_css:
            shutil.copyfile(self.config.extra_css,
                            os.path.join(self.directory, self.extra_css))

    def html_file(self, fr, analysis):
        """Generate an HTML file for one source file."""
        rootname = flat_rootname(fr.relative_filename())
        html_filename = rootname + ".html"
        ensure_dir(self.directory)
        html_path = os.path.join(self.directory, html_filename)

        # Get the numbers for this file.
        nums = analysis.numbers
        self.all_files_nums.append(nums)

        if self.skip_covered:
            # Don't report on 100% files.
            no_missing_lines = (nums.n_missing == 0)
            no_missing_branches = (nums.n_partial_branches == 0)
            if no_missing_lines and no_missing_branches:
                # If there's an existing file, remove it.
                file_be_gone(html_path)
                self.skipped_covered_count += 1
                return

        if self.skip_empty:
            # Don't report on empty files.
            if nums.n_statements == 0:
                file_be_gone(html_path)
                self.skipped_empty_count += 1
                return

        # Find out if the file on disk is already correct.
        if self.incr.can_skip_file(self.data, fr, rootname):
            self.file_summaries.append(self.incr.index_info(rootname))
            return

        # Write the HTML page for this file.
        file_data = self.datagen.data_for_file(fr, analysis)
        for ldata in file_data.lines:
            # Build the HTML for the line.
            html = []
            for tok_type, tok_text in ldata.tokens:
                if tok_type == "ws":
                    html.append(escape(tok_text))
                else:
                    tok_html = escape(tok_text) or '&nbsp;'
                    html.append(f'<span class="{tok_type}">{tok_html}</span>')
            ldata.html = ''.join(html)

            if ldata.short_annotations:
                # 202F is NARROW NO-BREAK SPACE.
                # 219B is RIGHTWARDS ARROW WITH STROKE.
                ldata.annotate = ",&nbsp;&nbsp; ".join(
                    f"{ldata.number}&#x202F;&#x219B;&#x202F;{d}"
                    for d in ldata.short_annotations)
            else:
                ldata.annotate = None

            if ldata.long_annotations:
                longs = ldata.long_annotations
                if len(longs) == 1:
                    ldata.annotate_long = longs[0]
                else:
                    ldata.annotate_long = "{:d} missed branches: {}".format(
                        len(longs),
                        ", ".join(
                            f"{num:d}) {ann_long}"
                            for num, ann_long in enumerate(longs, start=1)),
                    )
            else:
                ldata.annotate_long = None

            css_classes = []
            if ldata.category:
                css_classes.append(
                    self.template_globals['category'][ldata.category])
            ldata.css_class = ' '.join(css_classes) or "pln"

        html = self.source_tmpl.render(file_data.__dict__)
        write_html(html_path, html)

        # Save this file's information for the index file.
        index_info = {
            'nums': nums,
            'html_filename': html_filename,
            'relative_filename': fr.relative_filename(),
        }
        self.file_summaries.append(index_info)
        self.incr.set_index_info(rootname, index_info)

    def index_file(self):
        """Write the index.html file for this report."""
        index_tmpl = Templite(read_data("index.html"), self.template_globals)

        skipped_covered_msg = skipped_empty_msg = ""
        if self.skipped_covered_count:
            msg = "{} {} skipped due to complete coverage."
            skipped_covered_msg = msg.format(
                self.skipped_covered_count,
                "file" if self.skipped_covered_count == 1 else "files",
            )
        if self.skipped_empty_count:
            msg = "{} empty {} skipped."
            skipped_empty_msg = msg.format(
                self.skipped_empty_count,
                "file" if self.skipped_empty_count == 1 else "files",
            )

        html = index_tmpl.render({
            'files': self.file_summaries,
            'totals': self.totals,
            'skipped_covered_msg': skipped_covered_msg,
            'skipped_empty_msg': skipped_empty_msg,
        })

        index_file = os.path.join(self.directory, "index.html")
        write_html(index_file, html)
        self.coverage._message(f"Wrote HTML report to {index_file}")

        # Write the latest hashes for next time.
        self.incr.write()
Beispiel #45
0
class HtmlReporter(Reporter):
    """HTML reporting."""

    # These files will be copied from the htmlfiles dir to the output dir.
    STATIC_FILES = [
            "style.css",
            "jquery-1.4.3.min.js",
            "jquery.hotkeys.js",
            "jquery.isonscreen.js",
            "jquery.tablesorter.min.js",
            "coverage_html.js",
            "keybd_closed.png",
            "keybd_open.png",
            ]

    def __init__(self, cov, ignore_errors=False):
        super(HtmlReporter, self).__init__(cov, ignore_errors)
        self.directory = None
        self.template_globals = {
            'escape': escape,
            '__url__': coverage.__url__,
            '__version__': coverage.__version__,
            }
        self.source_tmpl = Templite(
            data("htmlfiles/pyfile.html"), self.template_globals
            )

        self.coverage = cov

        self.files = []
        self.arcs = self.coverage.data.has_arcs()
        self.status = HtmlStatus()

    def report(self, morfs, config=None):
        """Generate an HTML report for `morfs`.

        `morfs` is a list of modules or filenames.  `config` is a
        CoverageConfig instance.

        """
        assert config.html_dir, "must provide a directory for html reporting"

        # Read the status data.
        self.status.read(config.html_dir)

        # Check that this run used the same settings as the last run.
        m = Hasher()
        m.update(config)
        these_settings = m.digest()
        if self.status.settings_hash() != these_settings:
            self.status.reset()
            self.status.set_settings_hash(these_settings)

        # Process all the files.
        self.report_files(self.html_file, morfs, config, config.html_dir)

        if not self.files:
            raise CoverageException("No data to report.")

        # Write the index file.
        self.index_file()

        # Create the once-per-directory files.
        for static in self.STATIC_FILES:
            shutil.copyfile(
                data_filename("htmlfiles/" + static),
                os.path.join(self.directory, static)
                )

    def file_hash(self, source, cu):
        """Compute a hash that changes if the file needs to be re-reported."""
        m = Hasher()
        m.update(source)
        self.coverage.data.add_to_hash(cu.filename, m)
        return m.digest()

    def html_file(self, cu, analysis):
        """Generate an HTML file for one source file."""
        source_file = cu.source_file()
        try:
            source = source_file.read()
        finally:
            source_file.close()

        # Find out if the file on disk is already correct.
        flat_rootname = cu.flat_rootname()
        this_hash = self.file_hash(source, cu)
        that_hash = self.status.file_hash(flat_rootname)
        if this_hash == that_hash:
            # Nothing has changed to require the file to be reported again.
            self.files.append(self.status.index_info(flat_rootname))
            return

        self.status.set_file_hash(flat_rootname, this_hash)

        nums = analysis.numbers

        missing_branch_arcs = analysis.missing_branch_arcs()
        n_par = 0   # accumulated below.
        arcs = self.arcs

        # These classes determine which lines are highlighted by default.
        c_run = "run hide_run"
        c_exc = "exc"
        c_mis = "mis"
        c_par = "par " + c_run

        lines = []

        for lineno, line in enumerate(source_token_lines(source)):
            lineno += 1     # 1-based line numbers.
            # Figure out how to mark this line.
            line_class = []
            annotate_html = ""
            annotate_title = ""
            if lineno in analysis.statements:
                line_class.append("stm")
            if lineno in analysis.excluded:
                line_class.append(c_exc)
            elif lineno in analysis.missing:
                line_class.append(c_mis)
            elif self.arcs and lineno in missing_branch_arcs:
                line_class.append(c_par)
                n_par += 1
                annlines = []
                for b in missing_branch_arcs[lineno]:
                    if b < 0:
                        annlines.append("exit")
                    else:
                        annlines.append(str(b))
                annotate_html = "&nbsp;&nbsp; ".join(annlines)
                if len(annlines) > 1:
                    annotate_title = "no jumps to these line numbers"
                elif len(annlines) == 1:
                    annotate_title = "no jump to this line number"
            elif lineno in analysis.statements:
                line_class.append(c_run)

            # Build the HTML for the line
            html = []
            for tok_type, tok_text in line:
                if tok_type == "ws":
                    html.append(escape(tok_text))
                else:
                    tok_html = escape(tok_text) or '&nbsp;'
                    html.append(
                        "<span class='%s'>%s</span>" % (tok_type, tok_html)
                        )

            lines.append({
                'html': ''.join(html),
                'number': lineno,
                'class': ' '.join(line_class) or "pln",
                'annotate': annotate_html,
                'annotate_title': annotate_title,
            })

        # Write the HTML page for this file.
        html_filename = flat_rootname + ".html"
        html_path = os.path.join(self.directory, html_filename)
        html = spaceless(self.source_tmpl.render(locals()))
        fhtml = open(html_path, 'w')
        try:
            fhtml.write(html)
        finally:
            fhtml.close()

        # Save this file's information for the index file.
        index_info = {
            'nums': nums,
            'par': n_par,
            'html_filename': html_filename,
            'name': cu.name,
            }
        self.files.append(index_info)
        self.status.set_index_info(flat_rootname, index_info)

    def index_file(self):
        """Write the index.html file for this report."""
        index_tmpl = Templite(
            data("htmlfiles/index.html"), self.template_globals
            )

        files = self.files
        arcs = self.arcs

        totals = sum([f['nums'] for f in files])

        fhtml = open(os.path.join(self.directory, "index.html"), "w")
        try:
            fhtml.write(index_tmpl.render(locals()))
        finally:
            fhtml.close()

        # Write the latest hashes for next time.
        self.status.write(self.directory)
Beispiel #46
0
class HtmlReporter(Reporter):
    """HTML reporting."""

    # These files will be copied from the htmlfiles dir to the output dir.
    STATIC_FILES = [
            ("style.css", ""),
            ("jquery.min.js", "jquery"),
            ("jquery.hotkeys.js", "jquery-hotkeys"),
            ("jquery.isonscreen.js", "jquery-isonscreen"),
            ("jquery.tablesorter.min.js", "jquery-tablesorter"),
            ("jquery.qtip-1.0.0-rc3.min.js", "jquery-qtip"),
            ("coverage_html.js", ""),
            ("keybd_closed.png", ""),
            ("keybd_open.png", ""),
            ("page.png", ""),
            ]

    def __init__(self, cov, config):
        super(HtmlReporter, self).__init__(cov, config)
        self.directory = None
        self.template_globals = {
            'escape': escape,
            'title': self.config.html_title,
            '__url__': coverage.__url__,
            '__version__': coverage.__version__,
            }
        self.source_tmpl = Templite(
            data("pyfile.html"), self.template_globals
            )

        self.coverage = cov

        self.files = []
        self.arcs = self.coverage.data.has_arcs()
        self.status = HtmlStatus()
        self.extra_css = None
        self.totals = Numbers()

    def report(self, morfs):
        """Generate an HTML report for `morfs`.

        `morfs` is a list of modules or filenames.

        """
        assert self.config.html_dir, "must give a directory for html reporting"

        # Read the status data.
        self.status.read(self.config.html_dir)

        # Check that this run used the same settings as the last run.
        m = Hasher()
        m.update(self.config)
        these_settings = m.digest()
        if self.status.settings_hash() != these_settings:
            self.status.reset()
            self.status.set_settings_hash(these_settings)

        # The user may have extra CSS they want copied.
        if self.config.extra_css:
            self.extra_css = os.path.basename(self.config.extra_css)

        # Process all the files.
        self.report_files(self.html_file, morfs, self.config.html_dir)

        if not self.files:
            raise CoverageException("No data to report.")

        # Write the index file.
        self.index_file()

        self.make_local_static_report_files()

        return self.totals.pc_covered

    def make_local_static_report_files(self):
        """Make local instances of static files for HTML report."""
        # The files we provide must always be copied.
        for static, pkgdir in self.STATIC_FILES:
            shutil.copyfile(
                data_filename(static, pkgdir),
                os.path.join(self.directory, static)
                )

        # The user may have extra CSS they want copied.
        if self.extra_css:
            shutil.copyfile(
                self.config.extra_css,
                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, cu):
        """Compute a hash that changes if the file needs to be re-reported."""
        m = Hasher()
        m.update(source)
        self.coverage.data.add_to_hash(cu.filename, m)
        return m.digest()

    def html_file(self, cu, analysis):
        """Generate an HTML file for one source file."""
        source = cu.source()

        # Find out if the file on disk is already correct.
        flat_rootname = cu.flat_rootname()
        this_hash = self.file_hash(source, cu)
        that_hash = self.status.file_hash(flat_rootname)
        if this_hash == that_hash:
            # Nothing has changed to require the file to be reported again.
            self.files.append(self.status.index_info(flat_rootname))
            return

        self.status.set_file_hash(flat_rootname, this_hash)

        # If need be, determine the encoding of the source file. We use it
        # later to properly write the HTML.
        if sys.version_info < (3, 0):
            encoding = cu.source_encoding()
            # Some UTF8 files have the dreaded UTF8 BOM. If so, junk it.
            if encoding.startswith("utf-8") and source[:3] == "\xef\xbb\xbf":
                source = source[3:]
                encoding = "utf-8"

        # Get the numbers for this file.
        nums = analysis.numbers

        if self.arcs:
            missing_branch_arcs = analysis.missing_branch_arcs()

        # These classes determine which lines are highlighted by default.
        c_run = "run hide_run"
        c_exc = "exc"
        c_mis = "mis"
        c_par = "par " + c_run

        lines = []

        for lineno, line in enumerate(cu.source_token_lines(), start=1):
            # Figure out how to mark this line.
            line_class = []
            annotate_html = ""
            annotate_title = ""
            if lineno in analysis.statements:
                line_class.append("stm")
            if lineno in analysis.excluded:
                line_class.append(c_exc)
            elif lineno in analysis.missing:
                line_class.append(c_mis)
            elif self.arcs and lineno in missing_branch_arcs:
                line_class.append(c_par)
                annlines = []
                for b in missing_branch_arcs[lineno]:
                    if b < 0:
                        annlines.append("exit")
                    else:
                        annlines.append(str(b))
                annotate_html = "&nbsp;&nbsp; ".join(annlines)
                if len(annlines) > 1:
                    annotate_title = "no jumps to these line numbers"
                elif len(annlines) == 1:
                    annotate_title = "no jump to this line number"
            elif lineno in analysis.statements:
                line_class.append(c_run)

            # Build the HTML for the line
            html = []
            for tok_type, tok_text in line:
                if tok_type == "ws":
                    html.append(escape(tok_text))
                else:
                    tok_html = escape(tok_text) or '&nbsp;'
                    html.append(
                        "<span class='%s'>%s</span>" % (tok_type, tok_html)
                        )

            callers_html_list = []
            callers = analysis.callers_data.get(lineno)
            if callers:
                line_class.append("has_callers")
                for caller in sorted(callers.test_methods.keys()):
                    caller_count = callers.test_methods[caller]
                    html_filename = self.get_href_for_source_file(caller.filename)
                    content = caller.function_name + " in " + caller.filename + " line " + str(caller.line_no) + \
                        " (" + str(caller_count) + ")"
                    caller_html = "<a href=" + html_filename + "#n" + str(caller.line_no) + ">" + content + "</a>"
                    callers_html_list.append(caller_html)

            lines.append({
                'html': ''.join(html),
                'number': lineno,
                'class': ' '.join(line_class) or "pln",
                'annotate': annotate_html,
                'annotate_title': annotate_title,
                'callers_html_list': callers_html_list,
            })

        # Write the HTML page for this file.
        html = spaceless(self.source_tmpl.render({
            'c_exc': c_exc, 'c_mis': c_mis, 'c_par': c_par, 'c_run': c_run,
            'arcs': self.arcs, 'extra_css': self.extra_css,
            'cu': cu, 'nums': nums, 'lines': lines,
        }))

        if sys.version_info < (3, 0):
            # In theory, all the characters in the source can be decoded, but
            # strange things happen, so use 'replace' to keep errors at bay.
            html = html.decode(encoding, 'replace')

        html_filename = flat_rootname + ".html"
        html_path = os.path.join(self.directory, html_filename)
        self.write_html(html_path, html)

        # Save this file's information for the index file.
        index_info = {
            'nums': nums,
            'html_filename': html_filename,
            'name': cu.name,
            }
        self.files.append(index_info)
        self.status.set_index_info(flat_rootname, index_info)

    def get_href_for_source_file(self, source_filename):
        # Bad hacks here...
        found_cu = None
        for cu in self.code_units:
            if cu.filename == source_filename:
                found_cu = cu
                break
        if found_cu is not None:
            # Best case: we found the code unit in our data already.
            return found_cu.flat_rootname() + ".html"
        else:
            # Test seems to live outside our code unit files.
            # This is almost certainly wrong below.
            if source_filename[0] == "<":
                source_filename = source_filename.replace("<", "_").replace(">", "_")
            source_filename = source_filename.replace(".py", ".html").replace("/", "_")
            return source_filename

    def index_file(self):
        """Write the index.html file for this report."""
        index_tmpl = Templite(
            data("index.html"), self.template_globals
            )

        self.totals = sum(f['nums'] for f in self.files)

        html = index_tmpl.render({
            'arcs': self.arcs,
            'extra_css': self.extra_css,
            'files': self.files,
            'totals': self.totals,
        })

        if sys.version_info < (3, 0):
            html = html.decode("utf-8")
        self.write_html(
            os.path.join(self.directory, "index.html"),
            html
            )

        # Write the latest hashes for next time.
        self.status.write(self.directory)