Describe the bug
When using coverage.py with asyncio code that cancels tasks and catches CancelledError, lines immediately following the await are not tracked as covered, even though they execute.
To Reproduce
"""
Run with: coverage run --branch repro.py && coverage report -m
"""
import asyncio
from asyncio import CancelledError, Task, create_task
class TaskManager:
def __init__(self):
self._tasks: list[Task] = []
self.cleanup_completed = False
async def start_tasks(self, count: int):
for i in range(count):
self._tasks.append(create_task(self._long_running_task(i)))
async def _long_running_task(self, task_id: int):
await asyncio.sleep(10)
return f"task_{task_id}_done"
async def cancel_remaining_tasks(self):
for task in self._tasks:
if not task.done():
task.cancel()
try:
await task
except CancelledError:
pass # Swallow the cancellation
self._tasks.clear()
async def stop(self):
if self._tasks:
await self.cancel_remaining_tasks()
self.cleanup_completed = True # <-- LINE 35: NOT COVERED but executes
async def main():
manager = TaskManager()
await manager.start_tasks(5)
await asyncio.sleep(0.01)
await manager.stop()
assert manager.cleanup_completed, "This passes, proving line 35 executed!"
print(f"cleanup_completed = {manager.cleanup_completed}")
if __name__ == "__main__":
asyncio.run(main())
Expected behavior
Line 35 (self.cleanup_completed = True) should be marked as covered since it executes (proven by the assertion passing and the print output).
Environment
coverage_version: 7.13.2
core: -none-
CTracer: available
python: 3.11.11
platform: macOS-26.2-arm64-arm-64bit
implementation: CPython
Relevant Installed Dependencies
coverage==7.13.2
greenlet==3.1.1
Additional context
- The issue also occurs with --concurrency=thread,greenlet
- The pattern is: after awaiting a coroutine that cancels tasks and catches CancelledError, the next line is not tracked
- This affects real-world code that needs to gracefully shut down async task pools
Describe the bug
When using coverage.py with asyncio code that cancels tasks and catches CancelledError, lines immediately following the await are not tracked as covered, even though they execute.
To Reproduce
Expected behavior
Line 35 (self.cleanup_completed = True) should be marked as covered since it executes (proven by the assertion passing and the print output).
Environment
Relevant Installed Dependencies
Additional context