Skip to content

Commit

Permalink
fix: class statements shouldn't be branches. #1449
Browse files Browse the repository at this point in the history
Revert "refactor: we no longer need to treat 'class' lines specially"

This reverts commit 79f9f45.
  • Loading branch information
nedbat committed Sep 27, 2022
1 parent 2c4bd7f commit fd36918
Show file tree
Hide file tree
Showing 5 changed files with 26 additions and 7 deletions.
4 changes: 4 additions & 0 deletions CHANGES.rst
Expand Up @@ -20,6 +20,9 @@ development at the same time, such as 4.5.x and 5.0.
Unreleased
----------

- Starting with coverage.py 6.2, ``class`` statements were marked as a branch.
This wasn't right, and has been reverted, fixing `issue 1449`_.

- Packaging is now compliant with `PEP 517`_, closing `issue 1395`_.

- A new debug option ``--debug=pathmap`` shows details of the remapping of
Expand All @@ -31,6 +34,7 @@ Unreleased
.. _bug 50381: https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=50381
.. _PEP 517: https://peps.python.org/pep-0517/
.. _issue 1395: https://github.com/nedbat/coveragepy/issues/1395
.. _issue 1449: https://github.com/nedbat/coveragepy/issues/1449


.. _changes_6-4-4:
Expand Down
15 changes: 15 additions & 0 deletions coverage/parser.py
Expand Up @@ -67,6 +67,9 @@ def __init__(self, text=None, filename=None, exclude=None):
# The raw line numbers of excluded lines of code, as marked by pragmas.
self.raw_excluded = set()

# The line numbers of class definitions.
self.raw_classdefs = set()

# The line numbers of docstring lines.
self.raw_docstrings = set()

Expand Down Expand Up @@ -130,6 +133,12 @@ def _raw_parse(self):
indent += 1
elif toktype == token.DEDENT:
indent -= 1
elif toktype == token.NAME:
if ttext == 'class':
# Class definitions look like branches in the bytecode, so
# we need to exclude them. The simplest way is to note the
# lines with the 'class' keyword.
self.raw_classdefs.add(slineno)
elif toktype == token.OP:
if ttext == ':' and nesting == 0:
should_exclude = (elineno in self.raw_excluded) or excluding_decorators
Expand Down Expand Up @@ -292,6 +301,12 @@ def exit_counts(self):
continue
exit_counts[l1] += 1

# Class definitions have one extra exit, so remove one for each:
for l in self.raw_classdefs:
# Ensure key is there: class definitions can include excluded lines.
if l in exit_counts:
exit_counts[l] -= 1

return exit_counts

def missing_arc_description(self, start, end, executed_arcs=None):
Expand Down
2 changes: 2 additions & 0 deletions lab/parser.py
Expand Up @@ -108,6 +108,8 @@ def one_file(self, options, filename):
marks[2] = str(exits)
if lineno in pyparser.raw_docstrings:
marks[3] = '"'
if lineno in pyparser.raw_classdefs:
marks[3] = 'C'
if lineno in pyparser.raw_excluded:
marks[4] = 'x'

Expand Down
6 changes: 2 additions & 4 deletions tests/test_lcov.py
Expand Up @@ -203,10 +203,8 @@ def test_is_it_x(self):
DA:9,0,FPTWzd68bDx76HN7VHu1wA
LF:6
LH:4
BRDA:0,0,0,1
BRDA:7,0,1,1
BRF:2
BRH:2
BRF:0
BRH:0
end_of_record
""")
actual_result = self.get_lcov_report_content()
Expand Down
6 changes: 3 additions & 3 deletions tests/test_parser.py
Expand Up @@ -43,7 +43,7 @@ class Bar:
pass
""")
assert parser.exit_counts() == {
2:2, 3:1, 4:2, 5:1, 7:1, 9:2, 10:1
2:1, 3:1, 4:2, 5:1, 7:1, 9:1, 10:1
}

def test_generator_exit_counts(self):
Expand Down Expand Up @@ -89,7 +89,7 @@ class Bar:
pass
""")
assert parser.exit_counts() == {
1:1, 2:1, 3:1
1:0, 2:1, 3:1
}

def test_missing_branch_to_excluded_code(self):
Expand Down Expand Up @@ -472,7 +472,7 @@ def foo(self, a):
class Bar:
pass
"""
counts = { 2:2, 3:1, 4:2, 5:1, 7:1, 9:2, 10:1 }
counts = { 2:1, 3:1, 4:2, 5:1, 7:1, 9:1, 10:1 }
fname = slug + ".py"
self.make_file(fname, text, newline=newline)
parser = self.parse_file(fname)
Expand Down

0 comments on commit fd36918

Please sign in to comment.