summaryrefslogtreecommitdiff
path: root/coverage/pytracer.py
diff options
context:
space:
mode:
authorNed Batchelder <ned@nedbatchelder.com>2015-04-20 12:21:15 -0400
committerNed Batchelder <ned@nedbatchelder.com>2015-04-20 12:21:15 -0400
commit1b81747e52752a30be1c4271e24d23a7cb3f71b4 (patch)
tree8297b06a003a0c1a79c2bf391d2c0f0cfb128d81 /coverage/pytracer.py
parent03eb833bac7731bd6dfd4ca5d0eae1da7213eb57 (diff)
parentde4cfde7b1f7b3d3bee11a26b4c1bb3ae598259c (diff)
downloadpython-coveragepy-git-1b81747e52752a30be1c4271e24d23a7cb3f71b4.tar.gz
Merge issue-324 fix
Diffstat (limited to 'coverage/pytracer.py')
-rw-r--r--coverage/pytracer.py24
1 files changed, 19 insertions, 5 deletions
diff --git a/coverage/pytracer.py b/coverage/pytracer.py
index 0eafbef0..3f03aaf7 100644
--- a/coverage/pytracer.py
+++ b/coverage/pytracer.py
@@ -1,7 +1,15 @@
"""Raw data collector for Coverage."""
+import dis
import sys
+from coverage import env
+
+# We need the YIELD_VALUE opcode below, in a comparison-friendly form.
+YIELD_VALUE = dis.opmap['YIELD_VALUE']
+if env.PY2:
+ YIELD_VALUE = chr(YIELD_VALUE)
+
class PyTracer(object):
"""Python implementation of the raw data tracer."""
@@ -79,9 +87,11 @@ class PyTracer(object):
if tracename not in self.data:
self.data[tracename] = {}
self.cur_file_dict = self.data[tracename]
- # Set the last_line to -1 because the next arc will be entering a
- # code block, indicated by (-1, n).
- self.last_line = -1
+ # The call event is really a "start frame" event, and happens for
+ # function calls and re-entering generators. The f_lasti field is
+ # -1 for calls, and a real offset for generators. Use -1 as the
+ # line number for calls, and the real line number for generators.
+ self.last_line = -1 if (frame.f_lasti < 0) else frame.f_lineno
elif event == 'line':
# Record an executed line.
if self.cur_file_dict is not None:
@@ -93,8 +103,12 @@ class PyTracer(object):
self.last_line = lineno
elif event == 'return':
if self.arcs and self.cur_file_dict:
- first = frame.f_code.co_firstlineno
- self.cur_file_dict[(self.last_line, -first)] = None
+ # Record an arc leaving the function, but beware that a
+ # "return" event might just mean yielding from a generator.
+ bytecode = frame.f_code.co_code[frame.f_lasti]
+ if bytecode != YIELD_VALUE:
+ first = frame.f_code.co_firstlineno
+ self.cur_file_dict[(self.last_line, -first)] = None
# Leaving this function, pop the filename stack.
self.cur_file_dict, self.last_line = self.data_stack.pop()
elif event == 'exception':