This rule raises an issue when asyncio.create_task() or asyncio.ensure_future() is called without saving the returned
task.
When creating asyncio tasks with asyncio.create_task() or asyncio.ensure_future(), you create independent units of work
that execute concurrently. However, if you don’t save the returned task object in a variable or collection, the task may be garbage collected at any
time, even before it’s done.
This happens because the event loop only maintains a weak reference to tasks. Without a strong reference:
For a task to run to completion and handle exceptions properly, you must save the task reference and eventually await it.
To properly handle asyncio tasks:
asyncio.gather(), asyncio.wait(), or similar functions asyncio.TaskGroup (available in Python 3.11+) for structured concurrency with better cancellation semantics and
error handling
import asyncio
async def fetch_data():
asyncio.create_task(process_data()) # Noncompliant: task may be garbage collected before completion
async def process_data():
await asyncio.sleep(1)
return {"result": "processed"}
import asyncio
async def fetch_data():
task = asyncio.create_task(process_data()) # Compliant
await task
async def process_data():
await asyncio.sleep(1)
return {"result": "processed"}
Or, using TaskGroup (Python 3.11+):
import asyncio
async def fetch_data():
async with asyncio.TaskGroup() as tg:
# Tasks are managed by the TaskGroup and won't be garbage collected
tg.create_task(process_data())
async def process_data():
await asyncio.sleep(1)
return {"result": "processed"}