Skip to content

retry_if_not_exception_type swallows asyncio.CancelledError #529

@jackwildman

Description

@jackwildman

Problem

When using retry_if_no_exception_type to avoid retries for a specific exception, this causes asyncio.CancelledError to be caught and retried. This is contra-typical behaviour, as CancelledError does not typically cause a retry to occur in other scenarios. This is because task cancellation is not typically a retry case, and allow these errors to bubble up enables cancellation utilities like asyncio.wait_for to work as excpected.

Steps to reproduce

Observe the behaviour as expected with this:

from tenacity import retry, wait_fixed, stop_after_attempt
import asyncio

@retry(
    wait=wait_fixed(10),
    stop=stop_after_attempt(2),
    reraise=True,
)
async def test():
    print("Sleeping")
    await asyncio.sleep(3)
    print("Done")

await asyncio.wait_for(test(), 1)  # Expected to cancel after 1s

The output here is:

Sleeping
CancelledError                            Traceback (most recent call last)
... (traceback)

We see incorrect behaviour in this case:

from tenacity import retry, wait_fixed, stop_after_attempt, retry_if_not_exception_type
import asyncio

@retry(
    wait=wait_fixed(10),
    stop=stop_after_attempt(2),
    reraise=True,
    retry=retry_if_not_exception_type(ValueError)
)
async def test_with_issue():
    print("Sleeping")
    await asyncio.sleep(3)
    print("Done")

await asyncio.wait_for(test_with_issue(), 1)  # Expected to cancel after 1s

The output here is:

Sleeping
Sleeping
Done

Here we see the retry occurring. We expect no retry to occur, and for the behaviour in this case to be the same as the former.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions