summaryrefslogtreecommitdiff
path: root/coverage/collector.py
diff options
context:
space:
mode:
Diffstat (limited to 'coverage/collector.py')
-rw-r--r--coverage/collector.py39
1 files changed, 31 insertions, 8 deletions
diff --git a/coverage/collector.py b/coverage/collector.py
index ea6ef58b..54a4ffef 100644
--- a/coverage/collector.py
+++ b/coverage/collector.py
@@ -10,36 +10,50 @@ except ImportError:
class Tracer:
"""Python implementation of the raw data tracer."""
def __init__(self):
+ self.data = None
+ self.should_trace = None
+ self.should_trace_cache = None
self.cur_filename = None
self.filename_stack = []
def _global_trace(self, frame, event, arg_unused):
"""The trace function passed to sys.settrace."""
if event == 'call':
+ # Entering a new function context. Decide if we should trace
+ # in this file.
filename = frame.f_code.co_filename
tracename = self.should_trace_cache.get(filename)
if tracename is None:
tracename = self.should_trace(filename)
self.should_trace_cache[filename] = tracename
if tracename:
+ # We need to trace. Push the current filename on the stack
+ # and record the new current filename.
self.filename_stack.append(self.cur_filename)
self.cur_filename = tracename
+ # Use _local_trace for tracing within this function.
return self._local_trace
else:
+ # No tracing in this function.
return None
return self._global_trace
def _local_trace(self, frame, event, arg_unused):
+ """The trace function used within a function."""
if event == 'line':
+ # Record an executed line.
self.data[(self.cur_filename, frame.f_lineno)] = True
elif event == 'return':
+ # Leaving this function, pop the filename stack.
self.cur_filename = self.filename_stack.pop()
return self._local_trace
def start(self):
+ """Start this Tracer."""
sys.settrace(self._global_trace)
def stop(self):
+ """Stop this Tracer."""
sys.settrace(None)
@@ -54,47 +68,55 @@ class Collector:
def __init__(self, should_trace):
"""Create a collector.
- `should_trace` is a function, taking a filename, and returns a
- canonicalized filename, or False depending on whether the file should be
- traced or not.
+ `should_trace` is a function, taking a filename, and returning a
+ canonicalized filename, or False depending on whether the file should
+ be traced or not.
"""
self.should_trace = should_trace
self.reset()
def reset(self):
+ """Clear collected data, and prepare to collect more."""
# A dictionary with an entry for (Python source file name, line number
# in that file) if that line has been executed.
self.data = {}
- # A cache of the decision about whether to trace execution in a file.
- # A dict of filename to boolean.
+ # A cache of the results from should_trace, the decision about whether
+ # to trace execution in a file. A dict of filename to (filename or
+ # False).
self.should_trace_cache = {}
+ # The Tracer object on the main thread.
+ self.tracer = None
+
def _start_tracer(self):
+ """Start a new Tracer object, returning it."""
tracer = Tracer()
tracer.data = self.data
tracer.should_trace = self.should_trace
tracer.should_trace_cache = self.should_trace_cache
tracer.start()
return tracer
-
+
# The trace function has to be set individually on each thread before
# execution begins. Ironically, the only support the threading module has
# for running code before the thread main is the tracing function. So we
# install this as a trace function, and the first time it's called, it does
# the real trace installation.
-
+
def _installation_trace(self, frame_unused, event_unused, arg_unused):
"""Called on new threads, installs the real tracer."""
# Remove ourselves as the trace function
sys.settrace(None)
- # Install the real tracer
+ # Install the real tracer.
+ # TODO: Is it OK that these other-thread tracers are never stopped?
self._start_tracer()
# Return None to reiterate that we shouldn't be used for tracing.
return None
def start(self):
+ """Start collecting trace information."""
# Install the tracer on this thread.
self.tracer = self._start_tracer()
# Install our installation tracer in threading, to jump start other
@@ -102,6 +124,7 @@ class Collector:
threading.settrace(self._installation_trace)
def stop(self):
+ """Stop collecting trace information."""
self.tracer.stop()
threading.settrace(None)