summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNed Batchelder <ned@nedbatchelder.com>2021-08-14 19:24:10 -0400
committerNed Batchelder <ned@nedbatchelder.com>2021-08-14 19:28:18 -0400
commitf6d3e88ba5b2dab1720281885c99cdf3ce2844bc (patch)
tree17acde78dbc8695d7ba19f3a164ebabb30dfde73
parenta996e357d1e82c8ed08cda0a43a5ab0fa05f68fd (diff)
downloadpython-coveragepy-git-f6d3e88ba5b2dab1720281885c99cdf3ce2844bc.tar.gz
fix: missing exceptions through with statements in 3.10 aren't considered missing branches. #1205
-rw-r--r--CHANGES.rst5
-rw-r--r--coverage/parser.py12
-rw-r--r--tests/test_arcs.py56
3 files changed, 65 insertions, 8 deletions
diff --git a/CHANGES.rst b/CHANGES.rst
index 0fc84cb3..4baa8a36 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -37,6 +37,10 @@ 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`_).
@@ -44,6 +48,7 @@ Unreleased
.. _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:
diff --git a/coverage/parser.py b/coverage/parser.py
index 8d4e9ffb..8792d0ac 100644
--- a/coverage/parser.py
+++ b/coverage/parser.py
@@ -556,14 +556,14 @@ class WithBlock(BlockBase):
# 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):
@@ -573,7 +573,7 @@ class WithBlock(BlockBase):
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)
@@ -1232,10 +1232,6 @@ class AstArcAnalyzer:
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)
diff --git a/tests/test_arcs.py b/tests/test_arcs.py
index 5059fdc7..f1b57c57 100644
--- a/tests/test_arcs.py
+++ b/tests/test_arcs.py
@@ -279,6 +279,62 @@ class WithTest(CoverageTest):
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."""