This rule raises an issue when a cancellation scope (timeout context) is used without any checkpoints making the timeout functionality ineffective.
When using asynchronous programming with libraries like trio or anyio, cancellation scopes (timeout contexts) are used to
implement timeouts and cancellation. However, these mechanisms only work when there are checkpoints within the scope where cancellation can occur.
Without any checkpoints, the timeout will never be triggered, making it ineffective.
A checkpoint is a point in the code where cancellation can be detected and acted upon. Common checkpoints include:
yield statements await statements Without proper checkpoints in cancel scopes:
There is no direct checkpoint method in asyncio, but you can use await asyncio.sleep(0) as a workaround.
import asyncio
async def process_data(data):
try:
async with asyncio.timeout(1.0): # Noncompliant
result = expensive_computation(data)
return result
except asyncio.TimeoutError:
return None
import asyncio
async def process_data(data):
try:
async with asyncio.timeout(1.0): # Compliant
result = expensive_computation(data)
await asyncio.sleep(0)
return result
except asyncio.TimeoutError:
return None
import trio
async def process_data(data):
async with trio.move_on_after(1.0): # Noncompliant
result = expensive_computation(data)
return result
import trio
async def process_data(data):
async with trio.move_on_after(1.0): # Compliant
result = expensive_computation(data)
await trio.lowlevel.checkpoint()
return result
import anyio
async def process_data(data):
async with anyio.move_on_after(1.0): # Noncompliant
result = expensive_computation(data)
return result
import anyio
async def process_data(data):
async with anyio.move_on_after(1.0): # Compliant
result = expensive_computation(data)
await anyio.lowlevel.checkpoint()
return result