From 700f7083743424ba4370fe6892006a090db08909 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Fri, 9 Feb 2018 16:26:39 -0500 Subject: Properly handle empty decorated functions in 3.7. #640 --- CHANGES.rst | 6 +++++- coverage/parser.py | 13 +++++++------ tests/test_parser.py | 17 +++++++++++++++++ 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 5aa88742..3779a006 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -19,7 +19,11 @@ Change history for Coverage.py Unreleased ---------- -(none yet) +- On Python 3.7, reporting about a decorated function with no body other than a + docstring would crash coverage.py with an IndexError (`issue 640`_). This is + now fixed. + +.. _issue 640: https://bitbucket.org/ned/coveragepy/issues/640/indexerror-reporting-on-an-empty-decorated .. _changes_45: diff --git a/coverage/parser.py b/coverage/parser.py index 590eacee..6e6cccd5 100644 --- a/coverage/parser.py +++ b/coverage/parser.py @@ -831,12 +831,13 @@ class AstArcAnalyzer(object): # in `self.statements`. For some constructs, `line_for_node` is # not what we'd think of as the first line in the statement, so map # it to the first one. - body_start = self.line_for_node(node.body[0]) - body_start = self.multiline.get(body_start, body_start) - for lineno in range(last+1, body_start): - if lineno in self.statements: - self.add_arc(last, lineno) - last = lineno + if node.body: + body_start = self.line_for_node(node.body[0]) + body_start = self.multiline.get(body_start, body_start) + for lineno in range(last+1, body_start): + if lineno in self.statements: + self.add_arc(last, lineno) + last = lineno # The body is handled in collect_arcs. return set([ArcStart(last)]) diff --git a/tests/test_parser.py b/tests/test_parser.py index e9628ac7..afb87716 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -187,6 +187,23 @@ class PythonParserTest(CoverageTest): self.assertEqual(parser.raw_statements, set([1, 2, 3, 5, 6, 7, 8])) self.assertEqual(parser.statements, set([1, 2, 3])) + def test_empty_decorated_function(self): + parser = self.parse_source("""\ + def decorator(func): + return func + + @decorator + def foo(self): + '''Docstring''' + + @decorator + def bar(self): + pass + """) + self.assertEqual(parser.statements, set([1, 2, 4, 8, 10])) + self.assertEqual(parser.arcs(), set(self.arcz_to_arcs(".1 14 48 8. .2 2. -8A A-8"))) + self.assertEqual(parser.exit_counts(), {1: 1, 2: 1, 4: 1, 8: 1, 10: 1}) + class ParserMissingArcDescriptionTest(CoverageTest): """Tests for PythonParser.missing_arc_description.""" -- cgit v1.2.1