Skip to content

Commit

Permalink
fix: Python3.12 now inlines comprehensions
Browse files Browse the repository at this point in the history
  • Loading branch information
nedbat committed May 11, 2023
1 parent 2b84823 commit e6fe386
Show file tree
Hide file tree
Showing 6 changed files with 45 additions and 14 deletions.
4 changes: 3 additions & 1 deletion CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions coverage/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -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?
Expand Down
7 changes: 4 additions & 3 deletions coverage/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -1343,9 +1343,10 @@ def _code_object__ClassDef(self, node: ast.ClassDef) -> None:

_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.
Expand Down
11 changes: 11 additions & 0 deletions doc/migrating.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,14 @@ 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
------------------------

- 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.
24 changes: 18 additions & 6 deletions tests/test_arcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -559,22 +559,26 @@ def whileelse(seq):
)

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]
for tup in o:
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)]
for tup in [a for a in o]:
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
Expand Down Expand Up @@ -639,6 +643,10 @@ def test_generator_expression_another_way(self) -> None:
)

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))
Expand All @@ -647,7 +655,7 @@ def test_other_comprehensions(self) -> None:
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("""\
Expand All @@ -657,10 +665,14 @@ def test_other_comprehensions(self) -> None:
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
Expand All @@ -675,7 +687,7 @@ def test_multiline_dict_comp(self) -> None:
}
x = 11
""",
arcz="-22 2B B-2 2-2"
arcz="-22 2B B-2" + extra_arcz,
)
# Multi dict comp:
self.check_coverage("""\
Expand All @@ -695,7 +707,7 @@ def test_multiline_dict_comp(self) -> None:
}
x = 15
""",
arcz="-22 2F F-2 2-2"
arcz="-22 2F F-2" + extra_arcz,
)


Expand Down
9 changes: 5 additions & 4 deletions tests/test_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,10 +322,11 @@ def test_missing_arc_descriptions_for_small_callables(self) -> None:
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("""\
Expand Down

0 comments on commit e6fe386

Please sign in to comment.