diff options
Diffstat (limited to 'coverage')
-rw-r--r-- | coverage/collector.py | 2 | ||||
-rw-r--r-- | coverage/control.py | 25 | ||||
-rw-r--r-- | coverage/plugin.py | 52 | ||||
-rw-r--r-- | coverage/pytracer.py | 13 |
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: |