summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO.txt8
-rw-r--r--coverage/html.py57
-rw-r--r--coverage/misc.py6
-rw-r--r--coverage/results.py7
-rw-r--r--tests/test_misc.py6
5 files changed, 63 insertions, 21 deletions
diff --git a/TODO.txt b/TODO.txt
index 438bcee1..1de6e0b5 100644
--- a/TODO.txt
+++ b/TODO.txt
@@ -31,10 +31,10 @@ Key:
- Change data file to json
- Create data api
- New ast-based branch coverage?
-- gevent, etc.
-- Remove the old command-line syntax
- - A pain, b/c of the structure of the tests.
- - BTW: make an easier way to write those tests.
++ gevent, etc.
++ Remove the old command-line syntax
+ + A pain, b/c of the structure of the tests.
+ + BTW: make an easier way to write those tests.
diff --git a/coverage/html.py b/coverage/html.py
index 0829f219..37d62280 100644
--- a/coverage/html.py
+++ b/coverage/html.py
@@ -1,9 +1,9 @@
"""HTML reporting for Coverage."""
-import os, re, shutil, sys
+import json, os, re, shutil, sys
import coverage
-from coverage.backward import pickle
+from coverage.backward import iitems
from coverage.misc import CoverageException, Hasher
from coverage.report import Reporter
from coverage.results import Numbers
@@ -98,7 +98,7 @@ class HtmlReporter(Reporter):
# Check that this run used the same settings as the last run.
m = Hasher()
m.update(self.config)
- these_settings = m.digest()
+ these_settings = m.hexdigest()
if self.status.settings_hash() != these_settings:
self.status.reset()
self.status.set_settings_hash(these_settings)
@@ -146,7 +146,7 @@ class HtmlReporter(Reporter):
m = Hasher()
m.update(source)
self.coverage.data.add_to_hash(cu.filename, m)
- return m.digest()
+ return m.hexdigest()
def html_file(self, cu, analysis):
"""Generate an HTML file for one source file."""
@@ -286,9 +286,36 @@ class HtmlReporter(Reporter):
class HtmlStatus(object):
"""The status information we keep to support incremental reporting."""
- STATUS_FILE = "status.dat"
+ STATUS_FILE = "status.json"
STATUS_FORMAT = 1
+ # The data looks like:
+ #
+ # {
+ # 'format': 1,
+ # 'settings': '\x87\x9cc8\x80\xe5\x97\xb16\xfcv\xa2\x8d\x8a\xbb\xcf',
+ # 'version': '4.0a1',
+ # 'files': {
+ # 'cogapp___init__': {
+ # 'hash': '\x99*\x0e\\\x10\x11O\x06WG/gJ\x83\xdd\x99',
+ # 'index': {
+ # 'html_filename': 'cogapp___init__.html',
+ # 'name': 'cogapp/__init__',
+ # 'nums': <coverage.results.Numbers object at 0x10ab7ed0>,
+ # }
+ # },
+ # ...
+ # 'cogapp_whiteutils': {
+ # 'hash': 'o\xfd\x0e+s2="\xb2\x1c\xd6\xa1\xee\x85\x85\xda',
+ # 'index': {
+ # 'html_filename': 'cogapp_whiteutils.html',
+ # 'name': 'cogapp/whiteutils',
+ # 'nums': <coverage.results.Numbers object at 0x10ab7d90>,
+ # }
+ # },
+ # },
+ # }
+
def __init__(self):
self.reset()
@@ -302,8 +329,8 @@ class HtmlStatus(object):
usable = False
try:
status_file = os.path.join(directory, self.STATUS_FILE)
- with open(status_file, "rb") as fstatus:
- status = pickle.load(fstatus)
+ with open(status_file, "r") as fstatus:
+ status = json.load(fstatus)
except (IOError, ValueError):
usable = False
else:
@@ -314,7 +341,10 @@ class HtmlStatus(object):
usable = False
if usable:
- self.files = status['files']
+ self.files = {}
+ for filename, fileinfo in iitems(status['files']):
+ fileinfo['index']['nums'] = Numbers(*fileinfo['index']['nums'])
+ self.files[filename] = fileinfo
self.settings = status['settings']
else:
self.reset()
@@ -322,14 +352,19 @@ class HtmlStatus(object):
def write(self, directory):
"""Write the current status to `directory`."""
status_file = os.path.join(directory, self.STATUS_FILE)
+ files = {}
+ for filename, fileinfo in iitems(self.files):
+ fileinfo['index']['nums'] = fileinfo['index']['nums'].init_args()
+ files[filename] = fileinfo
+
status = {
'format': self.STATUS_FORMAT,
'version': coverage.__version__,
'settings': self.settings,
- 'files': self.files,
+ 'files': files,
}
- with open(status_file, "wb") as fout:
- pickle.dump(status, fout)
+ with open(status_file, "w") as fout:
+ json.dump(status, fout)
def settings_hash(self):
"""Get the hash of the coverage.py settings."""
diff --git a/coverage/misc.py b/coverage/misc.py
index a653bb62..f9a30bc5 100644
--- a/coverage/misc.py
+++ b/coverage/misc.py
@@ -130,9 +130,9 @@ class Hasher(object):
self.update(k)
self.update(a)
- def digest(self):
- """Retrieve the digest of the hash."""
- return self.md5.digest()
+ def hexdigest(self):
+ """Retrieve the hex digest of the hash."""
+ return self.md5.hexdigest()
class CoverageException(Exception):
diff --git a/coverage/results.py b/coverage/results.py
index 5eff0f3e..f1c63bbb 100644
--- a/coverage/results.py
+++ b/coverage/results.py
@@ -182,6 +182,13 @@ class Numbers(object):
self.n_partial_branches = n_partial_branches
self.n_missing_branches = n_missing_branches
+ def init_args(self):
+ """Return a list for __init__(*args) to recreate this object."""
+ return [
+ self.n_files, self.n_statements, self.n_excluded, self.n_missing,
+ self.n_branches, self.n_partial_branches, self.n_missing_branches,
+ ]
+
@classmethod
def set_precision(cls, precision):
"""Set the number of decimal places used to report percentages."""
diff --git a/tests/test_misc.py b/tests/test_misc.py
index 74ed1a78..37191f67 100644
--- a/tests/test_misc.py
+++ b/tests/test_misc.py
@@ -18,15 +18,15 @@ class HasherTest(CoverageTest):
h2.update("Goodbye!")
h3 = Hasher()
h3.update("Hello, world!")
- self.assertNotEqual(h1.digest(), h2.digest())
- self.assertEqual(h1.digest(), h3.digest())
+ self.assertNotEqual(h1.hexdigest(), h2.hexdigest())
+ self.assertEqual(h1.hexdigest(), h3.hexdigest())
def test_dict_hashing(self):
h1 = Hasher()
h1.update({'a': 17, 'b': 23})
h2 = Hasher()
h2.update({'b': 23, 'a': 17})
- self.assertEqual(h1.digest(), h2.digest())
+ self.assertEqual(h1.hexdigest(), h2.hexdigest())
class RemoveFileTest(CoverageTest):