Skip to content

Commit

Permalink
Detect when a 4.x data file is being read. #886
Browse files Browse the repository at this point in the history
  • Loading branch information
nedbat committed Dec 22, 2019
1 parent d66d496 commit 6765a72
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 6 deletions.
6 changes: 6 additions & 0 deletions CHANGES.rst
Expand Up @@ -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.
Expand All @@ -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
Expand Down
15 changes: 14 additions & 1 deletion coverage/sqldata.py
Expand Up @@ -1031,7 +1031,20 @@ def execute(self, sql, parameters=()):
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`."""
Expand Down
20 changes: 15 additions & 5 deletions tests/test_api.py
Expand Up @@ -295,6 +295,20 @@ def test_empty_reporting(self):
with self.assertRaisesRegex(CoverageException, "No data to report."):
cov.report()

def test_cov4_data_file(self):
if env.WINDOWS:
# For some reason on Windows this test fails while trying to remove the temp
# directory, because ".coverage" is still in use. Skip it.
self.skipTest("Windows gets tangled up on this test.")
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()

def make_code1_code2(self):
"""Create the code1.py and code2.py files."""
self.make_file("code1.py", """\
Expand Down Expand Up @@ -384,15 +398,11 @@ def make_good_data_files(self):
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"
Expand Down

0 comments on commit 6765a72

Please sign in to comment.