diff options
author | Ned Batchelder <ned@nedbatchelder.com> | 2009-07-04 19:23:20 -0400 |
---|---|---|
committer | Ned Batchelder <ned@nedbatchelder.com> | 2009-07-04 19:23:20 -0400 |
commit | 5a44a2097a3c7355c15b927ec1eb2168861c865d (patch) | |
tree | 18546b2ed69a1e2460e91d3d13bd74825917f25b /coverage/collector.py | |
parent | 9e37d56c3a485991f2af2b362581c3af7e2065ce (diff) | |
download | python-coveragepy-git-5a44a2097a3c7355c15b927ec1eb2168861c865d.tar.gz |
Fix Python 2.3's exception tracing by always using a dumbed-down Python tracer there.
Diffstat (limited to 'coverage/collector.py')
-rw-r--r-- | coverage/collector.py | 70 |
1 files changed, 52 insertions, 18 deletions
diff --git a/coverage/collector.py b/coverage/collector.py index f290b85b..d196062b 100644 --- a/coverage/collector.py +++ b/coverage/collector.py @@ -2,12 +2,28 @@ import sys, threading -try: - # Use the C extension code when we can, for speed. - from coverage.tracer import Tracer -except ImportError: - # If we don't have the C tracer, use this Python one. - class Tracer: +# Before Python 2.4, the trace function could be called with "call", and then +# leave the scope without calling "return", for example when an exception +# caused the scope to end. We use the matching call and return traces to do +# bookkeeping for speed, so we need to know up front if they match our +# expectations. +CALL_AND_RETURN_MATCH = (sys.hexversion >= 0x02040000) + + +Tracer = None +if CALL_AND_RETURN_MATCH: + # We have matching calls and returns, so we can use the C extension. + try: + # Use the C extension code when we can, for speed. + from coverage.tracer import Tracer + except ImportError: + # Couldn't import the C extension, maybe it isn't built. + pass + + +if not Tracer: + # If for whatever reason we don't have a Tracer yet, use this Python one. + class Tracer: # pylint: disable-msg=E0102 """Python implementation of the raw data tracer.""" def __init__(self): self.data = None @@ -37,25 +53,43 @@ except ImportError: # 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 - + + if CALL_AND_RETURN_MATCH: + 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 + else: + def _local_trace(self, frame, event, arg_unused): + """The trace function used within a function.""" + if event == 'line': + if not self.cur_filename: + # Kinda hacky: call _global_trace to do the work of + # setting the cur_filename. + if not self._global_trace(frame, 'call', None): + return None + # Record an executed line. + self.data[(self.cur_filename, frame.f_lineno)] = True + elif event == 'return' or event == 'exception': + # Leaving this function, discard the current filename so + # we'll re-compute it at the next line. + self.cur_filename = None + return self._local_trace + def start(self): """Start this Tracer.""" sys.settrace(self._global_trace) - + def stop(self): """Stop this Tracer.""" sys.settrace(None) + class Collector: """Collects trace data. |