Skip to content

[Bug] - CancelledError swallowed (with solution)  #2551

Closed
@svaraborut

Description

@svaraborut

Version: 4.2.2

Platform: any

Description:

I had various issues related to task cancellation when using Redis due to asyncio.CancelledError disappearing sometimes. Probably related to #2028

The bug is difficult to reproduce as cancel() needs to be called on a task exactly when is waiting within an async with async_timeout.timeout(timeout): block.

I tracked down the issue to async_timeout used inside redis.asyncio.connection. This module has this known issue that is not being addressed.

I patched the library externally with this code

import async_timeout


class _Timeout(async_timeout.Timeout):

    RANDOM_TOKEN = '0f0dd596-373b-42df-aa0b-682d046c5d24'

    def __exit__(self, exc_type, exc_val, exc_tb):
        self._do_exit(exc_type, exc_val)
        return None

    async def __aexit__(self, exc_type, exc_val, exc_tb):
        self._do_exit(exc_type, exc_val)
        return None

    def _do_exit(self, exc_type, exc_val):
        if exc_type is asyncio.CancelledError and str(exc_val) == _Timeout.RANDOM_TOKEN \
                and self._state == async_timeout._State.TIMEOUT:
            self._timeout_handler = None
            raise asyncio.TimeoutError
        # timeout has not expired
        self._state = async_timeout._State.EXIT
        self._reject()

    def _on_timeout(self, task: "asyncio.Task[None]") -> None:
        task.cancel(_Timeout.RANDOM_TOKEN)
        self._state = async_timeout._State.TIMEOUT
        # drop the reference early
        self._timeout_handler = None


async_timeout.Timeout = _Timeout

but connection.py should be rewritten using asyncio.timeout as this library may lead to strange behavior as mentioned here.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions