summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNed Batchelder <ned@nedbatchelder.com>2022-01-09 11:37:29 -0500
committerNed Batchelder <ned@nedbatchelder.com>2022-01-15 07:58:57 -0500
commitd723b46460dc7ffb4abf54806087ffd614b81331 (patch)
treef15a9e0a380af291c912e1fe417d157e7e897e68
parenta60a9e8b5f52a76ce15be3289ca64821ce0869e7 (diff)
downloadpython-coveragepy-git-d723b46460dc7ffb4abf54806087ffd614b81331.tar.gz
fix: 3.11 now traces decorator lines as the decorators execute
See: https://bugs.python.org/issue46234
-rw-r--r--coverage/env.py4
-rw-r--r--coverage/parser.py10
-rw-r--r--tests/test_arcs.py30
-rw-r--r--tests/test_parser.py4
4 files changed, 38 insertions, 10 deletions
diff --git a/coverage/env.py b/coverage/env.py
index 64d93e21..cf2f9d26 100644
--- a/coverage/env.py
+++ b/coverage/env.py
@@ -100,6 +100,10 @@ class PYBEHAVIOR:
# Some words are keywords in some places, identifiers in other places.
soft_keywords = (PYVERSION >= (3, 10))
+ # CPython 3.11 now jumps to the decorator line again while executing
+ # the decorator.
+ trace_decorator_line_again = (PYVERSION > (3, 11, 0, 'alpha', 3, 0))
+
# Coverage.py specifics.
diff --git a/coverage/parser.py b/coverage/parser.py
index 665360fa..a96964d4 100644
--- a/coverage/parser.py
+++ b/coverage/parser.py
@@ -944,10 +944,11 @@ class AstArcAnalyzer:
def _handle_decorated(self, node):
"""Add arcs for things that can be decorated (classes and functions)."""
main_line = last = node.lineno
- if node.decorator_list:
+ decs = node.decorator_list
+ if decs:
if env.PYBEHAVIOR.trace_decorated_def:
last = None
- for dec_node in node.decorator_list:
+ for dec_node in decs:
dec_start = self.line_for_node(dec_node)
if last is not None and dec_start != last:
self.add_arc(last, dec_start)
@@ -955,6 +956,11 @@ class AstArcAnalyzer:
if env.PYBEHAVIOR.trace_decorated_def:
self.add_arc(last, main_line)
last = main_line
+ if env.PYBEHAVIOR.trace_decorator_line_again:
+ for top, bot in zip(decs, decs[1:]):
+ self.add_arc(self.line_for_node(bot), self.line_for_node(top))
+ self.add_arc(self.line_for_node(decs[0]), main_line)
+ self.add_arc(main_line, self.line_for_node(decs[-1]))
# The definition line may have been missed, but we should have it
# in `self.statements`. For some constructs, `line_for_node` is
# not what we'd think of as the first line in the statement, so map
diff --git a/tests/test_arcs.py b/tests/test_arcs.py
index 978a3731..0635fcbc 100644
--- a/tests/test_arcs.py
+++ b/tests/test_arcs.py
@@ -1664,6 +1664,13 @@ class DecoratorArcTest(CoverageTest):
"""Tests of arcs with decorators."""
def test_function_decorator(self):
+ arcz = (
+ ".1 16 67 7A AE EF F. " # main line
+ ".2 24 4. -23 3-2 " # decorators
+ "-6D D-6 " # my_function
+ )
+ if env.PYBEHAVIOR.trace_decorator_line_again:
+ arcz += "A7 76 6A "
self.check_coverage("""\
def decorator(arg):
def _dec(f):
@@ -1681,13 +1688,17 @@ class DecoratorArcTest(CoverageTest):
a = 14
my_function()
""",
- arcz=
- ".1 16 67 7A AE EF F. " # main line
- ".2 24 4. -23 3-2 " # decorators
- "-6D D-6 ", # my_function
+ arcz=arcz,
)
def test_class_decorator(self):
+ arcz = (
+ ".1 16 67 6D 7A AE E. " # main line
+ ".2 24 4. -23 3-2 " # decorators
+ "-66 D-6 " # MyObject
+ )
+ if env.PYBEHAVIOR.trace_decorator_line_again:
+ arcz += "A7 76 6A "
self.check_coverage("""\
def decorator(arg):
def _dec(c):
@@ -1704,10 +1715,7 @@ class DecoratorArcTest(CoverageTest):
X = 13
a = 14
""",
- arcz=
- ".1 16 67 6D 7A AE E. " # main line
- ".2 24 4. -23 3-2 " # decorators
- "-66 D-6 ", # MyObject
+ arcz=arcz,
)
def test_bug_466a(self):
@@ -1716,6 +1724,8 @@ class DecoratorArcTest(CoverageTest):
arcz = ".1 1A A. 13 3. -35 58 8-3 "
if env.PYBEHAVIOR.trace_decorated_def:
arcz = arcz.replace("3.", "34 4.")
+ if env.PYBEHAVIOR.trace_decorator_line_again:
+ arcz += "43 "
self.check_coverage("""\
class Parser(object):
@@ -1737,6 +1747,8 @@ class DecoratorArcTest(CoverageTest):
arcz = ".1 1A A. 13 3. -35 58 8-3 "
if env.PYBEHAVIOR.trace_decorated_def:
arcz = arcz.replace("3.", "34 4.")
+ if env.PYBEHAVIOR.trace_decorator_line_again:
+ arcz += "43 "
self.check_coverage("""\
class Parser(object):
@@ -1925,6 +1937,8 @@ class AsyncTest(CoverageTest):
arcz = ".1 14 4. .2 2. -46 6-4 "
if env.PYBEHAVIOR.trace_decorated_def:
arcz = arcz.replace("4.", "45 5.")
+ if env.PYBEHAVIOR.trace_decorator_line_again:
+ arcz += "54 "
self.check_coverage("""\
def wrap(f): # 1
return f
diff --git a/tests/test_parser.py b/tests/test_parser.py
index 1e509035..2393ccd1 100644
--- a/tests/test_parser.py
+++ b/tests/test_parser.py
@@ -240,6 +240,10 @@ class PythonParserTest(CoverageTest):
expected_arcs.update(set(arcz_to_arcs("-46 6-4")))
expected_exits.update({6: 1})
+ if env.PYBEHAVIOR.trace_decorator_line_again:
+ expected_arcs.update(set(arcz_to_arcs("54 98")))
+ expected_exits.update({9: 2, 5: 2})
+
assert expected_statements == parser.statements
assert expected_arcs == parser.arcs()
assert expected_exits == parser.exit_counts()