Skip to content

Commit 378c321

Browse files
committed
fix: nested context managers shouldn't cause a phantom missing branch #1876
1 parent c8902ed commit 378c321

File tree

3 files changed

+33
-4
lines changed

3 files changed

+33
-4
lines changed

CHANGES.rst

+5
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,15 @@ upgrading your version of coverage.py.
2323
Unreleased
2424
----------
2525

26+
- Fix: nested context managers could incorrectly be analyzed to flag a missing
27+
branch on the last context manager, as described in `issue 1876`_. This is
28+
now fixed.
29+
2630
- Fix: the missing branch message about not exiting a module had an extra
2731
"didn't," as described in `issue 1873`_. This is now fixed.
2832

2933
.. _issue 1873: https://github.com/nedbat/coveragepy/issues/1873
34+
.. _issue 1876: https://github.com/nedbat/coveragepy/issues/1876
3035

3136

3237
.. start-releases

coverage/parser.py

+10-4
Original file line numberDiff line numberDiff line change
@@ -315,12 +315,18 @@ def fix_with_jumps(self, arcs: Iterable[TArc]) -> set[TArc]:
315315
to_add = set()
316316
for arc in arcs:
317317
if arc in self._with_jump_fixers:
318+
start = arc[0]
319+
to_remove.add(arc)
318320
start_next, prev_next = self._with_jump_fixers[arc]
319-
if start_next in arcs:
320-
to_add.add(prev_next)
321-
to_remove.add(arc)
321+
while start_next in self._with_jump_fixers:
322322
to_remove.add(start_next)
323-
return (set(arcs) | to_add) - to_remove
323+
start_next, prev_next = self._with_jump_fixers[start_next]
324+
to_remove.add(prev_next)
325+
to_add.add((start, prev_next[1]))
326+
to_remove.add(arc)
327+
to_remove.add(start_next)
328+
arcs = (set(arcs) | to_add) - to_remove
329+
return arcs
324330

325331
@functools.lru_cache
326332
def exit_counts(self) -> dict[TLineNo, int]:

tests/test_parser.py

+18
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,24 @@ def bar(self):
151151
assert expected_arcs == parser.arcs()
152152
assert expected_exits == parser.exit_counts()
153153

154+
def test_nested_context_managers(self) -> None:
155+
# https://github.com/nedbat/coveragepy/issues/1876
156+
parser = self.parse_text("""\
157+
a = 1
158+
with suppress(ValueError):
159+
with suppress(ValueError):
160+
x = 4
161+
with suppress(ValueError):
162+
x = 6
163+
with suppress(ValueError):
164+
x = 8
165+
a = 9
166+
""")
167+
168+
one_nine = set(range(1, 10))
169+
assert parser.statements == one_nine
170+
assert parser.exit_counts() == dict.fromkeys(one_nine, 1)
171+
154172
def test_module_docstrings(self) -> None:
155173
parser = self.parse_text("""\
156174
'''The docstring on line 1'''

0 commit comments

Comments
 (0)