Skip to content

Commit

Permalink
fix: create the db as needed when accessed
Browse files Browse the repository at this point in the history
  • Loading branch information
nedbat committed Jan 20, 2022
1 parent f4ee9a2 commit b41be3f
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 45 deletions.
6 changes: 6 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,20 @@ Unreleased

- Updated Python 3.11 support to 3.11.0a4, fixing `issue 1294`_.

- Fix: the coverage data file is now created in a more robust way, to avoid
problems when multiple processes are trying to write data at once. Fixes
`issue 1303`_ and `issue 883`_.

- Fix: a .gitignore file will only be written into the HTML report output
directory if the directory is empty. This should prevent certain unfortunate
accidents of writing the file where it is not wanted.

- Releases now have MacOS arm64 wheels for Apple Silicon (fixes `issue 1288`_).

.. _issue 883: https://github.com/nedbat/coveragepy/issues/883
.. _issue 1288: https://github.com/nedbat/coveragepy/issues/1288
.. _issue 1294: https://github.com/nedbat/coveragepy/issues/1294
.. _issue 1303: https://github.com/nedbat/coveragepy/issues/1303


.. _changes_62:
Expand Down
55 changes: 25 additions & 30 deletions coverage/sqldata.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,26 +256,6 @@ def _reset(self):
self._have_used = False
self._current_context_id = None

def _create_db(self):
"""Create a db file that doesn't exist yet.
Initializes the schema and certain metadata.
"""
if self._debug.should("dataio"):
self._debug.write(f"Creating data file {self._filename!r}")
self._dbs[threading.get_ident()] = db = SqliteDb(self._filename, self._debug)
with db:
db.executescript(SCHEMA)
db.execute("insert into coverage_schema (version) values (?)", (SCHEMA_VERSION,))
db.executemany(
"insert into meta (key, value) values (?, ?)",
[
("sys_argv", str(getattr(sys, "argv", None))),
("version", __version__),
("when", datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")),
]
)

def _open_db(self):
"""Open an existing db file, and read its metadata."""
if self._debug.should("dataio"):
Expand All @@ -289,11 +269,14 @@ def _read_db(self):
try:
schema_version, = db.execute_one("select version from coverage_schema")
except Exception as exc:
raise DataError(
"Data file {!r} doesn't seem to be a coverage data file: {}".format(
self._filename, exc
)
) from exc
if "no such table: coverage_schema" in str(exc):
self._init_db(db)
else:
raise DataError(
"Data file {!r} doesn't seem to be a coverage data file: {}".format(
self._filename, exc
)
) from exc
else:
if schema_version != SCHEMA_VERSION:
raise DataError(
Expand All @@ -309,13 +292,25 @@ def _read_db(self):
for path, file_id in db.execute("select path, id from file"):
self._file_map[path] = file_id

def _init_db(self, db):
"""Write the initial contents of the database."""
if self._debug.should("dataio"):
self._debug.write(f"Initing data file {self._filename!r}")
db.executescript(SCHEMA)
db.execute("insert into coverage_schema (version) values (?)", (SCHEMA_VERSION,))
db.executemany(
"insert or ignore into meta (key, value) values (?, ?)",
[
("sys_argv", str(getattr(sys, "argv", None))),
("version", __version__),
("when", datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")),
]
)

def _connect(self):
"""Get the SqliteDb object to use."""
if threading.get_ident() not in self._dbs:
if os.path.exists(self._filename):
self._open_db()
else:
self._create_db()
self._open_db()
return self._dbs[threading.get_ident()]

def __bool__(self):
Expand Down Expand Up @@ -522,7 +517,7 @@ def _choose_lines_or_arcs(self, lines=False, arcs=False):
self._has_arcs = arcs
with self._connect() as con:
con.execute(
"insert into meta (key, value) values (?, ?)",
"insert or ignore into meta (key, value) values (?, ?)",
("has_arcs", str(int(arcs)))
)

Expand Down
17 changes: 2 additions & 15 deletions tests/test_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -592,12 +592,6 @@ def test_read_errors(self):
covdata.read()
assert not covdata

self.make_file("empty.dat", "")
with pytest.raises(DataError, match=msg.format("empty.dat")):
covdata = DebugCoverageData("empty.dat")
covdata.read()
assert not covdata

def test_hard_read_error(self):
self.make_file("noperms.dat", "go away")
os.chmod("noperms.dat", 0)
Expand Down Expand Up @@ -626,14 +620,6 @@ def test_read_sql_errors(self):
covdata.read()
assert not covdata

with sqlite3.connect("no_schema.db") as con:
con.execute("create table foobar (baz text)")
msg = r"Couldn't .* '.*[/\\]no_schema.db': \S+"
with pytest.raises(DataError, match=msg):
covdata = DebugCoverageData("no_schema.db")
covdata.read()
assert not covdata


class CoverageDataFilesTest(CoverageTest):
"""Tests of CoverageData file handling."""
Expand Down Expand Up @@ -667,7 +653,8 @@ def test_debug_output_with_debug_option(self):

assert re.search(
r"^Erasing data file '.*\.coverage'\n" +
r"Creating data file '.*\.coverage'\n" +
r"Opening data file '.*\.coverage'\n" +
r"Initing data file '.*\.coverage'\n" +
r"Opening data file '.*\.coverage'\n$",
debug.get_output()
)
Expand Down

0 comments on commit b41be3f

Please sign in to comment.