From 11dcf13f48a3fd6cb21ad2e96998061d127ac2ff Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Thu, 11 May 2023 07:24:02 -0400 Subject: fix: Python3.12 now inlines comprehensions --- CHANGES.rst | 4 +++- coverage/env.py | 4 ++++ coverage/parser.py | 7 ++++--- doc/migrating.rst | 13 +++++++++++++ tests/test_arcs.py | 24 ++++++++++++++++++------ tests/test_parser.py | 9 +++++---- 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("""\ -- cgit v1.2.1