Skip to content

Commit dade9db

Browse files
committed
Avoid useless or redundant db operations. Faster.
Moving operations into the "with self._connect" means less opening and closing of the database. Returning early if there is no data to write avoids writing empty contexts.
1 parent 4b43eff commit dade9db

5 files changed

Lines changed: 28 additions & 24 deletions

File tree

CHANGES.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ Unreleased
3232
information to each covered line. Hovering over the "ctx" marker at the
3333
end of the line reveals a list of the contexts that covered the line.
3434

35+
- Dynamic contexts with no data are no longer written to the database.
36+
3537
- Added the classmethod :meth:`Coverage.current` to get the latest started
3638
`Coverage` instance.
3739

coverage/control.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -648,8 +648,9 @@ def _post_save_work(self):
648648
self._warn("No data was collected.", slug="no-data-collected")
649649

650650
# Find files that were never executed at all.
651-
for file_path, plugin_name in self._inorout.find_unexecuted_files():
652-
self._data.touch_file(file_path, plugin_name)
651+
if self._data:
652+
for file_path, plugin_name in self._inorout.find_unexecuted_files():
653+
self._data.touch_file(file_path, plugin_name)
653654

654655
if self.config.note:
655656
self._data.add_run_info(note=self.config.note)

coverage/sqldata.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -255,8 +255,10 @@ def add_lines(self, line_data):
255255
))
256256
self._start_using()
257257
self._choose_lines_or_arcs(lines=True)
258-
self._set_context_id()
258+
if not line_data:
259+
return
259260
with self._connect() as con:
261+
self._set_context_id()
260262
for filename, linenos in iitems(line_data):
261263
file_id = self._file_id(filename, add=True)
262264
data = [(file_id, self._current_context_id, lineno) for lineno in linenos]
@@ -279,8 +281,10 @@ def add_arcs(self, arc_data):
279281
))
280282
self._start_using()
281283
self._choose_lines_or_arcs(arcs=True)
282-
self._set_context_id()
284+
if not arc_data:
285+
return
283286
with self._connect() as con:
287+
self._set_context_id()
284288
for filename, arcs in iitems(arc_data):
285289
file_id = self._file_id(filename, add=True)
286290
data = [(file_id, self._current_context_id, fromno, tono) for fromno, tono in arcs]
@@ -307,6 +311,10 @@ def add_file_tracers(self, file_tracers):
307311
`file_tracers` is { filename: plugin_name, ... }
308312
309313
"""
314+
if self._debug.should('dataop'):
315+
self._debug.write("Adding file tracers: %d files" % (len(file_tracers),))
316+
if not file_tracers:
317+
return
310318
self._start_using()
311319
with self._connect() as con:
312320
for filename, plugin_name in iitems(file_tracers):
@@ -667,6 +675,12 @@ def run_infos(self):
667675

668676

669677
class SqliteDb(SimpleReprMixin):
678+
"""A simple abstraction over a SQLite database.
679+
680+
Use as a context manager to get an object you can call
681+
execute or executemany on.
682+
683+
"""
670684
def __init__(self, filename, debug):
671685
self.debug = debug if debug.should('sql') else None
672686
self.filename = filename
@@ -679,7 +693,7 @@ def connect(self):
679693
filename = os.path.relpath(self.filename)
680694
# It can happen that Python switches threads while the tracer writes
681695
# data. The second thread will also try to write to the data,
682-
# effectively causing a nested context. However, given the indempotent
696+
# effectively causing a nested context. However, given the idempotent
683697
# nature of the tracer operations, sharing a conenction among threads
684698
# is not a problem.
685699
if self.debug:

tests/test_api.py

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -326,21 +326,6 @@ def test_two_getdata_only_warn_once(self):
326326
with self.assert_warnings(cov, []):
327327
cov.get_data()
328328

329-
def test_two_getdata_only_warn_once_nostop(self):
330-
self.make_code1_code2()
331-
cov = coverage.Coverage(source=["."], omit=["code1.py"])
332-
cov.start()
333-
import_local_file("code1") # pragma: nested
334-
# We didn't collect any data, so we should get a warning.
335-
with self.assert_warnings(cov, ["No data was collected"]): # pragma: nested
336-
cov.get_data() # pragma: nested
337-
# But calling get_data a second time with no intervening activity
338-
# won't make another warning.
339-
with self.assert_warnings(cov, []): # pragma: nested
340-
cov.get_data() # pragma: nested
341-
# Then stop it, or the test suite gets out of whack.
342-
cov.stop() # pragma: nested
343-
344329
def test_two_getdata_warn_twice(self):
345330
self.make_code1_code2()
346331
cov = coverage.Coverage(source=["."], omit=["code1.py", "code2.py"])

tests/test_debug.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -136,16 +136,18 @@ def test_debug_callers(self):
136136
frames = re_lines(out_lines, frame_pattern).splitlines()
137137
self.assertEqual(len(real_messages), len(frames))
138138

139-
# The last message should be "Writing data", and the last frame should
140-
# be _write_file in data.py.
141139
last_line = out_lines.splitlines()[-1]
140+
141+
# The details of what to expect on the stack are empirical, and can change
142+
# as the code changes. This test is here to ensure that the debug code
143+
# continues working. It's ok to adjust these details over time.
142144
from coverage.data import STORAGE
143145
if STORAGE == "json":
144146
self.assertRegex(real_messages[-1], r"^\s*\d+\.\w{4}: Writing data")
145147
self.assertRegex(last_line, r"\s+_write_file : .*coverage[/\\]data.py @\d+$")
146148
else:
147-
self.assertRegex(real_messages[-1], r"^\s*\d+\.\w{4}: Creating data file")
148-
self.assertRegex(last_line, r"\s+_create_db : .*coverage[/\\]sqldata.py @\d+$")
149+
self.assertRegex(real_messages[-1], r"^\s*\d+\.\w{4}: Adding file tracers: 0 files")
150+
self.assertRegex(last_line, r"\s+add_file_tracers : .*coverage[/\\]sqldata.py @\d+$")
149151

150152
def test_debug_config(self):
151153
out_lines = self.f1_debug_output(["config"])

0 commit comments

Comments
 (0)