diff options
author | Ned Batchelder <ned@nedbatchelder.com> | 2014-11-29 08:55:08 -0500 |
---|---|---|
committer | Ned Batchelder <ned@nedbatchelder.com> | 2014-11-29 08:55:08 -0500 |
commit | 3f544ee33de31e96ca2d25d95da614e9a7a12f50 (patch) | |
tree | 55494ebfa947bcd81e24e91e94e960696fb96d61 | |
parent | 025cc4ab92236971adc3961e0a177019fed2e6fd (diff) | |
download | python-coveragepy-git-3f544ee33de31e96ca2d25d95da614e9a7a12f50.tar.gz |
Don't be confused by files with missing final newlines. #293
-rw-r--r-- | CHANGES.txt | 4 | ||||
-rw-r--r-- | coverage/parser.py | 14 | ||||
-rw-r--r-- | tests/test_parser.py | 32 |
3 files changed, 42 insertions, 8 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index 0ab34232..ddafc9de 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -47,6 +47,10 @@ Latest - Made some PyPy-specific tweaks to improve speed under PyPy. Thanks, Alex Gaynor. +- In some cases, with a file with a missing final newline, coverage would + count statements incorrectly. This is now fixed, closing `issue 293`_. + +.. _issue 293: https://bitbucket.org/ned/coveragepy/issue/293/number-of-statement-detection-wrong-if-no .. _issue 314: https://bitbucket.org/ned/coveragepy/issue/314/fail_under-param-not-working-in-coveragerc .. _issue 328: https://bitbucket.org/ned/coveragepy/issue/328/misbehavior-in-run-source .. _issue 334: https://bitbucket.org/ned/coveragepy/issue/334/pragma-not-recognized-if-tab-character diff --git a/coverage/parser.py b/coverage/parser.py index ae36a738..ef2ee5b8 100644 --- a/coverage/parser.py +++ b/coverage/parser.py @@ -49,9 +49,13 @@ class PythonParser(CodeParser): "No source for code: '%s': %s" % (self.filename, err) ) - # Scrap the BOM if it exists. - if self.text and ord(self.text[0]) == 0xfeff: - self.text = self.text[1:] + if self.text: + # Scrap the BOM if it exists. + if ord(self.text[0]) == 0xfeff: + self.text = self.text[1:] + # Python source should always have a final newline. + if self.text[-1] != "\n": + self.text += "\n" self.exclude = exclude @@ -346,9 +350,7 @@ class ByteParser(object): self.text = text try: - # Python 2.3 and 2.4 don't like partial last lines, so be sure - # the text ends nicely for them. - self.code = compile(text + '\n', filename, "exec") + self.code = compile(text, filename, "exec") except SyntaxError as synerr: raise NotPython( "Couldn't parse '%s' as Python source: '%s' at line %d" % diff --git a/tests/test_parser.py b/tests/test_parser.py index a392ea03..244d4c70 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -99,7 +99,7 @@ class ParserFileTest(CoverageTest): def parse_file(self, filename): """Parse `text` as source, and return the `PythonParser` used.""" parser = PythonParser(filename=filename, exclude="nocover") - parser.parse_source() + self.statements, self.excluded = parser.parse_source() return parser def test_line_endings(self): @@ -128,4 +128,32 @@ class ParserFileTest(CoverageTest): coverage = "\xe7\xf6v\xear\xe3g\xe9" """) parser = self.parse_file("encoded.py") - parser.exit_counts() # TODO: This value should be tested! + self.assertEqual(parser.exit_counts(), {1: 1}) + + def test_missing_line_ending(self): + # Test that the set of statements is the same even if a final + # multi-line statement has no final newline. + # https://bitbucket.org/ned/coveragepy/issue/293 + + self.make_file("normal.py", """\ + out, err = subprocess.Popen( + [sys.executable, '-c', 'pass'], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE).communicate() + """) + + self.parse_file("normal.py") + self.assertEqual(self.statements, set([1])) + + self.make_file("abrupt.py", """\ + out, err = subprocess.Popen( + [sys.executable, '-c', 'pass'], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE).communicate()""") # no final newline. + + # Double-check that some test helper wasn't being helpful. + with open("abrupt.py") as f: + self.assertEqual(f.read()[-1], ")") + + self.parse_file("abrupt.py") + self.assertEqual(self.statements, set([1])) |