Skip to content

Commit 23dac04

Browse files
committed
gh-142867: Fix trace --count showing annotations as missing
1 parent b9a4806 commit 23dac04

File tree

3 files changed

+56
-0
lines changed

3 files changed

+56
-0
lines changed

Lib/test/test_trace.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,57 @@ def test_cover_files_written_with_highlight(self):
499499
>>>>>> print('unreachable')
500500
'''))
501501

502+
503+
# gh-142867: Deferred annotations (PEP 649) should not appear as not covered
504+
class TestCoverageAnnotations(unittest.TestCase):
505+
506+
codefile = 'tmp_annotations.py'
507+
coverfile = 'tmp_annotations.cover'
508+
509+
def tearDown(self):
510+
unlink(self.codefile)
511+
unlink(self.coverfile)
512+
513+
def test_multiline_annotation_not_marked_uncovered(self):
514+
# gh-142867: Multiline type annotations should not be marked as
515+
# uncovered since annotation evaluation is deferred (PEP 649)
516+
with open(self.codefile, 'w', encoding='utf-8') as f:
517+
f.write(textwrap.dedent('''\
518+
def x() -> tuple[
519+
int,
520+
]:
521+
return (1,)
522+
523+
x()
524+
'''))
525+
argv = '-m trace --count --missing'.split() + [self.codefile]
526+
status, stdout, stderr = assert_python_ok(*argv)
527+
self.assertTrue(os.path.exists(self.coverfile))
528+
with open(self.coverfile, encoding='utf-8') as f:
529+
content = f.read()
530+
# The multiline annotation (line 2: "int,") should NOT be marked
531+
# as uncovered with ">>>>>>". It should have a blank prefix since
532+
# annotation lines are not executed during normal program flow.
533+
self.assertNotIn('>>>>>> ', content)
534+
535+
def test_class_annotation_not_marked_uncovered(self):
536+
# Class attribute annotations should not be marked as uncovered
537+
with open(self.codefile, 'w', encoding='utf-8') as f:
538+
f.write(textwrap.dedent('''\
539+
class X:
540+
a: int
541+
542+
x = X()
543+
'''))
544+
argv = '-m trace --count --missing'.split() + [self.codefile]
545+
status, stdout, stderr = assert_python_ok(*argv)
546+
self.assertTrue(os.path.exists(self.coverfile))
547+
with open(self.coverfile, encoding='utf-8') as f:
548+
content = f.read()
549+
# The annotation line should NOT be marked as uncovered
550+
self.assertNotIn('>>>>>> ', content)
551+
552+
502553
class TestCommandLine(unittest.TestCase):
503554

504555
def test_failures(self):

Lib/trace.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,9 @@ def _find_lines(code, strs):
346346
# and check the constants for references to other code objects
347347
for c in code.co_consts:
348348
if inspect.iscode(c):
349+
# skip __annotate__ code objects (PEP 649)
350+
if c.co_name == "__annotate__":
351+
continue
349352
# find another code object, so recurse into it
350353
linenos.update(_find_lines(c, strs))
351354
return linenos
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix :mod:`trace` module incorrectly marking deferred annotations (PEP 649)
2+
as not covered in ``--count --missing`` mode.

0 commit comments

Comments
 (0)