summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNed Batchelder <ned@nedbatchelder.com>2009-11-23 09:23:01 -0500
committerNed Batchelder <ned@nedbatchelder.com>2009-11-23 09:23:01 -0500
commite6505f52fca89539613df3bcd82b39624d14e22f (patch)
tree3ff8ff626f1e9f8c6cc43fd04990e516e8148517
parent7dd0e1ec7abda0f8f9609c10f988792b26a1768e (diff)
downloadpython-coveragepy-git-e6505f52fca89539613df3bcd82b39624d14e22f.tar.gz
ByteParser now recognizes synthetic 'return None' blocks and treats them correctly.
-rw-r--r--coverage/parser.py27
-rw-r--r--test/test_arcs.py6
-rw-r--r--test/test_parser.py4
3 files changed, 32 insertions, 5 deletions
diff --git a/coverage/parser.py b/coverage/parser.py
index 09050548..3e55ad6d 100644
--- a/coverage/parser.py
+++ b/coverage/parser.py
@@ -273,6 +273,8 @@ OP_BREAK_LOOP = _opcode('BREAK_LOOP')
OP_END_FINALLY = _opcode('END_FINALLY')
OP_COMPARE_OP = _opcode('COMPARE_OP')
COMPARE_EXCEPTION = 10 # just have to get this const from the code.
+OP_LOAD_CONST = _opcode('LOAD_CONST')
+OP_RETURN_VALUE = _opcode('RETURN_VALUE')
class ByteParser(object):
@@ -393,6 +395,9 @@ class ByteParser(object):
# is a count of how many ignores are left.
ignore_branch = 0
+ # We have to handle the last two bytecodes specially.
+ ult = penult = None
+
for bc in ByteCodes(self.code.co_code):
# Maybe have to start a new block
if bc.offset in bytes_lines_map:
@@ -445,8 +450,30 @@ class ByteParser(object):
# This is an except clause. We want to overlook the next
# branch, so that except's don't count as branches.
ignore_branch += 1
+
+ penult = ult
+ ult = bc
+
if chunks:
+ # The last two bytecodes could be a dummy "return None" that
+ # shouldn't be counted as real code. Every Python code object seems
+ # to end with a return, and a "return None" is inserted if there
+ # isn't an explicit return in the source.
+ if ult and penult:
+ if penult.op == OP_LOAD_CONST and ult.op == OP_RETURN_VALUE:
+ if self.code.co_consts[penult.arg] is None:
+ # This is "return None", but is it dummy? A real line
+ # would be a last chunk all by itself.
+ if chunks[-1].byte != penult.offset:
+ last_chunk = chunks[-1]
+ last_chunk.exits.remove(-1)
+ last_chunk.exits.add(penult.offset)
+ chunk = Chunk(penult.offset)
+ chunk.exits.add(-1)
+ chunks.append(chunk)
+
+ # Give all the chunks a length.
chunks[-1].length = bc.next_offset - chunks[-1].byte
for i in range(len(chunks)-1):
chunks[i].length = chunks[i+1].byte - chunks[i].byte
diff --git a/test/test_arcs.py b/test/test_arcs.py
index 9b2c386f..47a69987 100644
--- a/test/test_arcs.py
+++ b/test/test_arcs.py
@@ -113,7 +113,7 @@ class SimpleArcTest(CoverageTest):
arcz=".1 16 67 7. .2 23 24 3. 45 5.", arcz_missing=""
)
- def XXX_dont_confuse_exit_and_else(self):
+ def test_dont_confuse_exit_and_else(self):
self.check_coverage("""\
def foo():
if foo:
@@ -121,7 +121,7 @@ class SimpleArcTest(CoverageTest):
else:
a = 5
return a
- assert foo() == 3
+ assert foo() == 3 # 7
""",
arcz=".1 17 7. .2 23 36 25 56 6.", arcz_missing="25 56"
)
@@ -131,7 +131,7 @@ class SimpleArcTest(CoverageTest):
a = 3
else:
a = 5
- foo()
+ foo() # 6
""",
arcz=".1 16 6. .2 23 3. 25 5.", arcz_missing="25 5."
)
diff --git a/test/test_parser.py b/test/test_parser.py
index 9f0f00b0..0dc7089c 100644
--- a/test/test_parser.py
+++ b/test/test_parser.py
@@ -67,7 +67,7 @@ class ParserTest(CoverageTest):
1:0, 2:1, 3:1
})
- def XXX_missing_branch_to_excluded_code(self):
+ def test_missing_branch_to_excluded_code(self):
cp = self.parse_source("""\
if fooey:
a = 2
@@ -93,4 +93,4 @@ class ParserTest(CoverageTest):
a = 5
b = 6
""")
- self.assertEqual(cp.exit_counts(), { 1:13, 2:1, 6:1 })
+ self.assertEqual(cp.exit_counts(), { 1:1, 2:1, 3:1, 6:1 })