Skip to content

Commit 0d6a878

Browse files
committed
Detect when a 4.x data file is being read. #886
1 parent d66d496 commit 0d6a878

File tree

3 files changed

+32
-6
lines changed

3 files changed

+32
-6
lines changed

CHANGES.rst

+6
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ want to know what's different in 5.0 since 4.5.x, see :ref:`whatsnew5x`.
2424
Unreleased
2525
----------
2626

27+
- If a 4.x data file is the cause of a "file is not a database" error, then use
28+
a more specific error message, "Looks like a coverage 4.x data file, are you
29+
mixing versions of coverage?" Helps diagnose the problems described in
30+
`issue 886`_.
31+
2732
- Measurement contexts and relative file names didn't work together, as
2833
reported in `issue_899`_ and `issue_900`_. This is now fixed, thanks to
2934
David Szotten.
@@ -37,6 +42,7 @@ Unreleased
3742
different drive (`issue 895`_). Thanks, Olivier Grisel.
3843

3944
.. _issue 880: https://github.com/nedbat/coveragepy/issues/880
45+
.. _issue 886: https://github.com/nedbat/coveragepy/issues/886
4046
.. _issue 895: https://github.com/nedbat/coveragepy/issues/895
4147
.. _issue 899: https://github.com/nedbat/coveragepy/issues/899
4248
.. _issue 900: https://github.com/nedbat/coveragepy/issues/900

coverage/sqldata.py

+14-1
Original file line numberDiff line numberDiff line change
@@ -1031,7 +1031,20 @@ def execute(self, sql, parameters=()):
10311031
try:
10321032
return self.con.execute(sql, parameters)
10331033
except sqlite3.Error as exc:
1034-
raise CoverageException("Couldn't use data file {!r}: {}".format(self.filename, exc))
1034+
msg = str(exc)
1035+
try:
1036+
# `execute` is the first thing we do with the database, so try
1037+
# hard to provide useful hints if something goes wrong now.
1038+
with open(self.filename, "rb") as bad_file:
1039+
cov4_sig = b"!coverage.py: This is a private format"
1040+
if bad_file.read(len(cov4_sig)) == cov4_sig:
1041+
msg = (
1042+
"Looks like a coverage 4.x data file. "
1043+
"Are you mixing versions of coverage?"
1044+
)
1045+
except Exception:
1046+
pass
1047+
raise CoverageException("Couldn't use data file {!r}: {}".format(self.filename, msg))
10351048

10361049
def executemany(self, sql, data):
10371050
"""Same as :meth:`python:sqlite3.Connection.executemany`."""

tests/test_api.py

+12-5
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,17 @@ def test_empty_reporting(self):
295295
with self.assertRaisesRegex(CoverageException, "No data to report."):
296296
cov.report()
297297

298+
def test_cov4_data_file(self):
299+
cov4_data = (
300+
"!coverage.py: This is a private format, don't read it directly!"
301+
'{"lines":{"/private/tmp/foo.py":[1,5,2,3]}}'
302+
)
303+
self.make_file(".coverage", cov4_data)
304+
cov = coverage.Coverage()
305+
with self.assertRaisesRegex(CoverageException, "Looks like a coverage 4.x data file"):
306+
cov.load()
307+
cov.erase()
308+
298309
def make_code1_code2(self):
299310
"""Create the code1.py and code2.py files."""
300311
self.make_file("code1.py", """\
@@ -384,15 +395,11 @@ def make_good_data_files(self):
384395
cov.save()
385396
self.assert_file_count(".coverage.*", 2)
386397

387-
def make_bad_data_file(self):
388-
"""Make one bad data file."""
389-
self.make_file(".coverage.foo", """La la la, this isn't coverage data!""")
390-
391398
def test_combining_corrupt_data(self):
392399
# If you combine a corrupt data file, then you will get a warning,
393400
# and the file will remain.
394401
self.make_good_data_files()
395-
self.make_bad_data_file()
402+
self.make_file(".coverage.foo", """La la la, this isn't coverage data!""")
396403
cov = coverage.Coverage()
397404
warning_regex = (
398405
r"Couldn't use data file '.*\.coverage\.foo': file (is encrypted or )?is not a database"

0 commit comments

Comments
 (0)