Skip to content

Commit

Permalink
fix: nested context managers shouldn't cause a phantom missing branch #…
Browse files Browse the repository at this point in the history
  • Loading branch information
nedbat committed Oct 13, 2024
1 parent c8902ed commit 378c321
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 4 deletions.
5 changes: 5 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,15 @@ upgrading your version of coverage.py.
Unreleased
----------

- Fix: nested context managers could incorrectly be analyzed to flag a missing
branch on the last context manager, as described in `issue 1876`_. This is
now fixed.

- Fix: the missing branch message about not exiting a module had an extra
"didn't," as described in `issue 1873`_. This is now fixed.

.. _issue 1873: https://github.com/nedbat/coveragepy/issues/1873
.. _issue 1876: https://github.com/nedbat/coveragepy/issues/1876


.. start-releases
Expand Down
14 changes: 10 additions & 4 deletions coverage/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,12 +315,18 @@ def fix_with_jumps(self, arcs: Iterable[TArc]) -> set[TArc]:
to_add = set()
for arc in arcs:
if arc in self._with_jump_fixers:
start = arc[0]
to_remove.add(arc)
start_next, prev_next = self._with_jump_fixers[arc]
if start_next in arcs:
to_add.add(prev_next)
to_remove.add(arc)
while start_next in self._with_jump_fixers:
to_remove.add(start_next)
return (set(arcs) | to_add) - to_remove
start_next, prev_next = self._with_jump_fixers[start_next]
to_remove.add(prev_next)
to_add.add((start, prev_next[1]))
to_remove.add(arc)
to_remove.add(start_next)
arcs = (set(arcs) | to_add) - to_remove
return arcs

@functools.lru_cache
def exit_counts(self) -> dict[TLineNo, int]:
Expand Down
18 changes: 18 additions & 0 deletions tests/test_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,24 @@ def bar(self):
assert expected_arcs == parser.arcs()
assert expected_exits == parser.exit_counts()

def test_nested_context_managers(self) -> None:
# https://github.com/nedbat/coveragepy/issues/1876
parser = self.parse_text("""\
a = 1
with suppress(ValueError):
with suppress(ValueError):
x = 4
with suppress(ValueError):
x = 6
with suppress(ValueError):
x = 8
a = 9
""")

one_nine = set(range(1, 10))
assert parser.statements == one_nine
assert parser.exit_counts() == dict.fromkeys(one_nine, 1)

def test_module_docstrings(self) -> None:
parser = self.parse_text("""\
'''The docstring on line 1'''
Expand Down

0 comments on commit 378c321

Please sign in to comment.