diff options
author | Ned Batchelder <ned@nedbatchelder.com> | 2015-08-09 09:01:41 -0400 |
---|---|---|
committer | Ned Batchelder <ned@nedbatchelder.com> | 2015-08-09 09:01:41 -0400 |
commit | 3cc5b11a413426b07a51e86eab20a7e9ebab4f2d (patch) | |
tree | fbc33f9beb428fe698d28cbdc2419b9c90ed153e | |
parent | f72ee80a12af6d90172e1af954d0235c77ca7b51 (diff) | |
download | python-coveragepy-git-3cc5b11a413426b07a51e86eab20a7e9ebab4f2d.tar.gz |
Correct the handling of IndentationError and TokenError
-rw-r--r-- | coverage/parser.py | 27 | ||||
-rw-r--r-- | coverage/results.py | 4 | ||||
-rw-r--r-- | tests/test_parser.py | 20 |
3 files changed, 37 insertions, 14 deletions
diff --git a/coverage/parser.py b/coverage/parser.py index 42b9bcad..bc79a30a 100644 --- a/coverage/parser.py +++ b/coverage/parser.py @@ -38,7 +38,7 @@ class PythonParser(object): except IOError as err: raise NoSource( "No source for code: '%s': %s" % (self.filename, err) - ) + ) self.exclude = exclude @@ -112,7 +112,7 @@ class PythonParser(object): print("%10s %5s %-20r %r" % ( tokenize.tok_name.get(toktype, toktype), nice_pair((slineno, elineno)), ttext, ltext - )) + )) if toktype == token.INDENT: indent += 1 elif toktype == token.DEDENT: @@ -203,12 +203,16 @@ class PythonParser(object): """ try: self._raw_parse() - except (tokenize.TokenError, IndentationError) as tokerr: - msg, lineno = tokerr.args # pylint: disable=unpacking-non-sequence + except (tokenize.TokenError, IndentationError) as err: + if hasattr(err, "lineno"): + lineno = err.lineno # IndentationError + else: + lineno = err.args[1][0] # TokenError raise NotPython( - "Couldn't parse '%s' as Python source: '%s' at %s" % - (self.filename, msg, lineno) + "Couldn't parse '%s' as Python source: '%s' at line %d" % ( + self.filename, err.args[0], lineno ) + ) excluded_lines = self.first_lines(self.excluded) ignore = set() @@ -290,7 +294,7 @@ OPS_CODE_END = _opcode_set('RETURN_VALUE') OPS_CHUNK_END = _opcode_set( 'JUMP_ABSOLUTE', 'JUMP_FORWARD', 'RETURN_VALUE', 'RAISE_VARARGS', 'BREAK_LOOP', 'CONTINUE_LOOP', - ) +) # Opcodes that unconditionally begin a new code chunk. By starting new chunks # with unconditional jump instructions, we neatly deal with jumps to jumps @@ -300,7 +304,7 @@ OPS_CHUNK_BEGIN = _opcode_set('JUMP_ABSOLUTE', 'JUMP_FORWARD') # Opcodes that push a block on the block stack. OPS_PUSH_BLOCK = _opcode_set( 'SETUP_LOOP', 'SETUP_EXCEPT', 'SETUP_FINALLY', 'SETUP_WITH' - ) +) # Block types for exception handling. OPS_EXCEPT_BLOCKS = _opcode_set('SETUP_EXCEPT', 'SETUP_FINALLY') @@ -343,10 +347,9 @@ class ByteParser(object): for attr in ['co_lnotab', 'co_firstlineno', 'co_consts', 'co_code']: if not hasattr(self.code, attr): raise CoverageException( - "This implementation of Python doesn't support code " - "analysis.\n" + "This implementation of Python doesn't support code analysis.\n" "Run coverage.py under CPython for this command." - ) + ) def child_parsers(self): """Iterate over all the code objects nested within this one. @@ -664,4 +667,4 @@ class Chunk(object): "!" if self.first else "", "v" if self.entrance else "", list(self.exits), - ) + ) diff --git a/coverage/results.py b/coverage/results.py index 3f6ba123..2e82e24d 100644 --- a/coverage/results.py +++ b/coverage/results.py @@ -12,9 +12,9 @@ from coverage.misc import format_lines class Analysis(object): """The results of analyzing a FileReporter.""" - def __init__(self, data, file_reporters): + def __init__(self, data, file_reporter): self.data = data - self.file_reporter = file_reporters + self.file_reporter = file_reporter self.filename = self.file_reporter.filename self.statements = self.file_reporter.statements() self.excluded = self.file_reporter.excluded_statements() diff --git a/tests/test_parser.py b/tests/test_parser.py index 18621d15..84b9a214 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -8,6 +8,7 @@ import textwrap from tests.coveragetest import CoverageTest from coverage import env +from coverage.misc import NotPython from coverage.parser import PythonParser @@ -116,6 +117,25 @@ class PythonParserTest(CoverageTest): """) self.assertEqual(parser.exit_counts(), { 1:1, 2:1, 3:1, 6:1 }) + def test_indentation_error(self): + msg = ( + "Couldn't parse '<code>' as Python source: " + "'unindent does not match any outer indentation level' at line 3" + ) + with self.assertRaisesRegex(NotPython, msg): + _ = self.parse_source("""\ + 0 spaces + 2 + 1 + """) + + def test_token_error(self): + msg = "Couldn't parse '<code>' as Python source: 'EOF in multi-line string' at line 1" + with self.assertRaisesRegex(NotPython, msg): + _ = self.parse_source("""\ + ''' + """) + class ParserFileTest(CoverageTest): """Tests for coverage.py's code parsing from files.""" |