summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNed Batchelder <ned@nedbatchelder.com>2020-09-13 15:47:15 -0400
committerNed Batchelder <ned@nedbatchelder.com>2020-09-13 15:48:56 -0400
commit039ef0959c3f21fe0991204c19fb99fab14055f5 (patch)
tree3058128babbc49c88618e696ea28d35445f5763f
parent24eb6fdc8495f969ffeb724f2e96d3941442dd2d (diff)
downloadpython-coveragepy-git-039ef0959c3f21fe0991204c19fb99fab14055f5.tar.gz
If a plugin is disabled, don't try to record its file tracers. #1011
-rw-r--r--CHANGES.rst5
-rw-r--r--coverage/collector.py13
-rw-r--r--coverage/control.py4
-rw-r--r--tests/test_plugins.py32
4 files changed, 53 insertions, 1 deletions
diff --git a/CHANGES.rst b/CHANGES.rst
index 604763f7..29d7fbe3 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -31,7 +31,12 @@ Unreleased
to name a package to disambiguate this case. Thanks, Thomas Grainger. Fixes
`issue 268`_.
+- If a plugin was disabled due to an exception, we used to still try to record
+ its information, causing an exception, as reported in `issue 1011`_. This is
+ now fixed.
+
.. _issue 268: https://github.com/nedbat/coveragepy/issues/268
+.. _issue 1011: https://github.com/nedbat/coveragepy/issues/1011
.. _changes_521:
diff --git a/coverage/collector.py b/coverage/collector.py
index 3545ab1e..9333d66a 100644
--- a/coverage/collector.py
+++ b/coverage/collector.py
@@ -196,6 +196,8 @@ class Collector(object):
# handle them.
self.file_tracers = {}
+ self.disabled_plugins = set()
+
# The .should_trace_cache attribute is a cache from file names to
# coverage.FileDisposition objects, or None. When a file is first
# considered for tracing, a FileDisposition is obtained from
@@ -419,6 +421,10 @@ class Collector(object):
return dict((self.cached_mapped_file(k), v) for k, v in items if v)
+ def plugin_was_disabled(self, plugin):
+ """Record that `plugin` was disabled during the run."""
+ self.disabled_plugins.add(plugin._coverage_plugin_name)
+
def flush_data(self):
"""Save the collected data to our associated `CoverageData`.
@@ -434,7 +440,12 @@ class Collector(object):
self.covdata.add_arcs(self.mapped_file_dict(self.data))
else:
self.covdata.add_lines(self.mapped_file_dict(self.data))
- self.covdata.add_file_tracers(self.mapped_file_dict(self.file_tracers))
+
+ file_tracers = {
+ k: v for k, v in self.file_tracers.items()
+ if v not in self.disabled_plugins
+ }
+ self.covdata.add_file_tracers(self.mapped_file_dict(file_tracers))
self._clear_data()
return True
diff --git a/coverage/control.py b/coverage/control.py
index 7c4a4828..2d75417e 100644
--- a/coverage/control.py
+++ b/coverage/control.py
@@ -709,6 +709,10 @@ class Coverage(object):
self._init_data(suffix=None)
self._post_init()
+ for plugin in self._plugins:
+ if not plugin._coverage_enabled:
+ self._collector.plugin_was_disabled(plugin)
+
if self._collector and self._collector.flush_data():
self._post_save_work()
diff --git a/tests/test_plugins.py b/tests/test_plugins.py
index ed58c5f4..6340f9c3 100644
--- a/tests/test_plugins.py
+++ b/tests/test_plugins.py
@@ -611,6 +611,7 @@ class BadFileTracerTest(FileTracerTest):
cov = coverage.Coverage()
cov.set_option("run:plugins", [module_name])
self.start_import_stop(cov, "simple")
+ cov.save() # pytest-cov does a save after stop, so we'll do it too.
return cov
def run_bad_plugin(self, module_name, plugin_name, our_error=True, excmsg=None, excmsgs=None):
@@ -715,6 +716,37 @@ class BadFileTracerTest(FileTracerTest):
""")
self.run_bad_plugin("bad_plugin", "Plugin")
+ def test_file_tracer_fails_eventually(self):
+ # Django coverage plugin can report on a few files and then fail.
+ # https://github.com/nedbat/coveragepy/issues/1011
+ self.make_file("bad_plugin.py", """\
+ import os.path
+ import coverage.plugin
+ class Plugin(coverage.plugin.CoveragePlugin):
+ def __init__(self):
+ self.calls = 0
+
+ def file_tracer(self, filename):
+ print(filename)
+ self.calls += 1
+ if self.calls <= 2:
+ return FileTracer(filename)
+ else:
+ 17/0 # Oh noes!
+
+ class FileTracer(coverage.FileTracer):
+ def __init__(self, filename):
+ self.filename = filename
+ def source_filename(self):
+ return os.path.basename(self.filename).replace(".py", ".foo")
+ def line_number_range(self, frame):
+ return -1, -1
+
+ def coverage_init(reg, options):
+ reg.add_file_tracer(Plugin())
+ """)
+ self.run_bad_plugin("bad_plugin", "Plugin")
+
def test_file_tracer_returns_wrong(self):
self.make_file("bad_plugin.py", """\
import coverage.plugin