summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNed Batchelder <ned@nedbatchelder.com>2023-05-11 07:24:02 -0400
committerNed Batchelder <ned@nedbatchelder.com>2023-05-11 07:57:45 -0400
commit11dcf13f48a3fd6cb21ad2e96998061d127ac2ff (patch)
tree3884434db48f841345ca0f7e38e7286f2bcf03fd
parent2b84823331e5db2f89d5b8ab6ac00a4447c34cf6 (diff)
downloadpython-coveragepy-git-11dcf13f48a3fd6cb21ad2e96998061d127ac2ff.tar.gz
fix: Python3.12 now inlines comprehensions
-rw-r--r--CHANGES.rst4
-rw-r--r--coverage/env.py4
-rw-r--r--coverage/parser.py7
-rw-r--r--doc/migrating.rst13
-rw-r--r--tests/test_arcs.py24
-rw-r--r--tests/test_parser.py9
6 files changed, 47 insertions, 14 deletions
diff --git a/CHANGES.rst b/CHANGES.rst
index 8aefb88f..4a1570a7 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -20,7 +20,9 @@ development at the same time, such as 4.5.x and 5.0.
Unreleased
----------
-Nothing yet.
+- Python 3.12 beta 1 now inlines comprehensions. Previously they were compiled
+ as invisible functions and coverage.py would warn you if they weren't
+ completely executed. This no longer happens under Python 3.12.
.. scriv-start-here
diff --git a/coverage/env.py b/coverage/env.py
index bdc2c785..9f9b2553 100644
--- a/coverage/env.py
+++ b/coverage/env.py
@@ -137,6 +137,10 @@ class PYBEHAVIOR:
# only a 0-number line, which is ignored, giving a truly empty module.
empty_is_empty = (PYVERSION >= (3, 11, 0, "beta", 4))
+ # Are comprehensions inlined (new) or compiled as called functions (old)?
+ # Changed in https://github.com/python/cpython/pull/101441
+ comprehensions_are_functions = (PYVERSION <= (3, 12, 0, "alpha", 7, 0))
+
# Coverage.py specifics.
# Are we using the C-implemented trace function?
diff --git a/coverage/parser.py b/coverage/parser.py
index e653a9cc..e8d51a9b 100644
--- a/coverage/parser.py
+++ b/coverage/parser.py
@@ -1343,9 +1343,10 @@ class AstArcAnalyzer:
_code_object__Lambda = _make_expression_code_method("lambda")
_code_object__GeneratorExp = _make_expression_code_method("generator expression")
- _code_object__DictComp = _make_expression_code_method("dictionary comprehension")
- _code_object__SetComp = _make_expression_code_method("set comprehension")
- _code_object__ListComp = _make_expression_code_method("list comprehension")
+ if env.PYBEHAVIOR.comprehensions_are_functions:
+ _code_object__DictComp = _make_expression_code_method("dictionary comprehension")
+ _code_object__SetComp = _make_expression_code_method("set comprehension")
+ _code_object__ListComp = _make_expression_code_method("list comprehension")
# Code only used when dumping the AST for debugging.
diff --git a/doc/migrating.rst b/doc/migrating.rst
index 5bbc1f33..443afac6 100644
--- a/doc/migrating.rst
+++ b/doc/migrating.rst
@@ -39,3 +39,16 @@ Consider these changes when migrating to coverage.py 7.x:
entire list. Newer versions of coverage.py will be adding to the default set
of exclusions. Using ``exclude_also`` will let you benefit from those
updates.
+
+
+.. _migrating_py312:
+
+Migrating to Python 3.12
+------------------------
+
+Keep these things in mind when running under Python 3.12:
+
+- Python 3.12 now inlines list, dict, and set comprehensions. Previously, they
+ were compiled as functions that were called internally. Coverage.py would
+ warn you if comprehensions weren't fully completed, but this no longer
+ happens with Python 3.12.
diff --git a/tests/test_arcs.py b/tests/test_arcs.py
index d80a4637..d4c1ba9d 100644
--- a/tests/test_arcs.py
+++ b/tests/test_arcs.py
@@ -559,6 +559,10 @@ class LoopArcTest(CoverageTest):
)
def test_confusing_for_loop_bug_175(self) -> None:
+ if env.PYBEHAVIOR.comprehensions_are_functions:
+ extra_arcz = " -22 2-2"
+ else:
+ extra_arcz = ""
self.check_coverage("""\
o = [(1,2), (3,4)]
o = [a for a in o]
@@ -566,7 +570,7 @@ class LoopArcTest(CoverageTest):
x = tup[0]
y = tup[1]
""",
- arcz=".1 -22 2-2 12 23 34 45 53 3.",
+ arcz=".1 12 23 34 45 53 3." + extra_arcz,
)
self.check_coverage("""\
o = [(1,2), (3,4)]
@@ -574,7 +578,7 @@ class LoopArcTest(CoverageTest):
x = tup[0]
y = tup[1]
""",
- arcz=".1 12 -22 2-2 23 34 42 2.",
+ arcz=".1 12 23 34 42 2." + extra_arcz,
)
# https://bugs.python.org/issue44672
@@ -639,6 +643,10 @@ class LoopArcTest(CoverageTest):
)
def test_other_comprehensions(self) -> None:
+ if env.PYBEHAVIOR.comprehensions_are_functions:
+ extra_arcz = " -22 2-2"
+ else:
+ extra_arcz = ""
# Set comprehension:
self.check_coverage("""\
o = ((1,2), (3,4))
@@ -647,7 +655,7 @@ class LoopArcTest(CoverageTest):
x = tup[0]
y = tup[1]
""",
- arcz=".1 -22 2-2 12 23 34 45 53 3.",
+ arcz=".1 12 23 34 45 53 3." + extra_arcz,
)
# Dict comprehension:
self.check_coverage("""\
@@ -657,10 +665,14 @@ class LoopArcTest(CoverageTest):
x = tup[0]
y = tup[1]
""",
- arcz=".1 -22 2-2 12 23 34 45 53 3.",
+ arcz=".1 12 23 34 45 53 3." + extra_arcz,
)
def test_multiline_dict_comp(self) -> None:
+ if env.PYBEHAVIOR.comprehensions_are_functions:
+ extra_arcz = " 2-2"
+ else:
+ extra_arcz = ""
# Multiline dict comp:
self.check_coverage("""\
# comment
@@ -675,7 +687,7 @@ class LoopArcTest(CoverageTest):
}
x = 11
""",
- arcz="-22 2B B-2 2-2"
+ arcz="-22 2B B-2" + extra_arcz,
)
# Multi dict comp:
self.check_coverage("""\
@@ -695,7 +707,7 @@ class LoopArcTest(CoverageTest):
}
x = 15
""",
- arcz="-22 2F F-2 2-2"
+ arcz="-22 2F F-2" + extra_arcz,
)
diff --git a/tests/test_parser.py b/tests/test_parser.py
index f74420b5..d461cb6c 100644
--- a/tests/test_parser.py
+++ b/tests/test_parser.py
@@ -322,10 +322,11 @@ class ParserMissingArcDescriptionTest(CoverageTest):
assert expected == parser.missing_arc_description(2, -2)
expected = "line 3 didn't finish the generator expression on line 3"
assert expected == parser.missing_arc_description(3, -3)
- expected = "line 4 didn't finish the dictionary comprehension on line 4"
- assert expected == parser.missing_arc_description(4, -4)
- expected = "line 5 didn't finish the set comprehension on line 5"
- assert expected == parser.missing_arc_description(5, -5)
+ if env.PYBEHAVIOR.comprehensions_are_functions:
+ expected = "line 4 didn't finish the dictionary comprehension on line 4"
+ assert expected == parser.missing_arc_description(4, -4)
+ expected = "line 5 didn't finish the set comprehension on line 5"
+ assert expected == parser.missing_arc_description(5, -5)
def test_missing_arc_descriptions_for_exceptions(self) -> None:
parser = self.parse_text("""\