Skip to content

Commit

Permalink
fix: some clause exclusions were broken #1713
Browse files Browse the repository at this point in the history
  • Loading branch information
nedbat committed Dec 20, 2023
1 parent 962429c commit 07b76b2
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 9 deletions.
5 changes: 5 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,14 @@ development at the same time, such as 4.5.x and 5.0.
Unreleased
----------

- Fix: the change for multi-line signature exclusions in 7.3.3 broke other
forms of nested clauses being excluded properly. This is now fixed, closing
`issue 1713`_.

- Fix: in the HTML report, selecting code for copying won't select the line
numbers also. Thanks, `Robert Harris <pull 1717_>`_.

.. _issue 1713: https://github.com/nedbat/coveragepy/issues/1713
.. _pull 1717: https://github.com/nedbat/coveragepy/pull/1717


Expand Down
12 changes: 4 additions & 8 deletions coverage/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,10 @@ def _raw_parse(self) -> None:
self.raw_classdefs.add(slineno)
elif toktype == token.OP:
if ttext == ":" and nesting == 0:
should_exclude = (elineno in self.raw_excluded) or excluding_decorators
should_exclude = (
self.raw_excluded.intersection(range(first_line, elineno + 1))
or excluding_decorators
)
if not excluding and should_exclude:
# Start excluding a suite. We trigger off of the colon
# token so that the #pragma comment will be recognized on
Expand Down Expand Up @@ -190,12 +193,6 @@ def _raw_parse(self) -> None:
# so record a multi-line range.
for l in range(first_line, elineno+1): # type: ignore[unreachable]
self._multiline[l] = first_line
# Check if multi-line was before a suite (trigger by the colon token).
if nesting == 0 and prev_toktype == token.OP and prev_ttext == ":":
statement_multilines = set(range(first_line, elineno + 1))
if statement_multilines & set(self.raw_excluded):
exclude_indent = indent
excluding = True
first_line = None
first_on_line = True

Expand All @@ -213,7 +210,6 @@ def _raw_parse(self) -> None:
first_on_line = False

prev_toktype = toktype
prev_ttext = ttext

# Find the starts of the executable statements.
if not empty:
Expand Down
2 changes: 1 addition & 1 deletion tests/coveragetest.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ def check_coverage(
# Get the analysis results, and check that they are right.
analysis = cov._analyze(mod)
statements = sorted(analysis.statements)
if lines is not None and len(lines) != 0:
if lines:
if isinstance(lines[0], int):
# lines is just a list of numbers, it must match the statements
# found in the code.
Expand Down
53 changes: 53 additions & 0 deletions tests/test_coverage.py
Original file line number Diff line number Diff line change
Expand Up @@ -1740,6 +1740,59 @@ def my_func_2(super_long_input_argument_0=0, super_long_input_argument_1=1, supe
[], "5", excludes=['my_func']
)

def test_excluding_bug1713(self) -> None:
if env.PYVERSION >= (3, 10):
self.check_coverage("""\
print("1")
def hello_3(a): # pragma: no cover
match a:
case ("5"
| "6"):
print("7")
case "8":
print("9")
print("11")
""",
[1, 11],
)
self.check_coverage("""\
print("1")
def hello_3(a): # no thanks
if ("4" or
"5"):
print("6")
else:
print("8")
print("10")
""",
[1, 10], "", excludes=["no thanks"],
)
self.check_coverage("""\
print(1)
def func(a, b):
if a == 4: # pragma: no cover
func5()
if b:
print(7)
func8()
print(10)
""",
[1, 3, 10]
)
self.check_coverage("""\
class Foo: # pragma: no cover
def greet(self):
print("hello world")
""",
[]
)

def test_excluding_method(self) -> None:
self.check_coverage("""\
class Fooey:
Expand Down

0 comments on commit 07b76b2

Please sign in to comment.