Behavior of Timeout context manager is equal to using wait_for() without need for separate inner coroutine.
Like wait_for() cancels inner coroutine by sending CancelledError to inner and raising TimeoutError to outer. On Monday, December 28, 2015 at 1:00:27 PM UTC+2, Gustavo Carneiro wrote: > > Sorry for this 3rd email in a row, I'll shut up now. > > To clarify, my ideal Timeout context manager would be thus. > > async def task(): # let's call this task t1 > with Timeout(123): > await asyncio.sleep(321) # let's call this task t2 > > Given the above, outer task t1 is waiting for task t2, when the timeout > occurs. The context manager should: > 1. when entering the block: take a reference for the current task (as it > does now) > 2. when the timeout occurs: > a) take a note that the timeout occurred (as it does now) > b) somehow, find out which future our task t1 is waiting for (t2) and > cancel it > 3. when exiting the block, if the timeout has occurred: > a) do NOT cancel the task t1 > b) raise TimeoutError > > This way, normally the outer task gets cancelled anyway simply because of > the exception being raised. However, there is the possibility for the code > to catch the exception and allow the rest of the task to continue executing > as if nothing had happened. > > I believe it works exactly as you proposed. Task t1 is not cancelled but it's resumed with TimeoutError. The following test explicitly states this (https://github.com/KeepSafe/aiohttp/blob/master/tests/test_timeout.py#L112-L130): > has_timeout = False @asyncio.coroutine def outer(): nonlocal has_timeout try: with Timeout(0.001, loop=loop): yield from asyncio.sleep(1, loop=loop) except asyncio.TimeoutError: has_timeout = True task = ensure_future(outer(), loop=loop) yield from task assert has_timeout assert not task.cancelled() assert task.done() It works because Timeout() catches CancelledError on __exit__ and raises TimeoutError instead. Did I miss something? Cheers. > > On 28 December 2015 at 10:40, Gustavo Carneiro <[email protected] > <javascript:>> wrote: > >> >> >> On 28 December 2015 at 06:27, Andrew Svetlov <[email protected] >> <javascript:>> wrote: >> >>> asyncio has wait_for coroutine for timeouts. >>> >>> It works like: >>> >>> async def coro(): >>> # do long running task >>> await asyncio.sleep(10000) >>> >>> await asyncio.wait_for(coro(), 1.5) >>> >>> The approach requires splitting code into two functions: one for waiting >>> and other for performing the task. >>> >>> aiohttp has Timeout class ( >>> https://github.com/KeepSafe/aiohttp/blob/master/aiohttp/helpers.py#L451-L488 >>> ). >>> The code from example may be rewritten as: >>> >>> with Timeout(1.5): >>> # do long running task >>> await asyncio.sleep(10000) >>> >>> >> In my last email I forgot to comment on your original text: >> >> >>> It raises asyncio.TimeoutError and cancels inner code if timeout runs >>> out. >>> >> >> This statement is wrong. It doesn't cancel just the "inner code". If by >> "inner code" you mean the code inside the context manager block, then no, >> it cancels that code and any code that may come after. >> >> -- >> Gustavo J. A. M. Carneiro >> Gambit Research >> "The universe is always one step beyond logic." -- Frank Herbert >> > > > > -- > Gustavo J. A. M. Carneiro > Gambit Research > "The universe is always one step beyond logic." -- Frank Herbert >
