diff options
Diffstat (limited to 'coverage')
-rw-r--r-- | coverage/codeunit.py | 26 | ||||
-rw-r--r-- | coverage/control.py | 32 | ||||
-rw-r--r-- | coverage/plugin.py | 40 | ||||
-rw-r--r-- | coverage/pytracer.py | 24 | ||||
-rw-r--r-- | coverage/results.py | 3 | ||||
-rw-r--r-- | coverage/tracer.c | 12 |
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); |