On 29 December 2015 at 21:53, Andrew Svetlov <[email protected]> wrote:
> 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? > No, I missed something. I missed the part where you catch CancelledError and re-reaise as TimeoutError instead. Looks good, then! Cheers. > Cheers. >> >> On 28 December 2015 at 10:40, Gustavo Carneiro <[email protected]> >> wrote: >> >>> >>> >>> On 28 December 2015 at 06:27, Andrew Svetlov <[email protected]> >>> 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 >> > -- Gustavo J. A. M. Carneiro Gambit Research "The universe is always one step beyond logic." -- Frank Herbert
