summaryrefslogtreecommitdiff
path: root/coverage
diff options
context:
space:
mode:
Diffstat (limited to 'coverage')
-rw-r--r--coverage/codeunit.py26
-rw-r--r--coverage/control.py32
-rw-r--r--coverage/plugin.py40
-rw-r--r--coverage/pytracer.py24
-rw-r--r--coverage/results.py3
-rw-r--r--coverage/tracer.c12
6 files changed, 76 insertions, 61 deletions
diff --git a/coverage/codeunit.py b/coverage/codeunit.py
index c9ab2622..da617913 100644
--- a/coverage/codeunit.py
+++ b/coverage/codeunit.py
@@ -32,24 +32,16 @@ def code_unit_factory(morfs, file_locator, get_plugin=None):
if isinstance(morf, string_class) and get_plugin:
plugin = get_plugin(morf)
if plugin:
- klass = plugin.code_unit_class(morf)
- #klass = DjangoTracer # NOT REALLY! TODO
- # Hacked-in Mako support. Define COVERAGE_MAKO_PATH as a fragment of
- # the path that indicates the Python file is actually a compiled Mako
- # template. THIS IS TEMPORARY!
- #MAKO_PATH = os.environ.get('COVERAGE_MAKO_PATH')
- #if MAKO_PATH and isinstance(morf, string_class) and MAKO_PATH in morf:
- # # Super hack! Do mako both ways!
- # if 0:
- # cu = PythonCodeUnit(morf, file_locator)
- # cu.name += '_fako'
- # code_units.append(cu)
- # klass = MakoCodeUnit
- #elif isinstance(morf, string_class) and morf.endswith(".html"):
- # klass = DjangoCodeUnit
+ file_reporter = plugin.file_reporter(morf)
+ if file_reporter is None:
+ raise CoverageException(
+ "Plugin %r did not provide a file reporter for %r." % (
+ plugin.plugin_name, morf
+ )
+ )
else:
- klass = PythonCodeUnit
- code_units.append(klass(morf, file_locator))
+ file_reporter = PythonCodeUnit(morf, file_locator)
+ code_units.append(file_reporter)
return code_units
diff --git a/coverage/control.py b/coverage/control.py
index c31f15fa..66979c33 100644
--- a/coverage/control.py
+++ b/coverage/control.py
@@ -142,7 +142,7 @@ class Coverage(object):
self.omit = self.include = self.source = None
self.source_pkgs = self.file_locator = None
self.data = self.collector = None
- self.plugins = self.trace_judges = None
+ self.plugins = self.file_tracers = None
self.pylib_dirs = self.cover_dir = None
self.data_suffix = self.run_suffix = None
self._exclude_re = None
@@ -177,11 +177,11 @@ class Coverage(object):
# Load plugins
self.plugins = Plugins.load_plugins(self.config.plugins, self.config)
- self.trace_judges = []
+ self.file_tracers = []
for plugin in self.plugins:
- if plugin_implements(plugin, "trace_judge"):
- self.trace_judges.append(plugin)
- self.trace_judges.append(None) # The Python case.
+ if plugin_implements(plugin, "file_tracer"):
+ self.file_tracers.append(plugin)
+ self.file_tracers.append(None) # The Python case.
# _exclude_re is a dict mapping exclusion list names to compiled
# regexes.
@@ -325,15 +325,27 @@ class Coverage(object):
disp.canonical_filename = canonical
# Try the plugins, see if they have an opinion about the file.
- for plugin in self.trace_judges:
+ for plugin in self.file_tracers:
if plugin:
- plugin.trace_judge(disp)
+ #plugin.trace_judge(disp)
+ file_tracer = plugin.file_tracer(canonical)
+ if file_tracer is not None:
+ file_tracer.plugin_name = plugin.plugin_name
+ disp.trace = True
+ disp.file_tracer = file_tracer
+ disp.source_filename = self.file_locator.canonical_filename(file_tracer.source_filename())
else:
disp.trace = True
disp.source_filename = canonical
+ file_tracer = None
if disp.trace:
- disp.plugin = plugin
-
+ if file_tracer:
+ disp.file_tracer = file_tracer
+ if disp.source_filename is None:
+ raise CoverageException(
+ "Plugin %r didn't set source_filename for %r" %
+ (plugin, disp.original_filename)
+ )
if disp.check_filters:
reason = self._check_include_omit_etc(disp.source_filename)
if reason:
@@ -886,7 +898,7 @@ class FileDisposition(object):
self.check_filters = True
self.trace = False
self.reason = ""
- self.plugin = None
+ self.file_tracer = None
def debug_message(self):
"""Produce a debugging message explaining the outcome."""
diff --git a/coverage/plugin.py b/coverage/plugin.py
index 35be41a9..5d1c5306 100644
--- a/coverage/plugin.py
+++ b/coverage/plugin.py
@@ -2,28 +2,33 @@
import sys
+from coverage.misc import CoverageException
+
class CoveragePlugin(object):
"""Base class for coverage.py plugins."""
def __init__(self, options):
self.options = options
- def trace_judge(self, disposition):
- """Decide whether to trace this file with this plugin.
+ def file_tracer(self, filename):
+ """Return a FileTracer object for this file."""
+ return None
+
+ def file_reporter(self, filename):
+ """Return the FileReporter class to use for filename.
- Set disposition.trace to True if this plugin should trace this file.
- May also set other attributes in `disposition`.
+ This will only be invoked if `filename` returns non-None from
+ `file_tracer`. It's an error to return None.
"""
- return None
+ raise Exception("Plugin %r needs to implement file_reporter" % self.plugin_name)
- def source_file_name(self, filename):
- """Return the source name for a given Python filename.
- Can return None if tracing shouldn't continue.
+class FileTracer(object):
+ """Support needed for files during the tracing phase."""
- """
- return filename
+ def source_filename(self):
+ return "xyzzy"
def dynamic_source_file_name(self):
"""Returns a callable that can return a source name for a frame.
@@ -38,9 +43,16 @@ class CoveragePlugin(object):
"""
return None
- def code_unit_class(self, morf):
- """Return the CodeUnit class to use for a module or filename."""
- return None
+ def line_number_range(self, frame):
+ """Given a call frame, return the range of source line numbers."""
+ lineno = frame.f_lineno
+ return lineno, lineno
+
+
+class FileReporter(object):
+ """Support needed for files during the reporting phase."""
+ def __init__(self, filename):
+ self.filename = filename
class Plugins(object):
@@ -67,7 +79,7 @@ class Plugins(object):
if plugin_class:
options = config.get_plugin_options(module)
plugin = plugin_class(options)
- plugin.__name__ = module
+ plugin.plugin_name = module
plugins.order.append(plugin)
plugins.names[module] = plugin
diff --git a/coverage/pytracer.py b/coverage/pytracer.py
index 7563ae11..84071bb1 100644
--- a/coverage/pytracer.py
+++ b/coverage/pytracer.py
@@ -33,7 +33,7 @@ class PyTracer(object):
# The threading module to use, if any.
self.threading = None
- self.plugin = []
+ self.file_tracer = []
self.cur_file_dict = []
self.last_line = [0]
@@ -62,7 +62,7 @@ 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.plugin, self.cur_file_dict, self.last_line = (
+ self.file_tracer, self.cur_file_dict, self.last_line = (
self.data_stack.pop()
)
self.last_exc_back = None
@@ -71,7 +71,7 @@ class PyTracer(object):
# Entering a new function context. Decide if we should trace
# in this file.
self.data_stack.append(
- (self.plugin, self.cur_file_dict, self.last_line)
+ (self.file_tracer, self.cur_file_dict, self.last_line)
)
filename = frame.f_code.co_filename
disp = self.should_trace_cache.get(filename)
@@ -79,12 +79,12 @@ class PyTracer(object):
disp = self.should_trace(filename, frame)
self.should_trace_cache[filename] = disp
- self.plugin = None
+ self.file_tracer = None
self.cur_file_dict = None
if disp.trace:
tracename = disp.source_filename
- if disp.plugin:
- dyn_func = disp.plugin.dynamic_source_file_name()
+ if disp.file_tracer:
+ dyn_func = disp.file_tracer.dynamic_source_file_name()
if dyn_func:
tracename = dyn_func(tracename, frame)
if tracename:
@@ -95,17 +95,17 @@ class PyTracer(object):
if tracename:
if tracename not in self.data:
self.data[tracename] = {}
- if disp.plugin:
- self.plugin_data[tracename] = disp.plugin.__name__
+ if disp.file_tracer:
+ self.plugin_data[tracename] = disp.file_tracer.plugin_name
self.cur_file_dict = self.data[tracename]
- self.plugin = disp.plugin
+ 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.plugin:
- lineno_from, lineno_to = self.plugin.line_number_range(frame)
+ if self.file_tracer:
+ lineno_from, lineno_to = self.file_tracer.line_number_range(frame)
else:
lineno_from, lineno_to = frame.f_lineno, frame.f_lineno
if lineno_from != -1:
@@ -123,7 +123,7 @@ class PyTracer(object):
first = frame.f_code.co_firstlineno
self.cur_file_dict[(self.last_line, -first)] = None
# Leaving this function, pop the filename stack.
- self.plugin, self.cur_file_dict, self.last_line = (
+ self.file_tracer, self.cur_file_dict, self.last_line = (
self.data_stack.pop()
)
elif event == 'exception':
diff --git a/coverage/results.py b/coverage/results.py
index 6cbcbfc8..4449de56 100644
--- a/coverage/results.py
+++ b/coverage/results.py
@@ -11,9 +11,8 @@ class Analysis(object):
def __init__(self, cov, code_unit):
self.coverage = cov
- self.code_unit = code_unit
- self.filename = self.code_unit.filename
+ self.filename = code_unit.filename
self.parser = code_unit.get_parser(
exclude=self.coverage._exclude_regex('exclude')
)
diff --git a/coverage/tracer.c b/coverage/tracer.c
index dcc3b727..5a463fcb 100644
--- a/coverage/tracer.c
+++ b/coverage/tracer.c
@@ -505,7 +505,7 @@ CTracer_trace(CTracer *self, PyFrameObject *frame, int what, PyObject *arg_unuse
if (MyText_Check(tracename)) {
PyObject * file_data = PyDict_GetItem(self->data, tracename);
- PyObject * disp_plugin = NULL;
+ PyObject * disp_file_tracer = NULL;
PyObject * disp_plugin_name = NULL;
if (file_data == NULL) {
@@ -527,16 +527,16 @@ CTracer_trace(CTracer *self, PyFrameObject *frame, int what, PyObject *arg_unuse
if (self->plugin_data != NULL) {
/* If the disposition mentions a plugin, record that. */
- disp_plugin = PyObject_GetAttrString(disposition, "plugin");
- if (disp_plugin == NULL) {
+ disp_file_tracer = PyObject_GetAttrString(disposition, "file_tracer");
+ if (disp_file_tracer == NULL) {
STATS( self->stats.errors++; )
Py_DECREF(tracename);
Py_DECREF(disposition);
return RET_ERROR;
}
- if (disp_plugin != Py_None) {
- disp_plugin_name = PyObject_GetAttrString(disp_plugin, "__name__");
- Py_DECREF(disp_plugin);
+ if (disp_file_tracer != Py_None) {
+ disp_plugin_name = PyObject_GetAttrString(disp_file_tracer, "plugin_name");
+ Py_DECREF(disp_file_tracer);
if (disp_plugin_name == NULL) {
STATS( self->stats.errors++; )
Py_DECREF(tracename);