summaryrefslogtreecommitdiff
path: root/coverage/html.py
diff options
context:
space:
mode:
authorNed Batchelder <ned@nedbatchelder.com>2011-03-24 22:49:37 -0400
committerNed Batchelder <ned@nedbatchelder.com>2011-03-24 22:49:37 -0400
commit01ea68a1088fc1cdbdd586624ac695c5b697edeb (patch)
tree7b64f69deaeda1fa07c38bfdb64c7c4d8e879eb3 /coverage/html.py
parent1f950ca6c4c1da6d5f2f37faeb0217e9ff273b75 (diff)
downloadpython-coveragepy-git-01ea68a1088fc1cdbdd586624ac695c5b697edeb.tar.gz
Incremental HTML generation. Some cleanup would be good.
Diffstat (limited to 'coverage/html.py')
-rw-r--r--coverage/html.py105
1 files changed, 99 insertions, 6 deletions
diff --git a/coverage/html.py b/coverage/html.py
index 87edad4d..966e10d9 100644
--- a/coverage/html.py
+++ b/coverage/html.py
@@ -3,7 +3,8 @@
import os, re, shutil
from coverage import __url__, __version__ # pylint: disable=W0611
-from coverage.misc import CoverageException
+from coverage.backward import pickle
+from coverage.misc import CoverageException, Hasher
from coverage.phystokens import source_token_lines
from coverage.report import Reporter
from coverage.templite import Templite
@@ -41,9 +42,11 @@ class HtmlReporter(Reporter):
super(HtmlReporter, self).__init__(coverage, ignore_errors)
self.directory = None
self.source_tmpl = Templite(data("htmlfiles/pyfile.html"), globals())
+ self.coverage = coverage
self.files = []
- self.arcs = coverage.data.has_arcs()
+ self.arcs = self.coverage.data.has_arcs()
+ self.status = HtmlStatus()
def report(self, morfs, config=None):
"""Generate an HTML report for `morfs`.
@@ -54,6 +57,17 @@ class HtmlReporter(Reporter):
"""
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)
@@ -70,6 +84,13 @@ class HtmlReporter(Reporter):
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()
@@ -78,6 +99,17 @@ class HtmlReporter(Reporter):
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()
@@ -141,7 +173,7 @@ class HtmlReporter(Reporter):
})
# Write the HTML page for this file.
- html_filename = cu.flat_rootname() + ".html"
+ 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')
@@ -151,12 +183,14 @@ class HtmlReporter(Reporter):
fhtml.close()
# Save this file's information for the index file.
- self.files.append({
+ index_info = {
'nums': nums,
'par': n_par,
'html_filename': html_filename,
- 'cu': cu,
- })
+ '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."""
@@ -173,6 +207,65 @@ class HtmlReporter(Reporter):
finally:
fhtml.close()
+ # Write the latest hashes for next time.
+ self.status.write(self.directory)
+
+
+class HtmlStatus(object):
+ # A pickle of hashes for controlling deltas.
+ STATUS_FILE = "latest.dat"
+ STATUS_VERSION = 1
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ self.settings = ''
+ self.status = {}
+
+ def read(self, directory):
+ self.reset()
+ try:
+ status_file = os.path.join(directory, self.STATUS_FILE)
+ data = pickle.load(open(status_file, "rb"))
+ except IOError:
+ pass
+ else:
+ if data['version'] == self.STATUS_VERSION:
+ self.status = data['files']
+ self.settings = data['settings']
+
+ def write(self, directory):
+ status_file = os.path.join(directory, self.STATUS_FILE)
+ data = {
+ 'version': self.STATUS_VERSION,
+ 'settings': self.settings,
+ 'files': self.status,
+ }
+ fout = open(status_file, "wb")
+ try:
+ pickle.dump(data, fout)
+ finally:
+ fout.close()
+
+ def settings_hash(self):
+ return self.settings
+
+ def set_settings_hash(self, settings):
+ self.settings = settings
+
+ def file_hash(self, fname):
+ return self.status.get(fname, {}).get('hash', '')
+
+ def set_file_hash(self, fname, val):
+ self.status.setdefault(fname, {})['hash'] = val
+
+ def index_info(self, fname):
+ return self.status.get(fname, {}).get('index', {})
+
+ def set_index_info(self, fname, info):
+ self.status.setdefault(fname, {})['index'] = info
+
# Helpers for templates and generating HTML