diff options
| author | Ned Batchelder <ned@nedbatchelder.com> | 2019-07-07 07:40:24 -0400 | 
|---|---|---|
| committer | Ned Batchelder <ned@nedbatchelder.com> | 2019-07-07 13:39:13 -0400 | 
| commit | dade9db58673d4f712ba5c50ed66d1c67a7e1d5d (patch) | |
| tree | 1b88b3f99bd3348b566d01943439ccb72b4673ca | |
| parent | 4b43eff1377e818db2c42521d98bf2a0973b1cb6 (diff) | |
| download | python-coveragepy-git-dade9db58673d4f712ba5c50ed66d1c67a7e1d5d.tar.gz | |
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.
| -rw-r--r-- | CHANGES.rst | 2 | ||||
| -rw-r--r-- | coverage/control.py | 5 | ||||
| -rw-r--r-- | coverage/sqldata.py | 20 | ||||
| -rw-r--r-- | tests/test_api.py | 15 | ||||
| -rw-r--r-- | tests/test_debug.py | 10 | 
5 files changed, 28 insertions, 24 deletions
diff --git a/CHANGES.rst b/CHANGES.rst index 52ec0761..ec5fea2d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -32,6 +32,8 @@ Unreleased      information to each covered line.  Hovering over the "ctx" marker at the      end of the line reveals a list of the contexts that covered the line. +- Dynamic contexts with no data are no longer written to the database. +  - Added the classmethod :meth:`Coverage.current` to get the latest started    `Coverage` instance. diff --git a/coverage/control.py b/coverage/control.py index a4e21295..1001a8d6 100644 --- a/coverage/control.py +++ b/coverage/control.py @@ -648,8 +648,9 @@ class Coverage(object):              self._warn("No data was collected.", slug="no-data-collected")          # Find files that were never executed at all. -        for file_path, plugin_name in self._inorout.find_unexecuted_files(): -            self._data.touch_file(file_path, plugin_name) +        if self._data: +            for file_path, plugin_name in self._inorout.find_unexecuted_files(): +                self._data.touch_file(file_path, plugin_name)          if self.config.note:              self._data.add_run_info(note=self.config.note) diff --git a/coverage/sqldata.py b/coverage/sqldata.py index b8efd936..9d44db11 100644 --- a/coverage/sqldata.py +++ b/coverage/sqldata.py @@ -255,8 +255,10 @@ class CoverageSqliteData(SimpleReprMixin):              ))          self._start_using()          self._choose_lines_or_arcs(lines=True) -        self._set_context_id() +        if not line_data: +            return          with self._connect() as con: +            self._set_context_id()              for filename, linenos in iitems(line_data):                  file_id = self._file_id(filename, add=True)                  data = [(file_id, self._current_context_id, lineno) for lineno in linenos] @@ -279,8 +281,10 @@ class CoverageSqliteData(SimpleReprMixin):              ))          self._start_using()          self._choose_lines_or_arcs(arcs=True) -        self._set_context_id() +        if not arc_data: +            return          with self._connect() as con: +            self._set_context_id()              for filename, arcs in iitems(arc_data):                  file_id = self._file_id(filename, add=True)                  data = [(file_id, self._current_context_id, fromno, tono) for fromno, tono in arcs] @@ -307,6 +311,10 @@ class CoverageSqliteData(SimpleReprMixin):          `file_tracers` is { filename: plugin_name, ... }          """ +        if self._debug.should('dataop'): +            self._debug.write("Adding file tracers: %d files" % (len(file_tracers),)) +        if not file_tracers: +            return          self._start_using()          with self._connect() as con:              for filename, plugin_name in iitems(file_tracers): @@ -667,6 +675,12 @@ class CoverageSqliteData(SimpleReprMixin):  class SqliteDb(SimpleReprMixin): +    """A simple abstraction over a SQLite database. + +    Use as a context manager to get an object you can call +    execute or executemany on. + +    """      def __init__(self, filename, debug):          self.debug = debug if debug.should('sql') else None          self.filename = filename @@ -679,7 +693,7 @@ class SqliteDb(SimpleReprMixin):          filename = os.path.relpath(self.filename)          # It can happen that Python switches threads while the tracer writes          # data. The second thread will also try to write to the data, -        # effectively causing a nested context. However, given the indempotent +        # effectively causing a nested context. However, given the idempotent          # nature of the tracer operations, sharing a conenction among threads          # is not a problem.          if self.debug: diff --git a/tests/test_api.py b/tests/test_api.py index 920cd9ad..301257dc 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -326,21 +326,6 @@ class ApiTest(CoverageTest):          with self.assert_warnings(cov, []):              cov.get_data() -    def test_two_getdata_only_warn_once_nostop(self): -        self.make_code1_code2() -        cov = coverage.Coverage(source=["."], omit=["code1.py"]) -        cov.start() -        import_local_file("code1")                                     # pragma: nested -        # We didn't collect any data, so we should get a warning. -        with self.assert_warnings(cov, ["No data was collected"]):     # pragma: nested -            cov.get_data()                                             # pragma: nested -        # But calling get_data a second time with no intervening activity -        # won't make another warning. -        with self.assert_warnings(cov, []):                            # pragma: nested -            cov.get_data()                                             # pragma: nested -        # Then stop it, or the test suite gets out of whack. -        cov.stop()                                                     # pragma: nested -      def test_two_getdata_warn_twice(self):          self.make_code1_code2()          cov = coverage.Coverage(source=["."], omit=["code1.py", "code2.py"]) diff --git a/tests/test_debug.py b/tests/test_debug.py index 4eaba92e..351ef919 100644 --- a/tests/test_debug.py +++ b/tests/test_debug.py @@ -136,16 +136,18 @@ class DebugTraceTest(CoverageTest):          frames = re_lines(out_lines, frame_pattern).splitlines()          self.assertEqual(len(real_messages), len(frames)) -        # The last message should be "Writing data", and the last frame should -        # be _write_file in data.py.          last_line = out_lines.splitlines()[-1] + +        # The details of what to expect on the stack are empirical, and can change +        # as the code changes. This test is here to ensure that the debug code +        # continues working. It's ok to adjust these details over time.          from coverage.data import STORAGE          if STORAGE == "json":              self.assertRegex(real_messages[-1], r"^\s*\d+\.\w{4}: Writing data")              self.assertRegex(last_line, r"\s+_write_file : .*coverage[/\\]data.py @\d+$")          else: -            self.assertRegex(real_messages[-1], r"^\s*\d+\.\w{4}: Creating data file") -            self.assertRegex(last_line, r"\s+_create_db : .*coverage[/\\]sqldata.py @\d+$") +            self.assertRegex(real_messages[-1], r"^\s*\d+\.\w{4}: Adding file tracers: 0 files") +            self.assertRegex(last_line, r"\s+add_file_tracers : .*coverage[/\\]sqldata.py @\d+$")      def test_debug_config(self):          out_lines = self.f1_debug_output(["config"])  | 
