Skip to content

Commit 3274cba

Browse files
committed
Fix --source performance regression. #1037
1 parent ae35c27 commit 3274cba

File tree

3 files changed

+29
-11
lines changed

3 files changed

+29
-11
lines changed

CHANGES.rst

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

27-
Nothing yet.
27+
- When using ``--source`` on a large source tree, v5.x was slower than previous
28+
versions. This performance regression is now fixed, closing `issue 1037`_.
29+
30+
.. _issue 1037: https://github.com/nedbat/coveragepy/issues/1037
2831

2932

3033
.. _changes_53:

coverage/control.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"""Core control stuff for coverage.py."""
55

66
import atexit
7+
import collections
78
import contextlib
89
import os
910
import os.path
@@ -737,9 +738,12 @@ def _post_save_work(self):
737738
# Touch all the files that could have executed, so that we can
738739
# mark completely unexecuted files as 0% covered.
739740
if self._data is not None:
741+
file_paths = collections.defaultdict(list)
740742
for file_path, plugin_name in self._inorout.find_possibly_unexecuted_files():
741743
file_path = self._file_mapper(file_path)
742-
self._data.touch_file(file_path, plugin_name)
744+
file_paths[plugin_name].append(file_path)
745+
for plugin_name, paths in file_paths.items():
746+
self._data.touch_files(paths, plugin_name)
743747

744748
if self.config.note:
745749
self._warn("The '[run] note' setting is no longer supported.")

coverage/sqldata.py

+20-9
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,8 @@ class CoverageData(SimpleReprMixin):
167167
To record data for contexts, use :meth:`set_context` to set a context to
168168
be used for subsequent :meth:`add_lines` and :meth:`add_arcs` calls.
169169
170-
To add a source file without any measured data, use :meth:`touch_file`.
170+
To add a source file without any measured data, use :meth:`touch_file`,
171+
or :meth:`touch_files` for a list of such files.
171172
172173
Write the data to its file with :meth:`write`.
173174
@@ -536,16 +537,26 @@ def touch_file(self, filename, plugin_name=""):
536537
`plugin_name` is the name of the plugin responsible for this file. It is used
537538
to associate the right filereporter, etc.
538539
"""
540+
self.touch_files([filename], plugin_name)
541+
542+
def touch_files(self, filenames, plugin_name=""):
543+
"""Ensure that `filenames` appear in the data, empty if needed.
544+
545+
`plugin_name` is the name of the plugin responsible for these files. It is used
546+
to associate the right filereporter, etc.
547+
"""
539548
if self._debug.should('dataop'):
540-
self._debug.write("Touching %r" % (filename,))
549+
self._debug.write("Touching %r" % (filenames,))
541550
self._start_using()
542-
if not self._has_arcs and not self._has_lines:
543-
raise CoverageException("Can't touch files in an empty CoverageData")
544-
545-
self._file_id(filename, add=True)
546-
if plugin_name:
547-
# Set the tracer for this file
548-
self.add_file_tracers({filename: plugin_name})
551+
with self._connect(): # Use this to get one transaction.
552+
if not self._has_arcs and not self._has_lines:
553+
raise CoverageException("Can't touch files in an empty CoverageData")
554+
555+
for filename in filenames:
556+
self._file_id(filename, add=True)
557+
if plugin_name:
558+
# Set the tracer for this file
559+
self.add_file_tracers({filename: plugin_name})
549560

550561
def update(self, other_data, aliases=None):
551562
"""Update this data with data from several other :class:`CoverageData` instances.

0 commit comments

Comments
 (0)