summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.txt3
-rw-r--r--TODO.txt2
-rw-r--r--coverage/collector.py2
-rw-r--r--coverage/control.py9
-rw-r--r--coverage/pytracer.py48
-rw-r--r--coverage/test_helpers.py2
-rw-r--r--tests/test_plugins.py27
7 files changed, 50 insertions, 43 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 00ad1a3..3650a5a 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -6,7 +6,8 @@ Change history for Coverage.py
Latest
------
-- Plugin support is now implemented in the C tracer and the Python tracer.
+- Plugin support is now implemented in the C tracer instead of the Python
+ tracer.
- Added 3.5.0a1 to the list of supported versions.
diff --git a/TODO.txt b/TODO.txt
index da59db7..3ff9613 100644
--- a/TODO.txt
+++ b/TODO.txt
@@ -30,7 +30,7 @@ Key:
- Plugins
- Clean up
+ implement plugin support in CTracer
- - remove plugin support from PyTracer
+ + remove plugin support from PyTracer
- add services:
- filelocator
- warning
diff --git a/coverage/collector.py b/coverage/collector.py
index ded6d92..0348bb7 100644
--- a/coverage/collector.py
+++ b/coverage/collector.py
@@ -119,6 +119,8 @@ class Collector(object):
# trace function.
self._trace_class = CTracer or PyTracer
+ self.supports_plugins = self._trace_class is CTracer
+
def __repr__(self):
return "<Collector at 0x%x>" % id(self)
diff --git a/coverage/control.py b/coverage/control.py
index 1b21c3b..bb26b78 100644
--- a/coverage/control.py
+++ b/coverage/control.py
@@ -227,6 +227,15 @@ class Coverage(object):
concurrency=concurrency,
)
+ # Early warning if we aren't going to be able to support plugins.
+ if self.file_tracers and not self.collector.supports_plugins:
+ raise CoverageException(
+ "Plugin file tracers (%s) aren't supported with %s" % (
+ ", ".join(ft.plugin_name for ft in self.file_tracers),
+ self.collector.tracer_name(),
+ )
+ )
+
# Suffixes are a bit tricky. We want to use the data suffix only when
# collecting data, not when combining data. So we save it as
# `self.run_suffix` now, and promote it to `self.data_suffix` if we
diff --git a/coverage/pytracer.py b/coverage/pytracer.py
index 16a51c2..7029c25 100644
--- a/coverage/pytracer.py
+++ b/coverage/pytracer.py
@@ -28,13 +28,10 @@ class PyTracer(object):
self.arcs = False
self.should_trace = None
self.should_trace_cache = None
- self.check_include = None
self.warn = None
- self.plugin_data = None
# The threading module to use, if any.
self.threading = None
- self.file_tracer = []
self.cur_file_dict = []
self.last_line = [0]
@@ -63,72 +60,43 @@ class PyTracer(object):
if self.arcs and self.cur_file_dict:
pair = (self.last_line, -self.last_exc_firstlineno)
self.cur_file_dict[pair] = None
- self.file_tracer, self.cur_file_dict, self.last_line = (
- self.data_stack.pop()
- )
+ self.cur_file_dict, self.last_line = self.data_stack.pop()
self.last_exc_back = None
if event == 'call':
# Entering a new function context. Decide if we should trace
# in this file.
- self.data_stack.append(
- (self.file_tracer, self.cur_file_dict, self.last_line)
- )
+ self.data_stack.append((self.cur_file_dict, self.last_line))
filename = frame.f_code.co_filename
disp = self.should_trace_cache.get(filename)
if disp is None:
disp = self.should_trace(filename, frame)
self.should_trace_cache[filename] = disp
- self.file_tracer = None
self.cur_file_dict = None
if disp.trace:
tracename = disp.source_filename
- if disp.file_tracer and disp.has_dynamic_filename:
- tracename = disp.file_tracer.dynamic_source_filename(tracename, frame)
- if tracename:
- included = self.should_trace_cache.get(tracename)
- if included is None:
- included = self.check_include(tracename, frame)
- self.should_trace_cache[tracename] = included
- if not included:
- tracename = None
- else:
- tracename = None
- if tracename:
if tracename not in self.data:
self.data[tracename] = {}
- if disp.file_tracer:
- self.plugin_data[tracename] = disp.file_tracer.plugin_name
self.cur_file_dict = self.data[tracename]
- self.file_tracer = disp.file_tracer
# Set the last_line to -1 because the next arc will be entering a
# code block, indicated by (-1, n).
self.last_line = -1
elif event == 'line':
# Record an executed line.
if self.cur_file_dict is not None:
- if self.file_tracer:
- lineno_from, lineno_to = self.file_tracer.line_number_range(frame)
+ lineno = frame.f_lineno
+ if self.arcs:
+ self.cur_file_dict[(self.last_line, lineno)] = None
else:
- lineno_from, lineno_to = frame.f_lineno, frame.f_lineno
- if lineno_from != -1:
- if self.arcs:
- self.cur_file_dict[
- (self.last_line, lineno_from)
- ] = None
- else:
- for lineno in range(lineno_from, lineno_to+1):
- self.cur_file_dict[lineno] = None
- self.last_line = lineno_to
+ self.cur_file_dict[lineno] = None
+ self.last_line = lineno
elif event == 'return':
if self.arcs and self.cur_file_dict:
first = frame.f_code.co_firstlineno
self.cur_file_dict[(self.last_line, -first)] = None
# Leaving this function, pop the filename stack.
- self.file_tracer, self.cur_file_dict, self.last_line = (
- self.data_stack.pop()
- )
+ self.cur_file_dict, self.last_line = self.data_stack.pop()
elif event == 'exception':
self.last_exc_back = frame.f_back
self.last_exc_firstlineno = frame.f_code.co_firstlineno
diff --git a/coverage/test_helpers.py b/coverage/test_helpers.py
index 65d9977..e9ad377 100644
--- a/coverage/test_helpers.py
+++ b/coverage/test_helpers.py
@@ -249,7 +249,7 @@ class TempDirMixin(SysPathAwareMixin, ModuleAwareMixin, TestCase):
def report_on_class_behavior(cls):
"""Called at process exit to report on class behavior."""
for test_class, behavior in cls.class_behaviors.items():
- if behavior.tests == behavior.skipped:
+ if behavior.tests <= behavior.skipped:
bad = ""
elif behavior.temp_dir and behavior.tests_making_files == 0:
bad = "Inefficient"
diff --git a/tests/test_plugins.py b/tests/test_plugins.py
index 83dc4a1..2b4b2ee 100644
--- a/tests/test_plugins.py
+++ b/tests/test_plugins.py
@@ -3,6 +3,7 @@
import os.path
import coverage
+from coverage import env
from coverage.backward import StringIO
from coverage.control import Plugins
from coverage.misc import CoverageException
@@ -198,9 +199,35 @@ class PluginTest(CoverageTest):
self.assertEqual(expected_end, out_lines[-len(expected_end):])
+class PluginWarningOnPyTracer(CoverageTest):
+ """Test that we get a controlled exception with plugins on PyTracer."""
+ def setUp(self):
+ super(PluginWarningOnPyTracer, self).setUp()
+ if env.C_TRACER:
+ self.skip("This test is only about PyTracer.")
+
+ def test_exception_if_plugins_on_pytracer(self):
+ self.make_file("simple.py", """a = 1""")
+
+ cov = coverage.Coverage()
+ cov.config["run:plugins"] = ["tests.plugin1"]
+
+ msg = (
+ r"Plugin file tracers \(tests.plugin1\) "
+ r"aren't supported with PyTracer"
+ )
+ with self.assertRaisesRegex(CoverageException, msg):
+ self.start_import_stop(cov, "simple")
+
+
class FileTracerTest(CoverageTest):
"""Tests of plugins that implement file_tracer."""
+ def setUp(self):
+ super(FileTracerTest, self).setUp()
+ if not env.C_TRACER:
+ self.skip("Plugins are only supported with the C tracer.")
+
def test_plugin1(self):
self.make_file("simple.py", """\
import try_xyz