summaryrefslogtreecommitdiff
path: root/coverage
diff options
context:
space:
mode:
Diffstat (limited to 'coverage')
-rw-r--r--coverage/collector.py2
-rw-r--r--coverage/control.py25
-rw-r--r--coverage/plugin.py52
-rw-r--r--coverage/pytracer.py13
4 files changed, 67 insertions, 25 deletions
diff --git a/coverage/collector.py b/coverage/collector.py
index 72a7b7f1..6ffdc7c4 100644
--- a/coverage/collector.py
+++ b/coverage/collector.py
@@ -186,6 +186,8 @@ class Collector(object):
tracer.plugin_data = self.plugin_data
if hasattr(tracer, 'threading'):
tracer.threading = self.threading
+ if hasattr(tracer, 'check_include'):
+ tracer.check_include = self.check_include
fn = tracer.start()
self.tracers.append(tracer)
diff --git a/coverage/control.py b/coverage/control.py
index 550293c7..64175ee4 100644
--- a/coverage/control.py
+++ b/coverage/control.py
@@ -360,7 +360,10 @@ class Coverage(object):
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())
+ if file_tracer.has_dynamic_source_filename():
+ disp.has_dynamic_filename = True
+ else:
+ disp.source_filename = self.file_locator.canonical_filename(file_tracer.source_filename())
else:
disp.trace = True
disp.source_filename = canonical
@@ -368,15 +371,16 @@ class Coverage(object):
if disp.trace:
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:
- nope(disp, reason)
+ if not disp.has_dynamic_filename:
+ 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:
+ nope(disp, reason)
return disp
@@ -903,6 +907,7 @@ class FileDisposition(object):
self.trace = False
self.reason = ""
self.file_tracer = None
+ self.has_dynamic_filename = False
def debug_message(self):
"""Produce a debugging message explaining the outcome."""
diff --git a/coverage/plugin.py b/coverage/plugin.py
index 1e6e2353..24a2b9a3 100644
--- a/coverage/plugin.py
+++ b/coverage/plugin.py
@@ -41,22 +41,58 @@ class CoveragePlugin(object):
`file_tracer`. It's an error to return None.
"""
- raise Exception("Plugin %r needs to implement file_reporter" % self.plugin_name)
+ raise NotImplementedError("Plugin %r needs to implement file_reporter" % self.plugin_name)
class FileTracer(object):
- """Support needed for files during the tracing phase."""
+ """Support needed for files during the tracing phase.
+
+ You may construct this object from CoveragePlugin.file_tracer any way you
+ like. A natural choice would be to pass the filename given to file_tracer.
+
+ """
def source_filename(self):
- return "xyzzy"
+ """The source filename for this file.
+
+ This may be any filename you like. A key responsibility of a plugin is
+ to own the mapping from Python execution back to whatever source
+ filename was originally the source of the code.
+
+ Returns:
+ The filename to credit with this execution.
+
+ """
+ return None
- def dynamic_source_file_name(self):
- """Returns a callable that can return a source name for a frame.
+ def has_dynamic_source_filename(self):
+ """Does this FileTracer have dynamic source filenames?
- The callable should take a filename and a frame, and return either a
- filename or None:
+ FileTracers can provide dynamically determined filenames by implementing
+ dynamic_source_filename. Invoking that function is expensive. To
+ determine whether it should invoke it, coverage.py uses the result of
+ this function to know if it needs to bother invoking
+ dynamic_source_filename.
- def dynamic_source_filename_func(filename, frame)
+ Returns:
+ A boolean, true if `dynamic_source_filename` should be called to
+ get dynamic source filenames.
+
+ """
+ return False
+
+ def dynamic_source_filename(self, filename, frame):
+ """Returns a dynamically computed source filename.
+
+ Some plugins need to compute the source filename dynamically for each
+ frame.
+
+ This function will not be invoked if `has_dynamic_source_filename`
+ returns False.
+
+ Returns:
+ The source filename for this frame, or None if this frame shouldn't
+ be measured.
Can return None if dynamic filenames aren't needed.
diff --git a/coverage/pytracer.py b/coverage/pytracer.py
index 84071bb1..b4fd59fa 100644
--- a/coverage/pytracer.py
+++ b/coverage/pytracer.py
@@ -28,6 +28,7 @@ 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.
@@ -83,13 +84,11 @@ class PyTracer(object):
self.cur_file_dict = None
if disp.trace:
tracename = disp.source_filename
- if disp.file_tracer:
- dyn_func = disp.file_tracer.dynamic_source_file_name()
- if dyn_func:
- tracename = dyn_func(tracename, frame)
- if tracename:
- if not self.check_include(tracename):
- tracename = None
+ if disp.file_tracer and disp.has_dynamic_filename:
+ tracename = disp.file_tracer.dynamic_source_filename(tracename, frame)
+ if tracename:
+ if not self.check_include(tracename):
+ tracename = None
else:
tracename = None
if tracename: