summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNed Batchelder <ned@nedbatchelder.com>2022-09-26 19:21:41 -0400
committerNed Batchelder <ned@nedbatchelder.com>2022-09-26 20:27:16 -0400
commitfd36918609a18bd36a7e2c3b208220cf25c0957d (patch)
tree013019b92812c04882d85c83d334ecfae3dd6600
parent2c4bd7f1f9e8c3c429db2b9cfdc33ba0881f8fde (diff)
downloadpython-coveragepy-git-fd36918609a18bd36a7e2c3b208220cf25c0957d.tar.gz
fix: `class` statements shouldn't be branches. #1449
Revert "refactor: we no longer need to treat 'class' lines specially" This reverts commit 79f9f4575321fafc2ef770e3255f874db3d4b037.
-rw-r--r--CHANGES.rst4
-rw-r--r--coverage/parser.py15
-rw-r--r--lab/parser.py2
-rw-r--r--tests/test_lcov.py6
-rw-r--r--tests/test_parser.py6
5 files changed, 26 insertions, 7 deletions
diff --git a/CHANGES.rst b/CHANGES.rst
index 01d9f33e..2eba01dd 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -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
@@ -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:
diff --git a/coverage/parser.py b/coverage/parser.py
index 3dbfbf30..8b2a9ac5 100644
--- a/coverage/parser.py
+++ b/coverage/parser.py
@@ -67,6 +67,9 @@ class PythonParser:
# 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()
@@ -130,6 +133,12 @@ class PythonParser:
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
@@ -292,6 +301,12 @@ class PythonParser:
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):
diff --git a/lab/parser.py b/lab/parser.py
index 43f3da52..ebd4e7f3 100644
--- a/lab/parser.py
+++ b/lab/parser.py
@@ -108,6 +108,8 @@ class ParserMain:
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'
diff --git a/tests/test_lcov.py b/tests/test_lcov.py
index 09bb99aa..ed7706fb 100644
--- a/tests/test_lcov.py
+++ b/tests/test_lcov.py
@@ -203,10 +203,8 @@ class LcovTest(CoverageTest):
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()
diff --git a/tests/test_parser.py b/tests/test_parser.py
index 48f5ade3..6d181c9e 100644
--- a/tests/test_parser.py
+++ b/tests/test_parser.py
@@ -43,7 +43,7 @@ class PythonParserTest(CoverageTest):
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):
@@ -89,7 +89,7 @@ class PythonParserTest(CoverageTest):
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):
@@ -472,7 +472,7 @@ class ParserFileTest(CoverageTest):
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)