summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNed Batchelder <ned@nedbatchelder.com>2019-12-22 09:48:17 -0500
committerNed Batchelder <ned@nedbatchelder.com>2019-12-22 11:09:01 -0500
commit0d6a8784cdafd46409fe187ad87b7c38772d6d1b (patch)
tree72d83360eb575ad10d8ec0213a242bd9e0bf156c
parentd66d496b53e3fd8ba891a2704d82e0b6a0be1502 (diff)
downloadpython-coveragepy-git-0d6a8784cdafd46409fe187ad87b7c38772d6d1b.tar.gz
Detect when a 4.x data file is being read. #886
-rw-r--r--CHANGES.rst6
-rw-r--r--coverage/sqldata.py15
-rw-r--r--tests/test_api.py17
3 files changed, 32 insertions, 6 deletions
diff --git a/CHANGES.rst b/CHANGES.rst
index 37f36080..bd36d953 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -24,6 +24,11 @@ want to know what's different in 5.0 since 4.5.x, see :ref:`whatsnew5x`.
Unreleased
----------
+- If a 4.x data file is the cause of a "file is not a database" error, then use
+ a more specific error message, "Looks like a coverage 4.x data file, are you
+ mixing versions of coverage?" Helps diagnose the problems described in
+ `issue 886`_.
+
- Measurement contexts and relative file names didn't work together, as
reported in `issue_899`_ and `issue_900`_. This is now fixed, thanks to
David Szotten.
@@ -37,6 +42,7 @@ Unreleased
different drive (`issue 895`_). Thanks, Olivier Grisel.
.. _issue 880: https://github.com/nedbat/coveragepy/issues/880
+.. _issue 886: https://github.com/nedbat/coveragepy/issues/886
.. _issue 895: https://github.com/nedbat/coveragepy/issues/895
.. _issue 899: https://github.com/nedbat/coveragepy/issues/899
.. _issue 900: https://github.com/nedbat/coveragepy/issues/900
diff --git a/coverage/sqldata.py b/coverage/sqldata.py
index 01f5ce01..613a280c 100644
--- a/coverage/sqldata.py
+++ b/coverage/sqldata.py
@@ -1031,7 +1031,20 @@ class SqliteDb(SimpleReprMixin):
try:
return self.con.execute(sql, parameters)
except sqlite3.Error as exc:
- raise CoverageException("Couldn't use data file {!r}: {}".format(self.filename, exc))
+ msg = str(exc)
+ try:
+ # `execute` is the first thing we do with the database, so try
+ # hard to provide useful hints if something goes wrong now.
+ with open(self.filename, "rb") as bad_file:
+ cov4_sig = b"!coverage.py: This is a private format"
+ if bad_file.read(len(cov4_sig)) == cov4_sig:
+ msg = (
+ "Looks like a coverage 4.x data file. "
+ "Are you mixing versions of coverage?"
+ )
+ except Exception:
+ pass
+ raise CoverageException("Couldn't use data file {!r}: {}".format(self.filename, msg))
def executemany(self, sql, data):
"""Same as :meth:`python:sqlite3.Connection.executemany`."""
diff --git a/tests/test_api.py b/tests/test_api.py
index 8fa81d07..30a90c3c 100644
--- a/tests/test_api.py
+++ b/tests/test_api.py
@@ -295,6 +295,17 @@ class ApiTest(CoverageTest):
with self.assertRaisesRegex(CoverageException, "No data to report."):
cov.report()
+ def test_cov4_data_file(self):
+ cov4_data = (
+ "!coverage.py: This is a private format, don't read it directly!"
+ '{"lines":{"/private/tmp/foo.py":[1,5,2,3]}}'
+ )
+ self.make_file(".coverage", cov4_data)
+ cov = coverage.Coverage()
+ with self.assertRaisesRegex(CoverageException, "Looks like a coverage 4.x data file"):
+ cov.load()
+ cov.erase()
+
def make_code1_code2(self):
"""Create the code1.py and code2.py files."""
self.make_file("code1.py", """\
@@ -384,15 +395,11 @@ class ApiTest(CoverageTest):
cov.save()
self.assert_file_count(".coverage.*", 2)
- def make_bad_data_file(self):
- """Make one bad data file."""
- self.make_file(".coverage.foo", """La la la, this isn't coverage data!""")
-
def test_combining_corrupt_data(self):
# If you combine a corrupt data file, then you will get a warning,
# and the file will remain.
self.make_good_data_files()
- self.make_bad_data_file()
+ self.make_file(".coverage.foo", """La la la, this isn't coverage data!""")
cov = coverage.Coverage()
warning_regex = (
r"Couldn't use data file '.*\.coverage\.foo': file (is encrypted or )?is not a database"