aio-libs / async-timeout

asyncio-compatible timeout class

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Cancelling current task

decaz opened this issue · comments

I'm using aiohttp's WebSocket client and want to set my own exception for the current task on message receive timeout. But currently it's impossible due to async-timeout's task cancelling.
Ref: https://github.com/aio-libs/aiohttp/blob/master/aiohttp/client_ws.py#L195

Simple example listing:

import asyncio

from async_timeout import timeout


async def main():
    try:
        async with timeout(1):
            await asyncio.sleep(2)
    except asyncio.TimeoutError:
        future.set_exception(Exception('My exception'))


future = asyncio.ensure_future(main())

loop = asyncio.get_event_loop()
loop.run_forever()

Output:

$ python test_timeout.py
Exception in callback <TaskWakeupMethWrapper object at 0x7f0d9414bbb8>(<Future cancelled>)
handle: <Handle <TaskWakeupMethWrapper object at 0x7f0d9414bbb8>(<Future cancelled>)>
Traceback (most recent call last):
  File "/usr/lib/python3.6/asyncio/events.py", line 145, in _run
    self._callback(*self._args)
asyncio.base_futures.InvalidStateError: invalid state

How can I set my own exception if it possible?

If exception type is important inside the coroutine, you can catch CancelledError and re-raise as an exception of you choice. If exception type is important inside the awaiting code, you can catch TimeoutError.

Task.cancel() doesn't support custom exceptions for good reason, async-timeout should not invent anything in the area but follow the upstream.

@decaz Alternatively, you can subclass timeout and override the _cancel_task to throw an exception instead of cancelling.

Please no. Perhaps this way will be broken in a meantime.
The timeout class is not intended for inheritance.

Ok, simply raising the exception helps:

...
    except asyncio.TimeoutError:
        raise Exception('My exception')
...

I guess the problem comes when you want to use exactly future.set_exception instead of raising.

In example you basically trying to call Task.set_exception().
This is explicitly forbidden in Python 3.7 and was buggy in previous Python versions