Skip to content

Commit b4c19b2

Browse files
committed
fix: really close all sqlite dbs #2017
1 parent 478a204 commit b4c19b2

File tree

5 files changed

+30
-12
lines changed

5 files changed

+30
-12
lines changed

CHANGES.rst

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@ upgrading your version of coverage.py.
2323
Unreleased
2424
----------
2525

26-
Nothing yet.
26+
- Fix: really close all SQLite databases, even in-memory ones. Closes `issue
27+
2017`_.
28+
29+
.. _issue 2017: https://github.com/nedbat/coveragepy/issues/2017
2730

2831

2932
.. start-releases

coverage/control.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,7 @@ def __init__( # pylint: disable=too-many-arguments
282282
self._plugins: Plugins = Plugins()
283283
self._plugin_override = cast(Union[Iterable[TCoverageInit], None], plugins)
284284
self._data: CoverageData | None = None
285+
self._data_to_close: list[CoverageData] = []
285286
self._core: Core | None = None
286287
self._collector: Collector | None = None
287288
self._metacov = False
@@ -646,6 +647,7 @@ def _init_data(self, suffix: str | bool | None) -> None:
646647
debug=self._debug,
647648
no_disk=self._no_disk,
648649
)
650+
self._data_to_close.append(self._data)
649651

650652
def start(self) -> None:
651653
"""Start measuring code coverage.
@@ -718,6 +720,8 @@ def _atexit(self, event: str = "atexit") -> None:
718720
self.stop()
719721
if self._auto_save or event == "sigterm":
720722
self.save()
723+
for d in self._data_to_close:
724+
d.close(force=True)
721725

722726
def _on_sigterm(self, signum_unused: int, frame_unused: FrameType | None) -> None:
723727
"""A handler for signal.SIGTERM."""
@@ -1048,6 +1052,7 @@ def _prepare_data_for_reporting(self) -> None:
10481052
if self._data is not None:
10491053
mapped_data.update(self._data, map_path=self._make_aliases().map)
10501054
self._data = mapped_data
1055+
self._data_to_close.append(mapped_data)
10511056

10521057
def report(
10531058
self,

coverage/sqldata.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -271,13 +271,19 @@ def _choose_filename(self) -> None:
271271
def _reset(self) -> None:
272272
"""Reset our attributes."""
273273
if not self._no_disk:
274-
for db in self._dbs.values():
275-
db.close()
276-
self._dbs = {}
274+
self.close()
277275
self._file_map = {}
278276
self._have_used = False
279277
self._current_context_id = None
280278

279+
def close(self, force: bool = False) -> None:
280+
"""Really close all the database objects."""
281+
if self._debug.should("dataio"):
282+
self._debug.write(f"Closing dbs, force={force}: {self._dbs}")
283+
for db in self._dbs.values():
284+
db.close(force=force)
285+
self._dbs = {}
286+
281287
def _open_db(self) -> None:
282288
"""Open an existing db file, and read its metadata."""
283289
if self._debug.should("dataio"):

coverage/sqlitedb.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -76,13 +76,14 @@ def _connect(self) -> None:
7676
# to keep things going.
7777
self.execute_void("pragma synchronous=off", fail_ok=True)
7878

79-
def close(self) -> None:
79+
def close(self, force: bool = False) -> None:
8080
"""If needed, close the connection."""
81-
if self.con is not None and self.filename != ":memory:":
82-
if self.debug.should("sql"):
83-
self.debug.write(f"Closing {self.con!r} on {self.filename!r}")
84-
self.con.close()
85-
self.con = None
81+
if self.con is not None:
82+
if force or self.filename != ":memory:":
83+
if self.debug.should("sql"):
84+
self.debug.write(f"Closing {self.con!r} on {self.filename!r}")
85+
self.con.close()
86+
self.con = None
8687

8788
def __enter__(self) -> SqliteDb:
8889
if self.nest == 0:

tests/test_data.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -670,7 +670,7 @@ def test_error_when_closing(self, klass: TCoverageData) -> None:
670670
covdata.add_lines(LINES_1)
671671
# I don't know how to make a real error, so let's fake one.
672672
sqldb = list(covdata._dbs.values())[0]
673-
sqldb.close = lambda: 1/0 # type: ignore[assignment]
673+
sqldb.close = lambda: 1/0 # type: ignore
674674
covdata.add_lines(LINES_1)
675675

676676
def test_wrong_schema_version(self) -> None:
@@ -724,8 +724,11 @@ def test_debug_output_with_debug_option(self) -> None:
724724
covdata2.read()
725725
assert_line_counts(covdata2, SUMMARY_1)
726726

727+
print(debug.get_output())
728+
727729
assert re.search(
728-
r"^Erasing data file '.*\.coverage'\n" +
730+
r"^Closing dbs, force=False: {}\n" +
731+
r"Erasing data file '.*\.coverage'\n" +
729732
r"Opening data file '.*\.coverage'\n" +
730733
r"Initing data file '.*\.coverage'\n" +
731734
r"Opening data file '.*\.coverage'\n$",

0 commit comments

Comments
 (0)