diff options
author | Ned Batchelder <ned@nedbatchelder.com> | 2009-11-23 09:23:01 -0500 |
---|---|---|
committer | Ned Batchelder <ned@nedbatchelder.com> | 2009-11-23 09:23:01 -0500 |
commit | e6505f52fca89539613df3bcd82b39624d14e22f (patch) | |
tree | 3ff8ff626f1e9f8c6cc43fd04990e516e8148517 | |
parent | 7dd0e1ec7abda0f8f9609c10f988792b26a1768e (diff) | |
download | python-coveragepy-git-e6505f52fca89539613df3bcd82b39624d14e22f.tar.gz |
ByteParser now recognizes synthetic 'return None' blocks and treats them correctly.
-rw-r--r-- | coverage/parser.py | 27 | ||||
-rw-r--r-- | test/test_arcs.py | 6 | ||||
-rw-r--r-- | test/test_parser.py | 4 |
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 }) |