Skip to content

Commit

Permalink
fix: missing exceptions through with statements in 3.10 aren't consid…
Browse files Browse the repository at this point in the history
…ered missing branches. #1205
  • Loading branch information
nedbat committed Aug 14, 2021
1 parent a996e35 commit f6d3e88
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 8 deletions.
5 changes: 5 additions & 0 deletions CHANGES.rst
Expand Up @@ -37,13 +37,18 @@ Unreleased
now warnings, to ease the use of coverage across versions. Fixes `issue
1035`_.

- Fix handling of exceptions through context managers in Python 3.10. A missing
exception is no longer considered a missing branch from the with statement.
Fixes `issue 1205`_.

- Fix another rarer instance of "Error binding parameter 0 - probably
unsupported type." (`issue 1010`_).

.. _issue 1035: https://github.com/nedbat/coveragepy/issues/1035
.. _issue 1105: https://github.com/nedbat/coveragepy/issues/1105
.. _issue 1163: https://github.com/nedbat/coveragepy/issues/1163
.. _issue 1195: https://github.com/nedbat/coveragepy/issues/1195
.. _issue 1205: https://github.com/nedbat/coveragepy/issues/1205


.. _changes_60b1:
Expand Down
12 changes: 4 additions & 8 deletions coverage/parser.py
Expand Up @@ -556,14 +556,14 @@ def __init__(self, start):
# that need to go through the with-statement while exiting.
self.break_from = set()
self.continue_from = set()
self.raise_from = set()
self.return_from = set()

def _process_exits(self, exits, add_arc, from_set):
def _process_exits(self, exits, add_arc, from_set=None):
"""Helper to process the four kinds of exits."""
for xit in exits:
add_arc(xit.lineno, self.start, xit.cause)
from_set.update(exits)
if from_set is not None:
from_set.update(exits)
return True

def process_break_exits(self, exits, add_arc):
Expand All @@ -573,7 +573,7 @@ def process_continue_exits(self, exits, add_arc):
return self._process_exits(exits, add_arc, self.continue_from)

def process_raise_exits(self, exits, add_arc):
return self._process_exits(exits, add_arc, self.raise_from)
return self._process_exits(exits, add_arc)

def process_return_exits(self, exits, add_arc):
return self._process_exits(exits, add_arc, self.return_from)
Expand Down Expand Up @@ -1232,10 +1232,6 @@ def _handle__With(self, node):
self.process_continue_exits(
self._combine_finally_starts(with_block.continue_from, with_exit)
)
if with_block.raise_from:
self.process_raise_exits(
self._combine_finally_starts(with_block.raise_from, with_exit)
)
if with_block.return_from:
self.process_return_exits(
self._combine_finally_starts(with_block.return_from, with_exit)
Expand Down
56 changes: 56 additions & 0 deletions tests/test_arcs.py
Expand Up @@ -279,6 +279,62 @@ def test_continue_through_with(self):
arcz=arcz,
)

def test_raise_through_with(self):
if env.PYBEHAVIOR.exit_through_with:
arcz = ".1 12 27 78 8. 9A A. -23 34 45 53 6-2"
arcz_missing = "6-2 8."
arcz_unpredicted = "3-2 89"
else:
arcz = ".1 12 27 78 8. 9A A. -23 34 45 5-2 6-2"
arcz_missing = "6-2 8."
arcz_unpredicted = "89"
cov = self.check_coverage("""\
from contextlib import suppress
def f(x):
with suppress(): # used as a null context manager
print(4)
raise Exception("Boo6")
print(6)
try:
f(8)
except Exception:
print("oops 10")
""",
arcz=arcz,
arcz_missing=arcz_missing,
arcz_unpredicted=arcz_unpredicted,
)
expected = "line 3 didn't jump to the function exit"
assert self.get_missing_arc_description(cov, 3, -2) == expected

def test_untaken_raise_through_with(self):
if env.PYBEHAVIOR.exit_through_with:
#arcz = ".1 12 28 89 9. AB B. -23 3-2 34 45 56 53 63 37 7-2"
arcz = ".1 12 28 89 9. AB B. -23 34 45 56 53 63 37 7-2"
#arcz_missing = "3-2 56 63 AB B."
arcz_missing = "56 63 AB B."
else:
arcz = ".1 12 28 89 9. AB B. -23 34 45 56 6-2 57 7-2"
arcz_missing = "56 6-2 AB B."
cov = self.check_coverage("""\
from contextlib import suppress
def f(x):
with suppress(): # used as a null context manager
print(4)
if x == 5:
raise Exception("Boo6")
print(7)
try:
f(9)
except Exception:
print("oops 11")
""",
arcz=arcz,
arcz_missing=arcz_missing,
)
expected = "line 3 didn't jump to the function exit"
assert self.get_missing_arc_description(cov, 3, -2) == expected


class LoopArcTest(CoverageTest):
"""Arc-measuring tests involving loops."""
Expand Down

0 comments on commit f6d3e88

Please sign in to comment.