diff options
author | Ned Batchelder <ned@nedbatchelder.com> | 2009-07-06 10:17:49 -0400 |
---|---|---|
committer | Ned Batchelder <ned@nedbatchelder.com> | 2009-07-06 10:17:49 -0400 |
commit | f51e99f72441fc2eeb6820a3cfbfa0ee312052c7 (patch) | |
tree | 5db7e3972f03bff0e15e65997b73461870adeff5 /coverage/collector.py | |
parent | 5a44a2097a3c7355c15b927ec1eb2168861c865d (diff) | |
download | python-coveragepy-git-f51e99f72441fc2eeb6820a3cfbfa0ee312052c7.tar.gz |
A better way to fix the missing-return-after-exception problem in the trace function: no pyexpat specifics, and py 2.3 still uses C trace function.
Diffstat (limited to 'coverage/collector.py')
-rw-r--r-- | coverage/collector.py | 73 |
1 files changed, 25 insertions, 48 deletions
diff --git a/coverage/collector.py b/coverage/collector.py index d196062b..0a5349ad 100644 --- a/coverage/collector.py +++ b/coverage/collector.py @@ -2,28 +2,13 @@ import sys, threading -# 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 +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. + + class Tracer: """Python implementation of the raw data tracer.""" def __init__(self): self.data = None @@ -31,7 +16,8 @@ if not Tracer: self.should_trace_cache = None self.cur_filename = None self.filename_stack = [] - + self.last_exc_back = None + def _global_trace(self, frame, event, arg_unused): """The trace function passed to sys.settrace.""" if event == 'call': @@ -54,32 +40,23 @@ if not Tracer: return None return self._global_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. + def _local_trace(self, frame, event, arg_unused): + """The trace function used within a function.""" + if self.last_exc_back: + if frame == self.last_exc_back: + # Someone forgot a return event. 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 + self.last_exc_back = None + + 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() + elif event == 'exception': + self.last_exc_back = frame.f_back + return self._local_trace def start(self): """Start this Tracer.""" |